100

I'm going crazy: Where is the Ruby function for factorial? No, I don't need tutorial implementations, I just want the function from the library. It's not in Math!

I'm starting to doubt, is it a standard library function?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
rutger
  • 2,315
  • 4
  • 16
  • 9

20 Answers20

140

There is no factorial function in the standard library.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
121

Like this is better

(1..n).inject(:*) || 1
Alexander Revutsky
  • 1,323
  • 1
  • 9
  • 4
77

It's not in the standard library but you can extend the Integer class.

class Integer
  def factorial_recursive
    self <= 1 ? 1 : self * (self - 1).factorial_recursive
  end
  def factorial_iterative
    f = 1; for i in 1..self; f *= i; end; f
  end
  alias :factorial :factorial_iterative
end

N.B. Iterative factorial is a better choice for obvious performance reasons.

Daniel Diekmeier
  • 3,401
  • 18
  • 33
Pierre-Antoine LaFayette
  • 24,222
  • 8
  • 54
  • 58
29

Shamelessly cribbed from http://rosettacode.org/wiki/Factorial#Ruby, my personal favorite is

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

This implementation also happens to be the fastest among the variants listed in Rosetta Code.

update #1

Added || 1 to handle the zero case.

update #2

With thanks and appreciation to Mark Thomas, here's a version that is a bit more efficient, elegant and obscure:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end
fearless_fool
  • 33,645
  • 23
  • 135
  • 217
  • 1
    what the heck does that mean?! yeah it's fast but its very unuserfriendly though – chip May 17 '12 at 13:19
  • 3
    it's also incorrect for 0! - should be something like: if self <= 1; 1; else; (1..self).reduce(:*); end – Tarmo May 21 '12 at 19:55
  • 9
    @allen - Don't blame the language if you can't understand it. It simply means, take the range 1 to self, then remove the first element (1) from it (i.e. that's what reduce means in functional programming). Then remove the first element of what's left (2) and multiply (:*) those together. Now remove the first element from what's left (3) and multiply that with the running total. Keep going until there's nothing left (i.e. you've handled the entire range). If reduce fails (because the array is empty in the case of 0!) then just return 1 anyway. – SDJMcHattie Dec 17 '13 at 23:11
  • You can also handle the zero case by specifying the initial value in `reduce`: `(1..self).reduce(1,:*)`. – Mark Thomas Feb 01 '18 at 15:26
  • 3
    Actually you can use `(2..self).reduce(1,:*)`, if micro-efficiency is your thing :) – Mark Thomas Feb 01 '18 at 15:28
20

In math, factorial of n is just the gamma function of n+1
(see: http://en.wikipedia.org/wiki/Gamma_function)

Ruby has Math.gamma() so just use Math.gamma(n+1) and cast it back to an integer if desired.

Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
14

You could also use Math.gamma function which boils down to factorial for integer parameters.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
  • 3
    From the docs: "Note that gamma(n) is same as fact(n-1) for integer n > 0. However gamma(n) returns float and can be an approximation". If one takes that into account, it works, but the reduce solution seems a lot more straight forward. – Michael Kohl Jan 02 '12 at 15:51
  • Thanks for this! My gut says to use towards the standard library over a custom-written reduce whenever possible. Profiling might suggest otherwise. – David J. Sep 18 '12 at 17:31
  • 2
    Note: It's O(1) and precise for `0..22`: MRI Ruby actually performs a lookup for those values (see `static const double fact_table[]` in the [source](http://www.ruby-doc.org/core-2.1.1/Math.html#method-c-lgamma)). Beyond that, its an approximation. 23!, for instance, requires a 56-bit mantissa which is impossible to represent precisely using he IEEE 754 double which has a 53-bit mantissas. – fny Mar 17 '14 at 20:02
13
class Integer
  def !
    (1..self).inject(:*)
  end
end

examples

!3  # => 6
!4  # => 24
jasonleonhard
  • 12,047
  • 89
  • 66
  • What’s wrong with `class Integer ; def ! ; (1..self).inject(:*) ; end ; end`? – Aleksei Matiushkin Jun 21 '16 at 12:53
  • @mudasobwa I like it, I have refactored for simplicity. – jasonleonhard Jul 18 '16 at 19:14
  • 4
    I'd respectfully suggest that overriding an [instance method](https://ruby-doc.org/core-2.4.2/BasicObject.html#method-i-21) incorporated into all Ruby objects to return a truthy value, rather than a falsy one may not make you many friends. – MatzFan Oct 03 '17 at 15:20
  • It might be really dangerous to make the negate operator to become something else when `a` happens to be `Integer` in the case of `!a`... doing so may cause a bug to exist which is very hard to tell. If `a` happens to be a big number such as `357264543` then the processor is going into a big loop and people may wonder why the program all of a sudden becomes slow – nonopolarity Jul 08 '19 at 07:19
  • This answer was more just a cool thing to share rather than a practical example to use. – jasonleonhard Jul 09 '19 at 17:57
9

I would do

(1..n).inject(1, :*)
Santhosh
  • 28,097
  • 9
  • 82
  • 87
6

I just wrote my own:

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

Also, you can define a falling factorial:

def fall_fact(n,k)
  if k <= 0
    1
  else
    n*fall_fact(n - 1, k - 1)
  end
end
Jack Moon
  • 61
  • 1
  • 3
5

With high respect to all who participated and spent their time to help us, I would like to share my benchmarks of the solutions listed here. Params:

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

For 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
  • 1,049
  • 16
  • 32
  • 1
    Worth noting that the fastest one `Math.gamma(n+1)` is also only approximate for n > 22, so may not be suitable for all use cases. – Neil Slater Oct 16 '19 at 12:00
4

Just call this function

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

examples

factorial(3)
factorial(11)
jasonleonhard
  • 12,047
  • 89
  • 66
3

Using Math.gamma.floor is an easy way to produce an approximation and then round it back down to the correct integer result. Should work for all Integers, include an input check if necessary.

Ayarch
  • 31
  • 1
  • 6
    Correction: After `n = 22` it ceases to give an exact answer and produces approximations. – Ayarch Jan 10 '14 at 18:16
1

Just another way to do it, although it really isn't necessary.

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
  • 1,355
  • 2
  • 13
  • 22
1

You will probably find a Ruby feature request useful. It contains a nontrivial patch that includes a demo Bash script. The speed difference between a naive loop and the solution presented in the batch can be literally 100x (hundred fold). Written all in pure Ruby.

Martin Vahi
  • 129
  • 1
  • 5
1

Here is my version seems to be clear to me even though it's not as clean.

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

This was my irb testing line that showed each step.

num = 8;step = 0;(num - 1).times do (step += 1 ;num *= step; puts num) end;num
Cliff Thelin
  • 61
  • 1
  • 2
1

Why would the standard library require a factorial method, when there is a built-in iterator for this exact purpose? It is called upto.

No you do not need to use recursion, like all these other answers show.

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

Rather, the built-in iterator upto can be used to calculate factorials:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101
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
  • 12,420
  • 9
  • 82
  • 110
0

And yet another way (=

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
  • 3
  • 5
0

Just one more way to do it:

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

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

fact(6) => 720
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Robin Wood
  • 41
  • 3
0

In Ruby standard library function for factorial is not available. We can make a simple function of factorial in ruby in this way.

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

puts factorial_number(6) #Output is 720   => (6*5*4*3*2*1)
puts factorial_number(8) #Output is 40320 => (8*7*6*5*4*3*2*1)
Raksha Saini
  • 604
  • 12
  • 28