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.