Ruby-Fakultätsfunktion

87

Ich werde verrückt: Wo ist die Ruby-Funktion für Fakultät? Nein, ich brauche keine Tutorial-Implementierungen, ich möchte nur die Funktion aus der Bibliothek. Es ist nicht in Mathe!

Ich fange an zu zweifeln, ist es eine Standardbibliotheksfunktion?

Rutger
quelle
63
Ich mache es wie6.downto(1).inject(:*)
mckeed
42
@mckeed: Oder (1..6).inject(:*)was etwas prägnanter ist.
sepp2k
8
Warum sollten Sie damit rechnen?
Präsident James K. Polk
4
Ich frage mich, wie der Status der Mathematik- und Naturwissenschaftsbibliotheken für Ruby ist.
Andrew Grimm
5
Nur ein Hinweis zu den bereitgestellten Beispielen mit injizieren. (1..num).inject(:*)schlägt für den Fall fehl, wo num == 0. (1..(num.zero? ? 1 : num)).inject(:*)gibt die richtige Antwort für den Fall 0 und gibt nilfür negative Parameter zurück.
Jogh

Antworten:

135

In der Standardbibliothek gibt es keine Fakultätsfunktion.

sepp2k
quelle
7
Ruby hat die Math.gammaMethode, zB stackoverflow.com/a/37352690/407213
Dorian
Was für eine verrückte Logik! Wir haben (n-1)! Funktion und haben keine Ebene n! !!!
Alexander Gorg
109

So ist das besser

(1..n).inject(:*) || 1
Alexander Revutsky
quelle
33
Oder geben Sie den Anfangswert direkt an : (1..n).reduce(1, :*).
Andrew Marshall
77

Es ist nicht in der Standardbibliothek enthalten, aber Sie können die Integer-Klasse erweitern.

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 Iterative Fakultät ist aus offensichtlichen Leistungsgründen eine bessere Wahl.

Pierre-Antoine LaFayette
quelle
8
Er sagte ausdrücklich, er wolle keine Implementierung.
sepp2k
115
Er kann nicht; aber Leute, die SO nach "Ruby Fakultät" suchen, könnten.
Pierre-Antoine LaFayette
1
rosettacode.org/wiki/Factorial#Ruby ist einfach falsch. Es gibt keinen Fall für 0
Douglas G. Allen
Ist die rekursive Version tatsächlich langsamer? Es kann davon abhängen, ob Ruby eine rekursive Optimierung durchführt.
Lex Lindsey
23

Schamlos von http://rosettacode.org/wiki/Factorial#Ruby abgeschnitten , ist mein persönlicher Favorit

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Diese Implementierung ist auch die schnellste unter den in Rosetta Code aufgeführten Varianten.

Update Nr. 1

Hinzugefügt || 1, um den Nullfall zu behandeln.

Update Nr. 2

Mit Dank und Anerkennung an Mark Thomas ist hier eine Version, die etwas effizienter, eleganter und dunkler ist:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end
furchtloser Dummkopf
quelle
1
Was zum Teufel bedeutet das?! Ja, es ist schnell, aber es ist sehr unfreundlich
niccolo m.
3
es ist auch falsch für 0! - sollte so etwas wie sein: wenn self <= 1; 1; sonst; (1..selbst) .reduzieren (: *); Ende
Tarmo
8
@allen - Gib der Sprache keine Schuld, wenn du sie nicht verstehen kannst. Es bedeutet einfach, den Bereich 1 auf sich selbst zu nehmen und dann das erste Element (1) daraus zu entfernen (dh das bedeutet Reduzieren in der funktionalen Programmierung). Entfernen Sie dann das erste Element von dem, was noch übrig ist (2) und multiplizieren Sie diese (: *) miteinander. Entfernen Sie nun das erste Element von den verbleibenden Elementen (3) und multiplizieren Sie es mit der laufenden Summe. Fahren Sie fort, bis nichts mehr übrig ist (dh Sie haben den gesamten Bereich bearbeitet). Wenn Reduzieren fehlschlägt (weil das Array im Fall von 0 leer ist!), Geben Sie trotzdem 1 zurück.
SDJMcHattie
Sie können den Nullfall auch behandeln, indem Sie den Anfangswert in reduce: angeben (1..self).reduce(1,:*).
Mark Thomas
3
Eigentlich können Sie verwenden (2..self).reduce(1,:*), wenn Mikroeffizienz Ihr Ding ist :)
Mark Thomas
14

Sie können auch eine Math.gammaFunktion verwenden, die für ganzzahlige Parameter auf Fakultät hinausläuft.

Krishna Prasad Chitrapura
quelle
3
Aus den Dokumenten: "Beachten Sie, dass Gamma (n) dasselbe ist wie Fakt (n-1) für eine ganze Zahl n> 0. Gamma (n) gibt jedoch float zurück und kann eine Annäherung sein." Wenn man das berücksichtigt, funktioniert es, aber die Reduktionslösung scheint viel einfacher zu sein.
Michael Kohl
Danke dafür! Mein Bauch sagt, dass ich die Standardbibliothek über eine benutzerdefinierte Reduzierung verwenden soll, wann immer dies möglich ist. Die Profilerstellung könnte etwas anderes vorschlagen.
David J.
2
Hinweis: Es ist O (1) und genau für 0..22: MRI Ruby führt tatsächlich eine Suche nach diesen Werten durch (siehe static const double fact_table[]in der Quelle ). Darüber hinaus ist es eine Annäherung. 23! Erfordert beispielsweise eine 56-Bit-Mantisse, die mit dem IEEE 754-Doppel mit 53-Bit-Mantissen nicht genau dargestellt werden kann.
fny
13

In Mathe factorial of nist nur die gamma function of n+1
(siehe: http://en.wikipedia.org/wiki/Gamma_function )

Ruby hat es Math.gamma()also einfach benutzt Math.gamma(n+1)und auf Wunsch auf eine ganze Zahl zurückgesetzt.

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

Beispiele

!3  # => 6
!4  # => 24
jasonleonhard
quelle
Was ist los mit class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin
@mudasobwa Ich mag es, ich habe der Einfachheit halber überarbeitet.
Jasonleonhard
4
Ich würde respektvoll vorschlagen, dass das Überschreiben einer Instanzmethode, die in alle Ruby-Objekte integriert ist, um einen wahrheitsgemäßen Wert anstelle eines falschen Werts zurückzugeben, Sie möglicherweise nicht zu vielen Freunden macht.
MatzFan
Es kann sehr gefährlich sein, den Negationsoperator zu etwas anderem zu machen, wenn adies Integerder Fall ist. !aAndernfalls kann ein Fehler auftreten, der sehr schwer zu erkennen ist. Wenn es sich azufällig um eine große Zahl 357264543handelt,
gerät
Diese Antwort war eher eine coole Sache als ein praktisches Beispiel.
Jasonleonhard
8

Ich würde es tun

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

Ich habe gerade mein eigenes geschrieben:

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

Sie können auch eine fallende Fakultät definieren:

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

Rufen Sie einfach diese Funktion auf

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

Beispiele

factorial(3)
factorial(11)
jasonleonhard
quelle
3

Die Verwendung Math.gamma.floorist eine einfache Möglichkeit, eine Näherung zu erstellen und diese dann wieder auf das richtige ganzzahlige Ergebnis abzurunden. Sollte für alle Ganzzahlen funktionieren, fügen Sie gegebenenfalls eine Eingabeprüfung hinzu.

Ayarch
quelle
6
Korrektur: Nachdem n = 22es aufgehört hat, eine genaue Antwort zu geben und Annäherungen erzeugt.
Ayarch
2

Mit großem Respekt für alle, die teilgenommen haben und ihre Zeit damit verbracht haben, uns zu helfen, möchte ich meine Benchmarks für die hier aufgeführten Lösungen teilen. Parameter:

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

Für 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
quelle
1
Es ist erwähnenswert, dass der schnellste Math.gamma(n+1)auch nur ungefähr für n> 22 ist und daher möglicherweise nicht für alle Anwendungsfälle geeignet ist.
Neil Slater
1

Nur ein anderer Weg, obwohl es wirklich nicht notwendig ist.

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 Biere
quelle
1

Sie werden wahrscheinlich eine Ruby- Funktionsanforderung nützlich finden. Es enthält einen nicht trivialen Patch , der ein Demo-Bash-Skript enthält . Der Geschwindigkeitsunterschied zwischen einer naiven Schleife und der in der Charge dargestellten Lösung kann buchstäblich 100x (hundertfach) betragen. Geschrieben alles in reinem Rubin.

Martin Vahi
quelle
1

Hier scheint mir meine Version klar zu sein, obwohl sie nicht so sauber ist.

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

Dies war meine irb-Testlinie, die jeden Schritt zeigte.

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

Und noch ein anderer Weg (=

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
quelle
0

Nur noch eine Möglichkeit:

# 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
quelle
0

Warum sollte die Standardbibliothek eine faktorielle Methode erfordern, wenn es genau für diesen Zweck einen integrierten Iterator gibt? Es wird genanntupto .

Nein, Sie müssen keine Rekursion verwenden, wie all diese anderen Antworten zeigen.

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

Vielmehr kann der eingebaute Iterator bis zu verwendet werden, um Fakultäten zu berechnen:

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