Función factorial rubí

88

Me estoy volviendo loco: ¿Dónde está la función Ruby para factorial? No, no necesito implementaciones de tutoriales, solo quiero la función de la biblioteca. ¡No está en matemáticas!

Estoy empezando a dudar, ¿es una función de biblioteca estándar?

rutger
fuente
63
Me gusta6.downto(1).inject(:*)
mckeed
43
@mckeed: O (1..6).inject(:*)que es un poco más sucinto.
sepp2k
8
¿Por qué esperarías que hubiera uno?
Presidente James K. Polk
4
Me pregunto cuál es el estado de las bibliotecas de matemáticas y ciencias para Ruby.
Andrew Grimm
5
Solo una nota sobre los ejemplos proporcionados usando inject. (1..num).inject(:*)falla para el caso donde num == 0. (1..(num.zero? ? 1 : num)).inject(:*)da la respuesta correcta para el caso 0 y devuelve nillos parámetros negativos.
Yogh

Respuestas:

136

No hay función factorial en la biblioteca estándar.

sepp2k
fuente
7
Ruby tiene el Math.gammamétodo, por ejemplo, stackoverflow.com/a/37352690/407213
Dorian
¡Qué lógica más loca! ¡Tenemos (n-1)! función y no tiene n simple! !!!
Alexander Gorg
111

Como si esto fuera mejor

(1..n).inject(:*) || 1
Alexander Revutsky
fuente
34
O especificar el valor inicial directamente: (1..n).reduce(1, :*).
Andrew Marshall
77

No está en la biblioteca estándar, pero puede extender la clase 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 El factorial iterativo es una mejor opción por razones obvias de rendimiento.

Pierre-Antoine LaFayette
fuente
8
Dijo explícitamente que no quiere una implementación.
sepp2k
117
Puede que no; pero las personas que buscan SO para "Ruby factorial" podrían hacerlo.
Pierre-Antoine LaFayette
1
rosettacode.org/wiki/Factorial#Ruby es simplemente incorrecto. No hay caso para 0
Douglas G. Allen
¿Es la versión recursiva más lenta? Podría depender de si Ruby realiza una optimización recursiva de cola.
Lex Lindsey
24

Desvergonzadamente extraído de http://rosettacode.org/wiki/Factorial#Ruby , mi favorito personal es

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Esta implementación también resulta ser la más rápida entre las variantes enumeradas en el Código Rosetta.

actualización # 1

Agregado || 1para manejar el caso cero.

actualización # 2

Con agradecimiento y aprecio a Mark Thomas , aquí hay una versión que es un poco más eficiente, elegante y oscura:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end
intrépido_ tonto
fuente
1
¡¿Qué diablos significa eso?! sí, es rápido pero muy poco amigable
niccolo m.
3
¡también es incorrecto para 0! - debería ser algo como: if self <= 1; 1; más; (1..auto) .reducir (: *); fin
Tarmo
8
@allen: no culpes al idioma si no puedes entenderlo. Simplemente significa, tome el rango 1 a sí mismo, luego elimine el primer elemento (1) (es decir, eso es lo que reduce los medios en la programación funcional). Luego elimine el primer elemento de lo que queda (2) y multiplíquelos (: *) juntos. Ahora elimine el primer elemento de lo que queda (3) y multiplíquelo por el total acumulado. Continúe hasta que no quede nada (es decir, haya manejado todo el rango). Si la reducción falla (¡porque la matriz está vacía en el caso de 0!), Devuelva 1 de todos modos.
SDJMcHattie
También puede manejar el caso cero especificando el valor inicial en reduce: (1..self).reduce(1,:*).
Mark Thomas
3
En realidad, puede usarlo (2..self).reduce(1,:*), si lo suyo es la microeficiencia :)
Mark Thomas
14

También puede usar la Math.gammafunción que se reduce a factorial para parámetros enteros.

Krishna Prasad Chitrapura
fuente
3
De los documentos: "Tenga en cuenta que gamma (n) es lo mismo que fact (n-1) para el entero n> 0. Sin embargo, gamma (n) devuelve float y puede ser una aproximación". Si uno tiene eso en cuenta, funciona, pero la solución de reducción parece mucho más sencilla.
Michael Kohl
¡Gracias por esto! Mi instinto dice que se use para la biblioteca estándar en lugar de una reducción escrita personalizada siempre que sea posible. La elaboración de perfiles podría sugerir lo contrario.
David J.
2
Nota: Es O (1) y precisa para 0..22: MRI Ruby en realidad realiza una búsqueda de esos valores (ver static const double fact_table[]en la fuente ). Más allá de eso, es una aproximación. 23 !, por ejemplo, requiere una mantisa de 56 bits que es imposible de representar con precisión usando el doble IEEE 754 que tiene mantisas de 53 bits.
fny
13
class Integer
  def !
    (1..self).inject(:*)
  end
end

ejemplos

!3  # => 6
!4  # => 24
Jasonleonhard
fuente
¿Qué pasa con class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin
@mudasobwa Me gusta, lo he refactorizado por simplicidad.
Jasonleonhard
4
Respetuosamente sugeriría que anular un método de instancia incorporado en todos los objetos Ruby para devolver un valor verdadero, en lugar de uno falso, puede que no le haga muchos amigos.
MatzFan
Puede ser realmente peligroso hacer que el operador de negación se convierta en otra cosa cuando asucede Integeren el caso de !a... hacerlo puede causar que exista un error que es muy difícil de decir. Si aresulta ser un número grande, 357264543entonces el procesador entra en un gran bucle y la gente puede preguntarse por qué el programa de repente se vuelve lento
polaridad
Esta respuesta fue más algo interesante para compartir que un ejemplo práctico para usar.
jasonleonhard
9

yo lo haría

(1..n).inject(1, :*)
Santhosh
fuente
6

Acabo de escribir el mío:

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

Además, puede definir un factorial descendente:

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

Solo llama a esta función

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

ejemplos

factorial(3)
factorial(11)
Jasonleonhard
fuente
3

Usar Math.gamma.floores una manera fácil de producir una aproximación y luego redondearla hacia abajo al resultado entero correcto. Debería funcionar para todos los enteros, incluya una verificación de entrada si es necesario.

Ayarch
fuente
6
Corrección: Después n = 22deja de dar una respuesta exacta y produce aproximaciones.
Ayarch
2

Con mucho respeto a todos los que participaron y dedicaron su tiempo a ayudarnos, me gustaría compartir mis puntos de referencia de las soluciones enumeradas aquí. Parámetros:

iteraciones = 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)

Para 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)
Alexander Gorg
fuente
1
Cabe señalar que el más rápido Math.gamma(n+1)también es solo aproximado para n> 22, por lo que puede no ser adecuado para todos los casos de uso.
Neil Slater
1

Solo otra forma de hacerlo, aunque realmente no es necesario.

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
Cervezas Nate
fuente
1

Probablemente encuentre útil una solicitud de función de Ruby . Contiene un parche no trivial que incluye un script de demostración Bash . La diferencia de velocidad entre un bucle ingenuo y la solución presentada en el lote puede ser literalmente 100x (cien veces). Escrito todo en Ruby puro.

Martin Vahi
fuente
1

Aquí está mi versión que me parece clara a pesar de que no es tan limpia.

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

Esta fue mi línea de prueba de irb que mostró cada paso.

num = 8;step = 0;(num - 1).times do (step += 1 ;num *= step; puts num) end;num
Cliff Thelin
fuente
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
Automatico
fuente
0

Y aún otra forma (=

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)
Sky Davis
fuente
0

Solo una forma más de hacerlo:

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

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

fact(6) => 720
Robin Wood
fuente
0

¿Por qué la biblioteca estándar requeriría un método factorial, cuando hay un iterador incorporado para este propósito exacto? Se llamaupto .

No, no es necesario utilizar la recursividad, como muestran todas estas otras respuestas.

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

Más bien, el iterador incorporado hasta se puede usar para calcular factoriales:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Donato
fuente