Fonction factorielle rubis


88

Je deviens fou: où est la fonction Ruby pour factorielle? Non, je n'ai pas besoin d'implémentations de tutoriel, je veux juste la fonction de la bibliothèque. Ce n'est pas en maths!

Je commence à douter, est-ce une fonction de bibliothèque standard?


63
Je le fais comme6.downto(1).inject(:*)
mckeed

43
@mckeed: Ou (1..6).inject(:*)qui est un peu plus succinct.
sepp2k

8
pourquoi vous attendez-vous à ce qu'il y en ait un?
President James K. Polk

4
Je me demande quel est le statut des bibliothèques de mathématiques et de sciences pour Ruby.
Andrew Grimm

5
Juste une note sur les exemples fournis utilisant inject. (1..num).inject(:*)échoue pour le cas où num == 0. (1..(num.zero? ? 1 : num)).inject(:*)donne la bonne réponse pour le cas 0 et renvoie nilles paramètres négatifs.
Yogh

Réponses:




77

Ce n'est pas dans la bibliothèque standard mais vous pouvez étendre 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 La factorielle itérative est un meilleur choix pour des raisons évidentes de performances.


8
Il a dit explicitement qu'il ne voulait pas de mise en œuvre.
sepp2k

117
Il ne peut pas; mais les gens qui recherchent SO pour "Ruby factorial" le pourraient.
Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby est tout simplement faux. Il n'y a pas de cas pour 0
Douglas

La version récursive est-elle réellement plus lente? Cela peut dépendre du fait que Ruby effectue une optimisation récursive de la queue.
Lex Lindsey

24

Honteusement cribbed de http://rosettacode.org/wiki/Factorial#Ruby , mon préféré est

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Cette implémentation est également la plus rapide parmi les variantes répertoriées dans Rosetta Code.

mise à jour n ° 1

Ajouté || 1pour gérer le cas zéro.

mise à jour # 2

Avec nos remerciements et nos remerciements à Mark Thomas , voici une version un peu plus efficace, élégante et obscure:

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

1
Qu'est ce que ça veut dire?! ouais c'est rapide mais c'est très peu convivial
niccolo m.

3
c'est également incorrect pour 0! - devrait être quelque chose comme: if self <= 1; 1; autre; (1.. self) .reduce (: *); end
Tarmo

8
@allen - Ne blâmez pas la langue si vous ne pouvez pas la comprendre. Cela signifie simplement, ramener la plage 1 à soi, puis en retirer le premier élément (1) (c'est-à-dire ce que signifie réduire en programmation fonctionnelle). Supprimez ensuite le premier élément de ce qui reste (2) et multipliez-les (: *) ensemble. Supprimez maintenant le premier élément de ce qui reste (3) et multipliez-le par le total cumulé. Continuez jusqu'à ce qu'il ne reste plus rien (c'est-à-dire que vous avez géré toute la plage). Si la réduction échoue (parce que le tableau est vide dans le cas de 0!), Il suffit de renvoyer 1 quand même.
SDJMcHattie

Vous pouvez également gérer le cas zéro en spécifiant la valeur initiale reduce: (1..self).reduce(1,:*).
Mark Thomas

3
En fait, vous pouvez utiliser (2..self).reduce(1,:*), si la micro-efficacité est votre truc :)
Mark Thomas


14

Vous pouvez également utiliser une Math.gammafonction qui se résume à factorielle pour les paramètres entiers.


3
D'après la documentation: "Notez que gamma (n) est identique au fait (n-1) pour l'entier n> 0. Cependant, gamma (n) renvoie float et peut être une approximation". Si l'on tient compte de cela, cela fonctionne, mais la solution de réduction semble beaucoup plus simple.
Michael Kohl

Merci pour cela! Mon instinct dit d'utiliser vers la bibliothèque standard sur une réduction personnalisée chaque fois que possible. Le profilage pourrait suggérer le contraire.
David J.

2
Remarque: C'est O (1) et précis pour 0..22: MRI Ruby effectue en fait une recherche pour ces valeurs (voir static const double fact_table[]dans la source ). Au-delà de cela, c'est une approximation. 23 !, par exemple, nécessite une mantisse de 56 bits qu'il est impossible de représenter avec précision en utilisant le double IEEE 754 qui a une mantisse de 53 bits.
fny

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

exemples

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

Quel est le problème avec class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin

@mudasobwa Je l'aime bien, j'ai refactoré pour plus de simplicité.
jasonleonhard

4
Je suggérerais respectueusement que remplacer une méthode d'instance incorporée dans tous les objets Ruby pour renvoyer une valeur de vérité, plutôt qu'une fausse, ne vous fera pas beaucoup d'amis.
MatzFan

Il peut être très dangereux de faire en sorte que l'opérateur negate devienne quelque chose d'autre alors aqu'il se trouve Integerdans le cas de !a... cela peut provoquer un bogue qui est très difficile à dire. S'il ase trouve qu'il s'agit d'un grand nombre comme celui-ci, 357264543le processeur entre dans une grande boucle et les gens peuvent se demander pourquoi le programme devient soudainement lent
nonopolarité

Cette réponse était plus juste une chose intéressante à partager plutôt qu'un exemple pratique à utiliser.
jasonleonhard


6

Je viens d'écrire le mien:

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

En outre, vous pouvez définir une factorielle décroissante:

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

4

Appelez simplement cette fonction

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

exemples

factorial(3)
factorial(11)

3

L'utilisation Math.gamma.floorest un moyen simple de produire une approximation, puis de l'arrondir au résultat entier correct. Devrait fonctionner pour tous les nombres entiers, incluez une vérification d'entrée si nécessaire.


6
Correction: après n = 22qu'il cesse de donner une réponse exacte et produit des approximations.
Ayarch

2

Avec le plus grand respect pour tous ceux qui ont participé et passé leur temps à nous aider, je voudrais partager mes points de repère des solutions énumérées ici. Paramètres:

itérations = 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)

Pour 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
Il convient de noter que le plus rapide Math.gamma(n+1)n'est également approximatif que pour n> 22, il peut donc ne pas convenir à tous les cas d'utilisation.
Neil Slater

1

Juste une autre façon de le faire, même si ce n'est vraiment pas nécessaire.

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

Voici ma version qui me semble claire même si elle n'est pas aussi propre.

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

C'était ma ligne de test irb qui montrait chaque étape.

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

Et encore une autre façon (=

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

Encore une façon de le faire:

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

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

fact(6) => 720

0

Pourquoi la bibliothèque standard nécessiterait-elle une méthode factorielle, alors qu'il existe un itérateur intégré à cet effet précis? Cela s'appelle upto.

Non, vous n'avez pas besoin d'utiliser la récursivité, comme le montrent toutes ces autres réponses.

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

Au contraire, l'itérateur intégré jusqu'à peut être utilisé pour calculer les factorielles:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.