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?


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:




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.


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

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,


14

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


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

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


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

4

Po prostu wywołaj tę funkcję

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

przykłady

factorial(3)
factorial(11)

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.


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)

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


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

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

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)

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.