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?


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:




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.


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

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.


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
class Integer
  def !
    (1..self).inject(:*)
  end
end

Beispiele

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

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


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

4

Rufen Sie einfach diese Funktion auf

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

Beispiele

factorial(3)
factorial(11)

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.


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)

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

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.


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

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

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)

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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.