Funkcja silnia Ruby

88

Oszalałem: gdzie jest funkcja Ruby dla silni? Nie, nie potrzebuję implementacji samouczków, chcę tylko funkcję z biblioteki. Nie ma tego w matematyce!

Zaczynam wątpić, czy jest to standardowa funkcja biblioteki?

rutger
źródło
63
Robię to jak6.downto(1).inject(:*)
mckeed
43
@mckeed: Lub (1..6).inject(:*)który jest nieco bardziej zwięzły.
wrzesień
8
dlaczego miałbyś się spodziewać, że taki będzie?
Prezydent James K. Polk
4
Zastanawiam się, jaki jest status bibliotek matematycznych i naukowych dla Rubiego.
Andrew Grimm
5
Tylko uwaga na podanych przykładach z użyciem inject. (1..num).inject(:*)zawodzi w przypadku, gdy num == 0. (1..(num.zero? ? 1 : num)).inject(:*)podaje poprawną odpowiedź dla przypadku 0 i zwraca nildla parametrów ujemnych.
Yogh

Odpowiedzi:

136

W bibliotece standardowej nie ma funkcji silni.

sepp2k
źródło
7
Ruby ma Math.gammametodę, np. Stackoverflow.com/a/37352690/407213
Dorian
Co za szalona logika! Mamy (n-1)! funkcji i nie mają zwykłego n! !!!
Alexander Gorg
111

Tak jest lepiej

(1..n).inject(:*) || 1
Alexander Revutsky
źródło
34
Lub określić wartość początkową bezpośrednio: (1..n).reduce(1, :*).
Andrew Marshall,
77

Nie ma jej w bibliotece standardowej, ale możesz rozszerzyć klasę 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

Uwaga: Silnia iteracyjna jest lepszym wyborem z oczywistych powodów dotyczących wydajności.

Pierre-Antoine LaFayette
źródło
8
Powiedział wprost, że nie chce implementacji.
wrzesień
117
Nie może; ale ludzie szukający SO „silni Ruby” mogą.
Pierre-Antoine LaFayette
1
rosettacode.org/wiki/Factorial#Ruby jest po prostu błędny. Nie ma powodu na 0
Douglas G. Allen
Czy wersja rekurencyjna jest rzeczywiście wolniejsza? Może to zależeć od tego, czy Ruby wykonuje optymalizację rekurencyjną ogona.
Lex Lindsey,
24

Bezwstydnie szopka z http://rosettacode.org/wiki/Factorial#Ruby , moim ulubionym jest

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Ta implementacja jest również najszybsza spośród wariantów wymienionych w kodzie Rosetta.

aktualizacja nr 1

Dodano || 1do obsługi przypadku zerowego.

aktualizacja nr 2

Z podziękowaniami i uznaniem dla Marka Thomasa , oto wersja, która jest nieco bardziej wydajna, elegancka i niejasna:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end
nieustraszony głupiec
źródło
1
co to do cholery znaczy ?! tak, jest szybki, ale jest bardzo nieprzyjazny
niccolo m.
3
jest również niepoprawne dla 0! - powinno wyglądać mniej więcej tak: if self <= 1; 1; jeszcze; (1.. siebie) .reduce (: *); koniec
Tarmo
8
@allen - nie obwiniaj języka, jeśli go nie rozumiesz. Oznacza to po prostu, weź zakres 1 do siebie, a następnie usuń z niego pierwszy element (1) (tj. To jest właśnie to, co redukuje środki w programowaniu funkcjonalnym). Następnie usuń pierwszy element z tego, co zostało (2) i pomnóż (: *) te razem. Teraz usuń pierwszy element z tego, co zostało (3) i pomnóż go przez sumę bieżącą. Kontynuuj, aż nic nie zostanie (tj. Obsłużyłeś cały zakres). Jeśli redukcja się nie powiedzie (ponieważ tablica jest pusta w przypadku 0!), I tak po prostu zwróć 1.
SDJMcHattie
Można również obsługiwać sprawę zerowej określając wartość początkową w reduce: (1..self).reduce(1,:*).
Mark Thomas
3
Właściwie możesz użyć (2..self).reduce(1,:*), jeśli mikroefektywność jest twoją sprawą :)
Mark Thomas,
16

W matematyce factorial of nto tylko gamma function of n+1
(patrz: http://en.wikipedia.org/wiki/Gamma_function )

Ruby ma Math.gamma()więc po prostu użyj Math.gamma(n+1)i w razie potrzeby rzuć go z powrotem na liczbę całkowitą.

Albert Renshaw
źródło
14

Możesz również użyć Math.gammafunkcji, która sprowadza się do silni dla parametrów całkowitych.

Krishna Prasad Chitrapura
źródło
3
Z dokumentacji: „Zwróć uwagę, że gamma (n) jest tym samym, co fakt (n-1) dla liczby całkowitej n> 0. Jednak gamma (n) zwraca wartość zmiennoprzecinkową i może być przybliżeniem”. Jeśli weźmie się to pod uwagę, to działa, ale rozwiązanie redukujące wydaje się o wiele prostsze.
Michael Kohl
Dzięki za to! Moje przeczucie mówi, żebym korzystał z biblioteki standardowej zamiast niestandardowej redukcji, jeśli to możliwe. Profilowanie może sugerować coś innego.
David J.
2
Uwaga: to O (1) i precyzyjna 0..22: MRI Ruby faktycznie wykonuje odnośnika do tych wartości (patrz static const double fact_table[]w źródle ). Poza tym jest to przybliżenie. 23!, Na przykład, wymaga 56-bitowej mantysy, której nie można precyzyjnie odwzorować przy użyciu podwójnego IEEE 754, który ma 53-bitowe mantysy.
fny
13
class Integer
  def !
    (1..self).inject(:*)
  end
end

przykłady

!3  # => 6
!4  # => 24
jasonleonhard
źródło
Co jest nie tak class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin
@mudasobwa Podoba mi się, przerobiłem dla uproszczenia.
jasonleonhard
4
Z całym szacunkiem zasugerowałbym, że nadpisanie metody instancji zawartej we wszystkich obiektach Ruby w celu zwrócenia prawdziwej wartości, a nie fałszywej, może nie przysporzyć Ci wielu przyjaciół.
MatzFan
To może być naprawdę niebezpieczne, jeśli operator negacji stanie się kimś innym, gdy atak się stanie Integerw przypadku !a... może to spowodować powstanie błędu, który jest bardzo trudny do stwierdzenia. Jeśli azdarzy się, że jest to duża liczba, taka jak 357264543wtedy, procesor wpada w dużą pętlę i ludzie mogą się zastanawiać, dlaczego program nagle staje się wolny
niepolarność
Ta odpowiedź była raczej fajną rzeczą do udostępnienia niż praktycznym przykładem do wykorzystania.
jasonleonhard
9

chciałbym zrobić

(1..n).inject(1, :*)
Santhosh
źródło
6

Właśnie napisałem własne:

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

Możesz także zdefiniować silnię opadającą:

def fall_fact(n,k)
  if k <= 0
    1
  else
    n*fall_fact(n - 1, k - 1)
  end
end
Jack Moon
źródło
4

Po prostu wywołaj tę funkcję

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

przykłady

factorial(3)
factorial(11)
jasonleonhard
źródło
3

Użycie Math.gamma.floorjest łatwym sposobem uzyskania przybliżenia, a następnie zaokrąglenia go z powrotem do prawidłowego wyniku będącego liczbą całkowitą. Powinien działać dla wszystkich liczb całkowitych, w razie potrzeby uwzględnij sprawdzenie danych wejściowych.

Ayarch
źródło
6
Korekta: po n = 22tym, jak przestaje udzielać dokładnej odpowiedzi i tworzy przybliżenia.
Ayarch
2

Z całym szacunkiem dla wszystkich, którzy uczestniczyli i poświęcili nam swój czas, chciałbym podzielić się moimi wzorcami rozwiązań wymienionych tutaj. Parametry:

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

Dla 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
źródło
1
Warto zauważyć, że najszybszy Math.gamma(n+1)jest również przybliżony tylko dla n> 22, więc może nie nadawać się do wszystkich przypadków użycia.
Neil Slater
1

Po prostu inny sposób na zrobienie tego, chociaż naprawdę nie jest to konieczne.

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
źródło
1

Prawdopodobnie okaże się przydatna prośba o funkcję Rubiego . Zawiera nietrywialną łatkę, która zawiera skrypt demonstracyjny Bash . Różnica prędkości między naiwną pętlą a rozwiązaniem przedstawionym w partii może wynosić dosłownie 100x (stukrotność). Napisane wszystko w czystym Ruby.

Martin Vahi
źródło
1

Oto moja wersja wydaje mi się jasna, chociaż nie jest tak czysta.

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

To była moja linia testowa IRB, która pokazywała każdy krok.

num = 8;step = 0;(num - 1).times do (step += 1 ;num *= step; puts num) end;num
Cliff Thelin
źródło
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
źródło
0

I jeszcze inny sposób (=

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
źródło
0

Jeszcze tylko jeden sposób, aby to zrobić:

# 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
źródło
0

Dlaczego biblioteka standardowa wymagałaby metody silniowej, skoro do tego celu służy wbudowany iterator? To się nazywa upto.

Nie, nie musisz używać rekursji, jak pokazują wszystkie te odpowiedzi.

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

Raczej wbudowany iterator upto może służyć do obliczania silni:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Donato
źródło