Ruby faktöriyel işlevi


89

Deliriyorum: Faktöriyel için Ruby işlevi nerede? Hayır, eğitim uygulamalarına ihtiyacım yok, sadece kütüphaneden işlevi istiyorum. Matematikte değil!

Şüphe etmeye başlıyorum, bu standart bir kütüphane işlevi mi?


63
Ben gibi yapıyorum6.downto(1).inject(:*)
mckeed

43
@mckeed: Veya (1..6).inject(:*)biraz daha özlü olan.
eylül

8
neden bir tane olmasını bekliyorsunuz?
Başkan James K. Polk

4
Ruby için matematik ve fen kütüphanelerinin durumunun ne olduğunu merak ediyorum.
Andrew Grimm

5
İnject kullanarak sağlanan örneklerle ilgili bir not. (1..num).inject(:*)olduğu durumda başarısız olur num == 0. (1..(num.zero? ? 1 : num)).inject(:*)0 durumu için doğru cevabı verir ve nilnegatif parametreler için geri döner .
Yogh

Yanıtlar:




77

Standart kitaplıkta değil, ancak Integer sınıfını genişletebilirsiniz.

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 Yinelemeli faktöryel, bariz performans nedenleri için daha iyi bir seçimdir.


8
Açıkça, bir uygulama istemediğini söyledi.
sepp2k

117
O olmayabilir; ancak SO'da "Ruby faktöriyel" için arama yapan kişiler olabilir.
Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby yanlıştır. 0 için bir vaka yok
Douglas

Özyinelemeli sürüm aslında daha mı yavaş? Ruby'nin kuyruk özyinelemeli optimizasyon yapıp yapmadığına bağlı olabilir.
Lex Lindsey

24

Utanmazca http://rosettacode.org/wiki/Factorial#Ruby'den alıntı yapıldı , kişisel favorim

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Bu uygulama aynı zamanda Rosetta Kodunda listelenen varyantlar arasında en hızlı olanıdır.

1 numaralı güncelleme

|| 1Sıfır durumu işlemek için eklendi .

güncelleme 2

Mark Thomas'a teşekkür ve minnettarlıkla , işte biraz daha verimli, zarif ve belirsiz bir versiyon:

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

1
bu ne demek oluyor ?! evet hızlı ama kullanıcı dostu değil
niccolo m.

3
0 için de yanlış! - şöyle bir şey olmalıdır: if self <= 1; 1; Başka; (1. kendini). Azalt (: *); bitiş
Tarmo

9
@allen - Anlayamıyorsanız dili suçlamayın. Basitçe, 1 aralığını kendinize almak, ardından ilk elemanı (1) ondan çıkarmak anlamına gelir (yani, işlevsel programlamada azaltmanın anlamı budur). Sonra geriye kalanın ilk öğesini kaldırın (2) ve bunları birlikte çarpın (: *). Şimdi ilk öğeyi kalandan çıkarın (3) ve bunu değişen toplamla çarpın. Hiçbir şey kalmayana kadar devam edin (yani tüm aralığı ele aldınız). Azaltma başarısız olursa (çünkü 0 durumunda dizi boştur!), O zaman yine de 1 döndür.
SDJMcHattie

Ayrıca başlangıç değeri belirterek sıfır durumda işleyebilir reduce: (1..self).reduce(1,:*).
Mark Thomas

3
Aslında (2..self).reduce(1,:*)mikro verimlilik sizin işiniz ise kullanabilirsiniz :)
Mark Thomas


14

Math.gammaTamsayı parametreleri için faktöriyel hale gelen işlevi de kullanabilirsiniz .


3
Dokümanlardan: "n> 0 tamsayısı için gama (n) 'nin olgu (n-1) ile aynı olduğuna dikkat edin. Ancak gama (n) float döndürür ve bir yaklaşım olabilir". Biri bunu hesaba katarsa ​​işe yarıyor, ancak azaltma çözümü çok daha basit görünüyor.
Michael Kohl

Bunun için teşekkürler! Bağırsağım, mümkün olduğunda özel yazılmış bir indirim yerine standart kitaplığa doğru kullanmayı söylüyor. Profil oluşturma aksini önerebilir.
David J.

2
Not: için bu kadar Ç (1) ve hassas 0..22: MRG Ruby aslında bu değerler için bir arama (bkz gerçekleştirir static const double fact_table[]içinde kaynağın ). Bunun ötesinde, bu bir yaklaşım. 23!, Örneğin, 53 bitlik mantislere sahip IEEE 754 double kullanılarak tam olarak temsil edilmesi imkansız olan 56 bitlik bir mantis gerektirir.
fny

13
class Integer
  def !
    (1..self).inject(:*)
  end
end

örnekler

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

Neyin var class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin

@mudasobwa Hoşuma gitti, basitlik için yeniden düzenledim.
jasonleonhard

4
Saygılarımla, sahte bir değer yerine gerçek bir değer döndürmek için tüm Ruby nesnelerine dahil edilmiş bir örnek yöntemini geçersiz kılmanın sizi çok fazla arkadaş yapmayacağını düşünüyorum.
MatzFan

Zaman başka bir şey haline inkâr operatörü yapmak gerçekten tehlikeli olabileceğini aolur Integerdurumunda !a... anlatmak çok zor varoldukları için bir hata neden olabilir Bunu yaparken. Böyle abüyük bir sayı olursa 357264543, işlemci büyük bir döngüye giriyor ve insanlar programın neden birdenbire yavaşladığını merak edebilir
nonopolarite

Bu cevap, pratik bir örnek olmaktan çok, paylaşılacak harika bir şeydi.
jasonleonhard


6

Ben sadece kendim yazdım:

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

Ayrıca, düşen bir faktöriyel tanımlayabilirsiniz:

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

4

Sadece bu işlevi çağırın

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

örnekler

factorial(3)
factorial(11)

3

Kullanmak Math.gamma.floor, bir yaklaşım oluşturmanın ve ardından onu doğru tam sayı sonucuna geri yuvarlamanın kolay bir yoludur. Tüm Tamsayılar için çalışmalıdır, gerekirse bir giriş kontrolü ekleyin.


6
Düzeltme: n = 22Kesin bir cevap vermeyi bıraktıktan ve tahminler ürettikten sonra .
Ayarch

2

Katılan ve bize yardım etmek için zaman ayıran herkese büyük bir saygı ile, burada listelenen çözümlerle ilgili kıyaslamalarımı paylaşmak istiyorum. Parametreler:

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

N = 10 için

  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
En hızlı olanın Math.gamma(n+1)da yalnızca n> 22 için yaklaşık değer olduğuna dikkat etmek gerekir , bu nedenle tüm kullanım durumları için uygun olmayabilir.
Neil Slater

1

Gerçekten gerekli olmasa da, bunu yapmanın başka bir yolu.

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

İşte benim versiyonum o kadar temiz olmasa da bana net görünüyor.

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

Bu, her adımı gösteren irb test hattımdı.

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

Ve yine başka bir yol (=

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

Bunu yapmanın bir yolu daha:

# fact(n) => Computes the Factorial of "n" = n!

def fact(n) (1..n).inject(1) {|r,i| r*i }end

fact(6) => 720

0

Tam olarak bu amaç için yerleşik bir yineleyici varken, standart kitaplık neden bir faktöryel yöntem gerektirsin? Denir upto.

Hayır, diğer tüm cevapların gösterdiği gibi özyineleme kullanmanıza gerek yoktur.

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

Bunun yerine, faktöriyelleri hesaplamak için yerleşik yineleyici kullanılabilir:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.