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?


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:




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.


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

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


14

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


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

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


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

4

Panggil saja fungsi ini

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

contoh

factorial(3)
factorial(11)

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.


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)

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

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.


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

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

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)

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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.