Ruby阶乘函数


88

我快疯了:阶乘的Ruby函数在哪里?不,我不需要教程实现,我只需要库中的函数。这不是数学!

我开始怀疑,这是标准库函数吗?


63
我这样做6.downto(1).inject(:*)
mckeed 2010年

43
@mckeed:或者(1..6).inject(:*)更简洁。
sepp2k 2010年

8
您为什么期望会有一个?
总统詹姆斯·波尔克(James K. Polk)2010年

4
我想知道Ruby的数学和科学图书馆的状况如何。
Andrew Grimm

5
只是使用inject提供的示例的注释。(1..num).inject(:*)对于的情况失败num == 0(1..(num.zero? ? 1 : num)).inject(:*)给出0情况的正确答案,并返回nil否定参数。
Yogh,

Answers:




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中搜索“ Ruby析因”。
皮埃尔·安托万·拉斐特

1
rosettacode.org/wiki/Factorial#Ruby是错误的。没有0的情况
Douglas G. Allen

递归版本实际上慢吗?这可能取决于Ruby是否进行尾递归优化。
Lex Lindsey '18

24

无耻地从http://rosettacode.org/wiki/Factorial#Ruby抄袭,我个人最喜欢的是

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

在Rosetta Code中列出的变体中,这种实现也是最快的。

更新#1

添加 || 1以处理零情况。

更新#2

感谢Mark Thomas的帮助,下面的版本效率更高,更优雅,更晦涩:

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

1
这是什么意思?!是的,虽然速度很快,但是非常不方便
niccolo m。

3
也是0不正确!-应该是这样的:如果self <= 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,:*)如果您需要提高微效率,则可以使用:)
Mark Thomas


14

您也可以使用Math.gamma将整数参数归结为阶乘的函数。


3
从文档中:“请注意,对于整数n> 0,gamma(n)与fact(n-1)相同。但是gamma(n)返回float且可以是近似值。” 如果考虑到这一点,它是可行的,但reduce解决方案似乎更为直接。
Michael Kohl 2012年

谢谢你!我的直觉说,尽可能在定制编写的reduce上使用标准库。剖析可能会建议相反。
David J.

2
注:它的O(1)和精确的0..22:MRI Ruby的实际执行这些值的查询(见static const double fact_table[])。除此之外,它是一个近似值。例如,23!需要56位尾数,而使用具有53位尾数的IEEE 754 double则无法精确表示。
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

4
我谨建议您重写覆盖所有Ruby对象的实例方法以返回真实值,而不是虚假的值可能不会使您成为很多朋友。
MatzFan

如果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


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也仅近似于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

只是另一种方法:

# 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.