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?

rutger
la source
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:

136

Il n'y a pas de fonction factorielle dans la bibliothèque standard.

sepp2k
la source
7
Ruby a la Math.gammaméthode, par exemple stackoverflow.com/a/37352690/407213
Dorian
Quelle logique folle! Nous avons (n-1)! fonction et n'ont pas de n simple! !!!
Alexander Gorg
111

Comme ça c'est mieux

(1..n).inject(:*) || 1
Alexandre Revutsky
la source
34
Ou spécifier la valeur initiale directement: (1..n).reduce(1, :*).
Andrew Marshall
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.

Pierre-Antoine LaFayette
la source
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
sans peur
la source
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.

Krishna Prasad Chitrapura
la source
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
Jasonleonhard
la source
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
9

je ferais

(1..n).inject(1, :*)
Santhosh
la source
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
Jack Moon
la source
4

Appelez simplement cette fonction

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

exemples

factorial(3)
factorial(11)
Jasonleonhard
la source
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.

Ayarch
la source
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)
Alexandre Gorg
la source
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
Nate Beers
la source
1

Vous trouverez probablement une demande de fonctionnalité Ruby utile. Il contient un correctif non trivial qui inclut un script de démonstration Bash . La différence de vitesse entre une boucle naïve et la solution présentée dans le lot peut être littéralement 100x (cent fois). Tout écrit en rubis pur.

Martin Vahi
la source
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
Cliff Thelin
la source
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
la source
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)
Sky Davis
la source
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
Robin Wood
la source
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
Donato
la source