Ruby階乗関数


88

私は夢中になっています:階乗のRuby関数はどこにありますか?いいえ、チュートリアルの実装は必要ありません。ライブラリの関数が必要なだけです。それは数学ではありません!

私は疑問を持ち始めています、それは標準ライブラリ関数ですか?


63
私はそれを好きです6.downto(1).inject(:*)
mckeed 2010年

43
@mckeed:または(1..6).inject(:*)、もう少し簡潔です。
sepp2k 2010年

8
なぜあなたはそれがあると期待しますか?
ジェームズ・K・ポーク大統領2010年

4
Rubyの数学と科学のライブラリの状況はどうなっているのだろうか。
Andrew Grimm 2011年

5
injectを使用して提供された例についてのメモ。(1..num).inject(:*)の場合は失敗しますnum == 0(1..(num.zero? ? 1 : num)).inject(:*)0の場合に正解を示し、nil負のパラメーターを返します。
Yogh 2011

回答:




77

標準ライブラリにはありませんが、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

注意パフォーマンス上の理由から、反復階乗の方が適しています。


8
彼は明確に言った、彼は実装を望んでいない。
sepp2k 2010年

117
彼はそうしないかもしれません。しかし、「ルビー階乗」をSOで検索している人はそうかもしれません。
Pierre-Antoine LaFayette 2010年

1
rosettacode.org/wiki/Factorial#Rubyは間違っています。0の場合はありません
ダグラスG.アレン

再帰バージョンは実際に遅いですか?Rubyが末尾再帰最適化を行うかどうかに依存する可能性があります。
レックスリンジー

24

http://rosettacode.org/wiki/Factorial#Rubyから恥知らずにクリブされました。私の個人的なお気に入りは

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

この実装は、RosettaCodeにリストされているバリアントの中で最速です。

アップデート#1

|| 1ゼロの場合を処理するために追加されました。

アップデート#2

Mark Thomasに感謝と感謝を込めて、もう少し効率的でエレガントでわかりにくいバージョンを次に示します。

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

1
一体どういう意味ですか?!ええ、それは速いですが、それでも非常に使い勝手が
悪い

3
0についても正しくありません!-次のようになります。ifself<= 1; 1; そうしないと; (1..self).reduce(:*); 終了
Tarmo 2012年

8
@ allen-理解できない場合でも、言語を非難しないでください。つまり、範囲1を自分自身に取り、最初の要素(1)を削除します(つまり、関数型プログラミングでreduceが意味することです)。次に、残っているものの最初の要素を削除し(2)、それらを乗算(:*)します。次に、残っているものから最初の要素を削除し(3)、それを現在の合計で乗算します。何も残らなくなるまで(つまり、範囲全体を処理するまで)続けます。reduceが失敗した場合(0の場合は配列が空であるため!)、とにかく1を返します。
SDJMcHattie 2013

reduce:で初期値を指定することにより、ゼロの場合を処理することもできます(1..self).reduce(1,:*)
マークトーマス

3
(2..self).reduce(1,:*)ミクロ効率があなたのものであるならば、実際にあなたは使うことができます:)
マークトーマス


14

Math.gamma整数パラメーターの階乗に要約される関数を使用することもできます。


3
ドキュメントから:「整数n> 0の場合、gamma(n)はfact(n-1)と同じであることに注意してください。ただし、gamma(n)はfloatを返し、近似値になる可能性があります」。それを考慮に入れると、それは機能しますが、reduceソリューションははるかに簡単に思えます。
Michael Kohl 2012年

これをありがとう!私の腸は、可能な限り、カスタム作成されたリデュースよりも標準ライブラリに向けて使用するように言っています。プロファイリングは別のことを示唆するかもしれません。
David J.

2
注:これは、O(1)だとの正確な0..22(参照MRI Rubyが実際にそれらの値のルックアップを実行します。static const double fact_table[]、ソース)。それを超えて、その近似。たとえば、23!には56ビットの仮数が必要ですが、53ビットの仮数を持つIEEE 754doubleを使用して正確に表すことは不可能です。
fny 2014年

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

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

何が問題になっていclass Integer ; def ! ; (1..self).inject(:*) ; end ; endますか?
Aleksei Matiushkin 2016年

@mudasobwa私はそれが好きです、私は単純さのためにリファクタリングしました。
jasonleonhard 2016

4
すべてのRubyオブジェクトに組み込まれているインスタンスメソッドをオーバーライドして、偽の値ではなく、真の値を返すことをお勧めします。多くの友達を作ることはできません。
MatzFan 2017年

aたまたま...Integerの場合、否定演算子を別のものにするのは本当に危険かもしれません!a。そうすると、バグが存在する可能性があり、それを知るのは非常に困難です。そのaような大きな数である場合357264543、プロセッサは大きなループに入り、人々はなぜプログラムが突然遅くなるのか疑問に思うかもしれません
非極性

この答えは、実際に使用する例というよりも、共有するのにちょうどいいものでした。
jasonleonhard


6

私は自分で書いたばかりです:

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

また、階乗冪を定義することもできます。

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

4

この関数を呼び出すだけです

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

factorial(3)
factorial(11)

3

を使用Math.gamma.floorすると、近似値を生成し、それを正しい整数の結果に切り捨てる簡単な方法です。すべての整数で機能するはずです。必要に応じて入力チェックを含めてください。


6
訂正:n = 22正確な答えを出すのをやめ、近似値を生成した後。
Ayarch 2014年

2

参加し、私たちを助けるために時間を費やしたすべての人に敬意を表して、ここにリストされているソリューションのベンチマークを共有したいと思います。パラメータ:

反復= 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の場合

  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
最速のものMath.gamma(n+1)もn> 22の場合にのみ概算であるため、すべてのユースケースに適しているとは限らないことに注意してください。
ニールスレーター

1

それを行うためのちょうど別の方法ですが、実際には必要ありません。

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

これは、それほどきれいではありませんが、私のバージョンは私には明らかなようです。

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

これは、各ステップを示した私のirbテストラインでした。

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

そしてさらに別の方法(=

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

それを行うもう1つの方法:

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

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

fact(6) => 720

0

この正確な目的のための組み込みイテレータがあるのに、なぜ標準ライブラリは階乗メソッドを必要とするのでしょうか?それはと呼ばれuptoます。

いいえ、他のすべての回答が示すように、再帰を使用する必要はありません。

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

むしろ、組み込みのイテレータuptoを使用して、階乗を計算できます。

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.