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?


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:




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.


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

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.


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

¿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


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

4

Solo llama a esta función

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

ejemplos

factorial(3)
factorial(11)

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.


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)

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


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

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

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)

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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.