Fungsi faktorial Ruby

89

Aku jadi gila: Di mana fungsi Ruby untuk faktorial? Tidak, saya tidak memerlukan implementasi tutorial, saya hanya ingin fungsi dari perpustakaan. Ini bukan di Matematika!

Saya mulai ragu, apakah ini fungsi perpustakaan standar?

rutger
sumber
63
Saya melakukannya seperti6.downto(1).inject(:*)
mckeed
43
@mckeed: Atau (1..6).inject(:*)yang lebih ringkas.
sepp2k
8
mengapa Anda berharap ada satu?
Presiden James K. Polk
4
Saya ingin tahu apa status perpustakaan matematika dan sains untuk Ruby.
Andrew Grimm
5
Perhatikan saja contoh yang diberikan menggunakan inject. (1..num).inject(:*)gagal untuk kasus di mana num == 0. (1..(num.zero? ? 1 : num)).inject(:*)memberikan jawaban yang benar untuk kasus 0 dan mengembalikan niluntuk parameter negatif.
Yogh

Jawaban:

136

Tidak ada fungsi faktorial di perpustakaan standar.

sepp2k.dll
sumber
7
Ruby memiliki Math.gammametode, misalnya stackoverflow.com/a/37352690/407213
Dorian
1
Logika yang gila! Kami memiliki (n-1)! berfungsi dan tidak memiliki n polos! !!!
Alexander Gorg
111

Seperti ini lebih baik

(1..n).inject(:*) || 1
Alexander Revutsky
sumber
34
Atau menentukan nilai awal langsung: (1..n).reduce(1, :*).
Andrew Marshall
77

Ini tidak ada di pustaka standar tetapi Anda dapat memperluas kelas 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

Faktorial berulang NB adalah pilihan yang lebih baik untuk alasan kinerja yang jelas.

Pierre-Antoine LaFayette
sumber
8
Dia secara eksplisit mengatakan, dia tidak menginginkan implementasi.
sepp2k
117
Dia mungkin tidak; tetapi orang yang menelusuri SO untuk "faktorial Ruby" mungkin.
Pierre-Antoine LaFayette
1
rosettacode.org/wiki/Factorial#Ruby salah. Tidak ada kasus untuk 0
Douglas G. Allen
Apakah versi rekursif sebenarnya lebih lambat? Ini mungkin tergantung pada apakah Ruby melakukan pengoptimalan rekursif-ekor.
Lex Lindsey
24

Tanpa malu-malu dikutip dari http://rosettacode.org/wiki/Factorial#Ruby , favorit pribadi saya adalah

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Implementasi ini juga merupakan yang tercepat di antara varian yang terdaftar di Rosetta Code.

perbarui # 1

Ditambahkan || 1untuk menangani kasus nol.

perbarui # 2

Dengan terima kasih dan penghargaan kepada Mark Thomas , berikut adalah versi yang sedikit lebih efisien, elegan, dan tidak jelas:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end
fearless_fool
sumber
1
apa sih artinya itu ?! ya itu cepat tapi meskipun sangat tidak ramah pengguna
niccolo m.
3
itu juga salah untuk 0! - harus seperti ini: if self <= 1; 1; lain; (1..self) .reduce (: *); akhir
Tarmo
9
@allen - Jangan salahkan bahasanya jika Anda tidak dapat memahaminya. Ini berarti, ambil rentang 1 ke diri sendiri, lalu hapus elemen pertama (1) darinya (yaitu, itulah yang mengurangi arti dalam pemrograman fungsional). Kemudian hapus elemen pertama dari apa yang tersisa (2) dan kalikan (: *) bersama-sama. Sekarang hapus elemen pertama dari yang tersisa (3) dan kalikan dengan total yang berjalan. Teruskan sampai tidak ada yang tersisa (mis. Anda telah menangani seluruh jangkauan). Jika pengurangan gagal (karena array kosong dalam kasus 0!) Maka kembalikan 1 saja.
SDJMcHattie
Anda juga dapat menangani kasus nol dengan menentukan nilai awal di reduce: (1..self).reduce(1,:*).
Mark Thomas
3
Sebenarnya Anda dapat menggunakan (2..self).reduce(1,:*), jika efisiensi mikro adalah hal Anda :)
Mark Thomas
16

Dalam matematika, factorial of nhanya gamma function of n+1
(lihat: http://en.wikipedia.org/wiki/Gamma_function )

Ruby Math.gamma()hanya menggunakan Math.gamma(n+1)dan melemparkannya kembali ke integer jika diinginkan.

Albert Renshaw
sumber
14

Anda juga bisa menggunakan Math.gammafungsi yang bermuara pada faktorial untuk parameter integer.

Krishna Prasad Chitrapura
sumber
3
Dari dokumen: "Perhatikan bahwa gamma (n) sama dengan fakta (n-1) untuk integer n> 0. Namun gamma (n) mengembalikan float dan dapat menjadi perkiraan". Jika seseorang memperhitungkannya, itu berhasil, tetapi solusi pengurangan tampaknya jauh lebih mudah.
Michael Kohl
Terima kasih untuk ini! Naluri saya mengatakan untuk menggunakan perpustakaan standar melalui pengurangan yang ditulis khusus bila memungkinkan. Pembuatan profil mungkin menyarankan sebaliknya.
David J.
2
Catatan: Ini O (1) dan tepat untuk 0..22: MRI Ruby sebenarnya melakukan pencarian untuk nilai-nilai itu (lihat static const double fact_table[]di sumber ). Di luar itu, itu adalah perkiraan. 23 !, misalnya, membutuhkan mantissa 56-bit yang tidak mungkin direpresentasikan secara tepat menggunakan IEEE 754 double yang memiliki mantisa 53-bit.
fny
13
class Integer
  def !
    (1..self).inject(:*)
  end
end

contoh

!3  # => 6
!4  # => 24
jasonleonhard
sumber
Ada apa dengan class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin
@mudasobwa Saya suka, saya telah merefaktor untuk kesederhanaan.
jasonleonhard
4
Saya dengan hormat menyarankan bahwa menimpa metode contoh yang dimasukkan ke semua objek Ruby untuk mengembalikan nilai yang sebenarnya, daripada yang salah mungkin tidak membuat Anda banyak teman.
MatzFan
Mungkin sangat berbahaya untuk membuat operator negate menjadi sesuatu yang lain ketika akebetulan terjadi Integerdalam kasus !a... melakukan hal itu dapat menyebabkan ada bug yang sangat sulit untuk diceritakan. Jika akebetulan jumlahnya besar seperti 357264543maka prosesor akan berputar besar dan orang mungkin bertanya-tanya mengapa program tiba-tiba menjadi lambat
nonopolaritas
Jawaban ini lebih merupakan hal yang keren untuk dibagikan daripada contoh praktis untuk digunakan.
jasonleonhard
9

Saya akan melakukannya

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

Saya baru saja menulis sendiri:

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

Selain itu, Anda dapat menentukan faktorial jatuh:

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

Panggil saja fungsi ini

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

contoh

factorial(3)
factorial(11)
jasonleonhard
sumber
3

Menggunakan Math.gamma.flooradalah cara mudah untuk menghasilkan perkiraan dan kemudian membulatkannya kembali ke hasil bilangan bulat yang benar. Harus berfungsi untuk semua Integer, sertakan pemeriksaan input jika perlu.

Ayarch
sumber
6
Koreksi: Setelah n = 22berhenti memberikan jawaban yang tepat dan menghasilkan perkiraan.
Ayarch
2

Dengan rasa hormat yang tinggi kepada semua yang berpartisipasi dan menghabiskan waktu mereka untuk membantu kami, saya ingin membagikan tolok ukur saya dari solusi yang tercantum di sini. Parameter:

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

Untuk 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
sumber
1
Perlu dicatat bahwa yang tercepat Math.gamma(n+1)juga hanya perkiraan untuk n> 22, jadi mungkin tidak cocok untuk semua kasus penggunaan.
Neil Slater
1

Hanya cara lain untuk melakukannya, meskipun sebenarnya tidak perlu.

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
sumber
1

Anda mungkin akan menemukan permintaan fitur Ruby berguna. Ini berisi patch nontrivial yang menyertakan skrip Bash demo . Perbedaan kecepatan antara loop naif dan solusi yang disajikan dalam batch secara harfiah bisa 100x (seratus kali lipat). Ditulis dengan Ruby murni.

Martin Vahi
sumber
1

Ini adalah versi saya yang tampaknya jelas bagi saya meskipun tidak sebersih itu.

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

Ini adalah garis pengujian irb saya yang menunjukkan setiap langkah.

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

Dan cara lain (=

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

Satu cara lagi untuk melakukannya:

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

Mengapa pustaka standar memerlukan metode faktorial, bila ada iterator bawaan untuk tujuan yang tepat ini? Itu disebut upto.

Tidak, Anda tidak perlu menggunakan rekursi, seperti yang ditunjukkan oleh semua jawaban lainnya.

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

Sebaliknya, iterator bawaan upto dapat digunakan untuk menghitung faktorial:

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