Efficiently calculate factorial without trailing zeros?

I am trying to improve the calculation time of the factorial of a large number.

The first code that simply iterates and multiplies.

def calculate_factorial_multi(number):
    '''
    This function takes one agruments and
    returns the factorials of that number


    This function uses the approach successive multiplication

    like 8! = 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1
    '''

    '''
    If 0 or 1 retrun immediately
    ''' 
    if number == 1 or number == 0:
        return 1

    result = 1 # variable to hold the result

    for x in xrange(1, number + 1, 1):
        result *= x
    return result

Profiled result for this function:

When n = 1000 - Total time: 0.001115 s

for n = 10000 - Total time: 0.035327 s

for n = 100000 - Total time: 3.77454 s.

From the line profiler for n = 100000, I see that most of the% of the time was spent at the multiplication stage, which is equal to '98 .8 '

31    100000      3728380     37.3     98.8         result *= x

So, I tried to reduce the multiplication in the factorial in half, for an even number, therefore, to reduce the force.

Multiplication code of the second half:

def calculate_factorial_multi_half(number):

    if number == 1 or number == 0:
        return 1

    handle_odd = False
    upto_number = number

    if number & 1 == 1:
        upto_number -= 1
        print upto_number
        handle_odd = True

    next_sum = upto_number
    next_multi = upto_number
    factorial = 1

    while next_sum >= 2:
        factorial *= next_multi
        next_sum -= 2
        next_multi += next_sum

    if handle_odd:
        factorial *= number

    return factorial

Profiled result for this function:

When n = 1000 - Total time: 0.00115 s

for n = 10000 - Total time: 0.023636 s

for n = 100000 - Total time: 3.65019 s

Which shows some improvement in the middle range, but did not improve with scaling.

% .

61     50000      3571928     71.4     97.9         factorial *= next_multi.

, , , .

.

def calculate_factorial_multi_half_trailO(number):
    '''
    Removes the trailling zeros
    '''
    if number == 1 or number == 0:
        return 1

    handle_odd = False
    upto_number = number

    if number & 1 == 1:
        upto_number -= 1
        handle_odd = True

    next_sum = upto_number
    next_multi = upto_number
    factorial = 1
    total_shift = 0
    while next_sum >= 2:
        factorial *= next_multi
        shift = len(str(factorial)) - len(str(factorial).rstrip('0'))
        total_shift += shift
        factorial >>= shift
        next_sum -= 2
        next_multi += next_sum

    if handle_odd:
        factorial *= number

    factorial <<= total_shift
    return factorial

:

n = 1000 - : 0,061524

n = 10000 - : 113,824

, - , '96.2% '

 22       500        59173    118.3     96.2        shift = len(str(factorial)) - len(str(factorial).rstrip('0')).

, , , .

. (Linux): 64bit, Ram: 6GB

+4
2

.

, , x x/lnx.

def calculate_factorial_multi(number):
    prime = [True]*(number + 1)
    result = 1
    for i in xrange (2, number+1):
        if prime[i]:
            #update prime table
            j = i+i
            while j <= number:
                prime[j] = False
                j += i
            #calculate number of i in n!
            sum = 0
            t = i
            while t <= number:
                sum += number/t
                t *= i
            result *= i**sum
    return result

n = 10000, : 0,017

n = 100000, : 2.047s

n = 500000, : 65.324s

(PS. , n = 100000, 3,454 .)

, . 5 n!.

def calculate_factorial_multi2(number):
    prime = [True]*(number + 1)
    result = 1
    factor2 = 0
    factor5 = 0
    for i in xrange (2, number+1):
        if prime[i]:
            #update prime table
            j = i+i
            while j <= number:
                prime[j] = False
                j += i
            #calculate the number of i in factors of n!
            sum = 0
            t = i
            while t <= number:
                sum += number/t
                t *= i
            if i == 2:
                factor2 = sum
            elif i == 5:
                factor5 = sum
            else:
                result *= i**sum

    return (result << (factor2 - factor5))*(10**factor5)

n = 10000, : 0,015

n = 100000, : 1.896s

n = 500000, : 57.101s

, .

+5

, , :

, 10 ^ 4 (.). ,

  • 10000.
  • , 1 10000 . ( ).
  • , 1229 .
  • . 10000 1229. ( , .)

PS: ( python, )

0

Source: https://habr.com/ru/post/1613156/


All Articles