Funzione fattoriale di rubino


88

Sto impazzendo: dov'è la funzione Ruby per fattoriale? No, non ho bisogno di implementazioni di tutorial, voglio solo la funzione dalla libreria. Non è in matematica!

Comincio a dubitare, è una funzione di libreria standard?


63
Mi piace6.downto(1).inject(:*)
mckeed

43
@mckeed: O (1..6).inject(:*)che è un po 'più succinto.
sepp2k

8
perché ti aspetti che ce ne sia uno?
Presidente James K. Polk

4
Mi chiedo quale sia lo stato delle biblioteche di matematica e scienze per Ruby.
Andrew Grimm

5
Solo una nota sugli esempi forniti usando inject. (1..num).inject(:*)fallisce nel caso in cui num == 0. (1..(num.zero? ? 1 : num)).inject(:*)fornisce la risposta corretta per il caso 0 e restituisce nilper i parametri negativi.
Yogh

Risposte:




77

Non è nella libreria standard ma puoi estendere la classe Integer.

class Integer
  def factorial_recursive
    self <= 1 ? 1 : self * (self - 1).factorial
  end
  def factorial_iterative
    f = 1; for i in 1..self; f *= i; end; f
  end
  alias :factorial :factorial_iterative
end

NB Il fattoriale iterativo è una scelta migliore per ovvi motivi di prestazioni.


8
Ha detto esplicitamente che non vuole un'implementazione.
sepp2k

117
Potrebbe non farlo; ma le persone che cercano COSÌ per "Fattoriale Ruby" potrebbero.
Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby è semplicemente sbagliato. Non c'è un caso per 0
Douglas G. Allen

La versione ricorsiva è effettivamente più lenta? Potrebbe dipendere dal fatto che Ruby esegua l'ottimizzazione ricorsiva della coda.
Lex Lindsey

24

Spudoratamente criptato da http://rosettacode.org/wiki/Factorial#Ruby , il mio preferito è

class Integer
  def fact
    (1..self).reduce(:*) || 1
  end
end

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Questa implementazione è anche la più veloce tra le varianti elencate in Rosetta Code.

aggiornamento n. 1

Aggiunto || 1per gestire il caso zero.

aggiornamento n. 2

Con grazie e apprezzamento a Mark Thomas , ecco una versione un po 'più efficiente, elegante e oscura:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end

1
cosa diavolo significa ?! sì, è veloce ma è molto insolito però
niccolo m.

3
non è corretto anche per 0! - dovrebbe essere qualcosa come: if self <= 1; 1; altro; (1..self) .riduce (: *); fine
Tarmo

8
@allen - Non incolpare la lingua se non riesci a capirla. Significa semplicemente, prendi l'intervallo 1 a self, quindi rimuovi il primo elemento (1) da esso (cioè questo è ciò che riduce significa nella programmazione funzionale). Quindi rimuovi il primo elemento di ciò che è rimasto (2) e moltiplica (: *) quelli insieme. Ora rimuovi il primo elemento da ciò che è rimasto (3) e moltiplicalo per il totale parziale. Continua finché non è rimasto più nulla (ovvero hai gestito l'intero intervallo). Se reduce fallisce (perché l'array è vuoto nel caso di 0!), Restituisci comunque 1.
SDJMcHattie

È inoltre possibile gestire il caso di zero specificando il valore iniziale reduce: (1..self).reduce(1,:*).
Mark Thomas

3
In realtà puoi usare (2..self).reduce(1,:*), se la microefficienza è la tua passione :)
Mark Thomas


14

È inoltre possibile utilizzare una Math.gammafunzione che si riduce a fattoriale per i parametri interi.


3
Dalla documentazione: "Nota che gamma (n) è uguale a fact (n-1) per intero n> 0. Tuttavia gamma (n) restituisce float e può essere un'approssimazione". Se si tiene conto di ciò, funziona, ma la soluzione di riduzione sembra molto più semplice.
Michael Kohl

Grazie per questo! Il mio istinto dice di usare verso la libreria standard su una riduzione scritta personalizzata quando possibile. La profilazione potrebbe suggerire diversamente.
David J.

2
Nota: è O (1) e preciso per 0..22: MRI Ruby esegue effettivamente una ricerca per quei valori (vedi static const double fact_table[]nella fonte ). Oltre a ciò, è un'approssimazione. 23!, Ad esempio, richiede una mantissa a 56 bit che è impossibile rappresentare con precisione utilizzando il doppio IEEE 754 che ha una mantissa a 53 bit.
fny

13
class Integer
  def !
    (1..self).inject(:*)
  end
end

esempi

!3  # => 6
!4  # => 24

Cosa c'è che non va class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin,

@mudasobwa mi piace, ho refactoring per semplicità.
jasonleonhard

4
Suggerirei rispettosamente che sovrascrivere un metodo di istanza incorporato in tutti gli oggetti Ruby per restituire un valore veritiero, piuttosto che falso, potrebbe non farti molti amici.
MatzFan

Potrebbe essere davvero pericoloso fare in modo che l'operatore di negazione diventi qualcos'altro quando aaccade Integernel caso di !a... farlo potrebbe causare l'esistenza di un bug che è molto difficile da dire. Se acapita di essere un grande numero come 357264543allora il processore sta entrando in un grande loop e le persone potrebbero chiedersi perché il programma all'improvviso diventa lento
nonopolarità

Questa risposta era più solo una cosa interessante da condividere piuttosto che un esempio pratico da usare.
jasonleonhard


6

Ho appena scritto il mio:

def fact(n)
  if n<= 1
    1
  else
    n * fact( n - 1 )
  end
end

Inoltre, puoi definire un fattoriale decrescente:

def fall_fact(n,k)
  if k <= 0
    1
  else
    n*fall_fact(n - 1, k - 1)
  end
end

4

Basta chiamare questa funzione

def factorial(n=0)
  (1..n).inject(:*)
end

esempi

factorial(3)
factorial(11)

3

L'utilizzo Math.gamma.floorè un modo semplice per produrre un'approssimazione e poi arrotondarla per difetto al risultato intero corretto. Dovrebbe funzionare per tutti i numeri interi, includere un controllo dell'input se necessario.


6
Correzione: dopo n = 22che cessa di dare una risposta esatta e produce approssimazioni.
Ayarch

2

Con grande rispetto per tutti coloro che hanno partecipato e hanno speso il loro tempo per aiutarci, vorrei condividere i miei parametri di riferimento delle soluzioni elencate qui. Parametri:

iterazioni = 1000

n = 6

                                     user     system      total        real
Math.gamma(n+1)                   0.000383   0.000106   0.000489 (  0.000487)
(1..n).inject(:*) || 1            0.003986   0.000000   0.003986 (  0.003987)
(1..n).reduce(1, :*)              0.003926   0.000000   0.003926 (  0.004023)
1.upto(n) {|x| factorial *= x }   0.003748   0.011734   0.015482 (  0.022795)

Per n = 10

  user     system      total        real
0.000378   0.000102   0.000480 (  0.000477)
0.004469   0.000007   0.004476 (  0.004491)
0.004532   0.000024   0.004556 (  0.005119)
0.027720   0.011211   0.038931 (  0.058309)

1
Vale la pena notare che il più veloce Math.gamma(n+1)è anche solo approssimativo per n> 22, quindi potrebbe non essere adatto a tutti i casi d'uso.
Neil Slater

1

Solo un altro modo per farlo, anche se in realtà non è necessario.

class Factorial
   attr_reader :num
   def initialize(num)
      @num = num
   end

   def find_factorial
      (1..num).inject(:*) || 1
   end
end

number = Factorial.new(8).find_factorial
puts number


1

Ecco la mia versione che mi sembra chiara anche se non è così pulita.

def factorial(num)
    step = 0
    (num - 1).times do (step += 1 ;num *= step) end
    return num
end

Questa era la mia linea di test IRB che mostrava ogni passaggio.

num = 8;step = 0;(num - 1).times do (step += 1 ;num *= step; puts num) end;num

0
class Integer
  def factorial
    return self < 0 ? false : self==0 ? 1 : self.downto(1).inject(:*)
    #Not sure what other libraries say, but my understanding is that factorial of 
    #anything less than 0 does not exist.
  end
end

0

E ancora un altro modo (=

def factorial(number)
  number = number.to_i
  number_range = (number).downto(1).to_a
  factorial = number_range.inject(:*)
  puts "The factorial of #{number} is #{factorial}"
end
factorial(#number)

0

Solo un altro modo per farlo:

# fact(n) => Computes the Factorial of "n" = n!

def fact(n) (1..n).inject(1) {|r,i| r*i }end

fact(6) => 720

0

Perché la libreria standard richiederebbe un metodo fattoriale, quando esiste un iteratore integrato per questo scopo esatto? Si chiama upto.

No, non è necessario utilizzare la ricorsione, come mostrano tutte queste altre risposte.

def fact(n)
  n == 0 ? 1 : n * fact(n - 1)
end  

Piuttosto, l'iteratore integrato fino a può essere utilizzato per calcolare i fattoriali:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.