the number of trailing zeros in a factorial of a given number - Ruby

Having a little trouble trying calculate the number of trailing zeros in a factorial of a given number. This is one of the challenges from Codewars- can't get mine to pass.

zeros(12) = 2       #=> 1 * 2 * 3 .. 12 = 479001600 

I think I'm on the wrong path here and there is probably a more elegant ruby way. This is what I have down so far.

def zeros(n) 
    x = (1..n).reduce(:*).to_s.scan(/[^0]/)
    return 0 if x == [] 
    return x[-1].length if x != [] 
end

Answers


This is more of a math question. And you're right, you are off on a wrong path. (I mean the path you are on is going to lead to a very inefficient solution)

Try to reduce the problem mathematically first. (BTW you are shooting for a log N order algorithm.)

In my answer I will try to skip a few steps, because it seems like a homework question.

The number of trailing zeros is going to be equal to the total power of 5s in the multiplication of the series.

the numbers between 1 and n will have n/5, n/25, n/125 numbers which are multiples of 5s, 25s, 125s respectively... and so on.

Try to take these hints and come up with an algorithm to count how many powers of 10 will be crammed in to that factorial.

Spoilers Ahead

I've decided to explain in detail below so if you want to try and solve it yourself then stop reading, try to think about it and then come back here.

Here is a step by step reduction of the problem

1.

The number of trailing zeros in a number is equivalent to the power of 10 in the factor of that number

e.g.

  • 40 = 4 * 10^1 and it has 1 trailing zero
  • 12 = 3 * 4 * 10^0 so it has 0 trailing zeros
  • 1500 = 3 * 5 * 10^2 so it has 2 trailing zeros
2.

The number power of 10 in the factors is the same as the minimum of the power of 2 and power of 5 in the factors

e.g.

  • 50 = 2^1 * 5^2 so the minimum power is 1
  • 300 = 3^1 * 2^2 * 5^2 so the minimum is 2 (we are only concerned with the minimum of the powers of 2 and 5, so ignore powers of 3 and all other prime factors)
3.

In any factorial there will be many more powers of 2 than the powers of 5

e.g.

  • 5! = 2^3 * 3^1 * 5^1
  • 10! = 2^8 * 3^4 * 5^2 * 7^1

As you can see the power of 2 is going to start increasing much faster so the power of 5 will be the minimum of the two.

Hence all we need to do is count the power of 5 in the factorial.

4.

Now lets focus on the power of 5 in any n!

  • 4! ~ 5^0
  • 5! ~ 5^1 (up to 9!)
  • 10! ~ 5^2 (up to 14!)
  • 15! ~ 5^3 (up to `19!)
  • 20! ~ 5^4 (up to 24!)
  • 25! ~ 5^6 (notice the jump from 5^4 to 5^6 because the number 25 adds two powers of 5)
5.

The way I'd like to count the total power of five in a factorial is... count all the multiples of 5, they all add one power of 5. Then count all the multiples of 25, they all add an extra power of 5. Notice how 25 added two powers of 5, so I can put that as, one power because it's a multiple of 5 and one extra power because it's a multiple of 25. Then count all the multiple of 125 (5^3) in the factorial multiplication, they add another extra power of 5... and so on.

6.

So how'd you put that as an algorithm ?

lets say the number is n. So...

  • pow1 = n/5 (rounded down to an integer)
  • pow2 = n/25
  • pow3 = n/125

and so on...

Now the total power pow = pow1 + pow2 + pow3 ...

7.

Now can you express that as a loop?


So, now that @Spunden has so artfully let the cat out of the bag, here's one way to implement it.

Code

def zeros(n)
  return 0 if n.zero?
  k = (Math.log(n)/Math.log(5)).to_i
  m = 5**k
  n*(m-1)/(4*m)
end

Examples

zeros(3)   #=>  0
zeros(5)   #=>  1
zeros(12)  #=>  2
zeros(15)  #=>  3
zeros(20)  #=>  4
zeros(25)  #=>  6
zeros(70)  #=> 16
zeros(75)  #=> 18
zeros(120) #=> 28
zeros(125) #=> 31

Explanation

Suppose n = 128.

Then each number between one and 128 (inclusive) that is divisible by 5^1=>5 provides at least one factor, and there are 128/5 => 25 such numbers. Of these, the only ones that provide more than one factor are those divisible by 5^2=>25, of which there are 128/25 => 5 (25, 50, 75, 100, 125). Of those, there is but 128/125 => 1 that provides more than two factors, and since 125/(5^4) => 0, no numbers contribute more than three divisors. Hence, the total number of five divisors is:

128/5 + 128/25 + 128/125 #=> 31

(Note that, for 125, which has three divisors of 5, one is counted in each of these three terms; for 25, 50, etc., which each have two factors of 5, one is counted in each of the first terms.)

For arbitrary n, we first compute the highest power k for which:

5**k <= n

which is:

k <= Math.log(n)/Math.log(5)

so the largest such value is:

k = (Math.log(n)/Math.log(5)).to_i

As @spundun noted, you could also calculate k by simply iterating, e.g.,

last = 1
(0..1.0/0).find { |i| (last *= 5) > n }

The total number of factors of five is therefore

(n/5) + (n/25) +...+ (n/5**k)

Defining:

r = 1/5,

this sum is seen to be:

n * s

where

s = r + r**2 +...+ r**k

The value of s is the sum of the terms of a geometric series. I forget the formula for that, but recall how it's derived:

s  = r + r**2 +...+ r**k
sr =     r**2 +...+ r**(k+1)

s-sr = r*(1-r**k)

s = r*(1-r**k)/(1-r)

I then did some rearrangement so that only only integer arithmetic would be used to calculate the result.


def zeros(n)
    zeros = 0
    zeros += n /= 5 while n >= 1
    zeros
end

If N is a number then number of trailing zeroes in N! is

N/5 + N/5^2 + N/5^3 ..... N/5^(m-1) WHERE (N/5^m)<1

You can learn here how this formula comes.


Here's a solution that is easier to read:

def zeros(num)
  char_array = num.to_s.split('')
  count = 0

  while char_array.pop == "0"
    count += 1
  end

  count
end

Let me know what you think and feel free to edit if you see an improvement!


The article A Note on Factorial and its Trailing Zeros in GanitCharcha is insightful and has explained the Mathematics behind this well. Take a look.

http://www.ganitcharcha.com/view-article-A-Note-on-Factorial-and-it's-Trailing-Zeros.html


My solution

def zeros(n)
  trailing_zeros = []
  fact = (1..n).inject(:*)
  fact.to_s.split('').reverse.select {|x| break if (x.to_i != 0); trailing_zeros << x}
  return trailing_zeros.count
end

n = int (raw_input())
count = 0
num = 1
for i in xrange(n+1):
        if i != 0:
            num = num * i

while(num >= 10):

    if num%10 == 0:
       count+=1
       num = num/10
    else:
        break

print count

As per the explanation given by @spundan and apart from @cary's code you can find number of trailing zero by just very simple and efficient way..see below code..

def zeros(n)
    ret = 0
    while n > 0 do 
        ret += n / 5
        n = n/5
    end
    ret
end

For example zeros(100000000) this will give you output -> 24999999 With the time Time Elapsed -> 5.0453e-05(Just See 5.0453e-05 ) This is the part of even milliseconds.


    n=int(input())
    j=5
    c=int(0)
    while int(n/j)>0:
        c=c+int(n/j)
        j=j*5
    print(c)

count = 0
i  =5
n = 100
k = n

while(n/i!=0):
    count+=(n/i)
    i=i*5
    n = k
print count

Need Your Help

Pretty Printing a pandas dataframe

python pandas dataframe printing

How can I print a pandas dataframe as a nice text-based table, like the following?

How do properties work in Object Oriented MATLAB?

matlab oop properties matlab-class

I am trying to create a MATLAB class with a member variable that's being updated as a result of a method invocation, but when I try to change the property within the class it (apperently, from what I