I recently work on some project Euler problems
Smallest multiple
Problem 5
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
I wrote my code it works great
def factor_finder(n, j=2):
factor_list = []
if n == 2:
return [2]
elif n == 3:
return [3]
else:
while n >= j * 2:
while n % j == 0:
n = int(n / j)
factor_list.append(j)
j += 1
if n > 1:
factor_list.append(n)
return factor_list
def smallest_multiples(n):
from functools import reduce
factor_list = []
final_list = []
for i in range(2, n + 1):
factor_list += factor_finder(i)
# print(factor_list)
for i in set(factor_list):
l1 = []
l2 = []
for j in factor_list:
if j == i:
l1.append(j)
else:
if len(l1) > len(l2):
l2 = l1
l1 = []
else:
l1 = []
# print(l2)
final_list += l2
# print(final_list)
return (
np.array(final_list).cumprod()[-1],
reduce((lambda x, y: x * y), final_list),
)
The result is:
%time
smallest_multiples(1000)
CPU times: user 5 µs, sys: 0 ns, total: 5 µs
Wall time: 32.4 µs
(-4008056434385126912,
7128865274665093053166384155714272920668358861885893040452001991154324087581111499476444151913871586911717817019575256512980264067621009251465871004305131072686268143200196609974862745937188343705015434452523739745298963145674982128236956232823794011068809262317708861979540791247754558049326475737829923352751796735248042463638051137034331214781746850878453485678021888075373249921995672056932029099390891687487672697950931603520000)
My question is why numpy.cumprod() failed to get the right number. I thought numpy is the very number tool. Can Somebody give me some idea?
The problem is that the number reached a size that meant that it was no longer representable by ints in Python. If you look here, you'll see that ints max out in size around 19 digits (i.e. 2^63 from 63 bits + sign bit) and then go into overflow. Numpy is based in C which uses fixed-precision for much faster computations with the trade off that it is limited by the 64bit integer and will overflow. Some functions in numpy even guard against this by converting to floats to do calculations which can hold even more digits.
If you tell numpy to use "object" as your datatype, there is a significant time penalty but it'll let you use the arbitrary-precision that you're used to in Python. For your code, it would look like:
return (
np.cumprod(final_list, dtype="object")[-1],
reduce((lambda x, y: x * y), final_list),
)
More about overflow in numpy.
Numerical analysis is not number theory. Correctness is not the only goal but has to be weighed against efficiency. Arbitrary precision numbers (like large integers) are slow, so numpy defaults to using fixed length integers. These just overflow when they become too large. You can instruct numpy to use arbitrary precision integers, but you will lose much of its speed:
np.arange(1, 100).prod() # fast but wrong
# 0
np.arange(1, 100, dtype=object).prod() # slow but correct
# 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
I have written the following code for evaluating integer partitions using the recurrence formula involving pentagonal numbers:
def part(n):
p = 0
if n == 0:
p += 1
else:
k = 1
while ((n >= (k*(3*k-1)/2)) or (n >= (k*(3*k+1)/2))):
i = (k * (3*k-1)/2)
j = (k * (3*k+1)/2)
if ((n-i) >= 0):
p -= ((-1)**k) * part(n-i)
if ((n-j) >= 0):
p -= ((-1)**k) * part(n-j)
k += 1
return p
n = int(raw_input("Enter a number: "))
m = part(n)
print m
The code works fine up until n=29. It gets a bit slow around n=24, but I still get an output within a decent runtime. I know the algorithm is correct because the numbers yielded are in accordance with known values.
For numbers above 35, I don't get an output even after waiting for a long time (about 30 minutes). I was under the impression that python can handle numbers much larger than the numbers used here. Can someone help me improve my runtime and get better results? Also, if there is something wrong with the code, please let me know.
You can use Memoization:
def memo(f):
mem = {}
def wrap(x):
if x not in mem:
mem[x] = f(x)
return mem[x]
return wrap
#memo
def part(n):
p = 0
if n == 0:
p += 1
else:
k = 1
while (n >= (k * (3 * k - 1) // 2)) or (n >= (k * (3 * k + 1) // 2)):
i = (k * (3 * k - 1) // 2)
j = (k * (3 * k + 1) // 2)
if (n - i) >= 0:
p -= ((-1) ** k) * part(n - i)
if (n - j) >= 0:
p -= ((-1) ** k) * part(n - j)
k += 1
return p
Demo:
In [9]: part(10)
Out[9]: 42
In [10]: part(20)
Out[10]: 627
In [11]: part(29)
Out[11]: 4565
In [12]: part(100)
Out[12]: 190569292
With memoization we remember previous calculation so for repeated calculations we just do a lookup in the dict.
Well there are a number of things you can do.
Remove duplicate calculations. - Basically you are calculating "3*k+1" many times for every execution of your while loop. You should calculate it once and assign it to a variable, and then use the variable.
Replace the (-1)**k with a much faster operation like something like -2*(k%2)+1). So instead of the calculation being linear with respect to k it is constant.
Cache the result of expensive deterministic calculations. "part" is a deterministic function. It gets called many times with the same arguments. You can build a hashmap of the inputs mapped to the results.
Consider refactoring it to use a loop rather than recursion. Python does not support tail recursion from what I understand, thus it is burdened with having to maintain very large stacks when you use deep recursion.
If you cache the calculations I can guarantee it will operate many times faster.
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == 1 or y == x:
print(1)
if y > x:
print(0)
else:
a = math.factorial(x)
b = math.factorial(y)
div = a // (b*(x-y))
print(div)
This binomial coefficient program works but when I input two of the same number which is supposed to equal to 1 or when y is greater than x it is supposed to equal to 0.
This question is old but as it comes up high on search results I will point out that scipy has two functions for computing the binomial coefficients:
scipy.special.binom()
scipy.special.comb()
import scipy.special
# the two give the same results
scipy.special.binom(10, 5)
# 252.0
scipy.special.comb(10, 5)
# 252.0
scipy.special.binom(300, 150)
# 9.375970277281882e+88
scipy.special.comb(300, 150)
# 9.375970277281882e+88
# ...but with `exact == True`
scipy.special.comb(10, 5, exact=True)
# 252
scipy.special.comb(300, 150, exact=True)
# 393759702772827452793193754439064084879232655700081358920472352712975170021839591675861424
Note that scipy.special.comb(exact=True) uses Python integers, and therefore it can handle arbitrarily large results!
Speed-wise, the three versions give somewhat different results:
num = 300
%timeit [[scipy.special.binom(n, k) for k in range(n + 1)] for n in range(num)]
# 52.9 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit [[scipy.special.comb(n, k) for k in range(n + 1)] for n in range(num)]
# 183 ms ± 814 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)each)
%timeit [[scipy.special.comb(n, k, exact=True) for k in range(n + 1)] for n in range(num)]
# 180 ms ± 649 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
(and for n = 300, the binomial coefficients are too large to be represented correctly using float64 numbers, as shown above).
Note that starting Python 3.8, the standard library provides the math.comb function to compute the binomial coefficient:
math.comb(n, k)
which is the number of ways to choose k items from n items without repetition n! / (k! (n - k)!):
import math
math.comb(10, 5) # 252
math.comb(10, 10) # 1
Here's a version that actually uses the correct formula . :)
#! /usr/bin/env python
''' Calculate binomial coefficient xCy = x! / (y! (x-y)!)
'''
from math import factorial as fac
def binomial(x, y):
try:
return fac(x) // fac(y) // fac(x - y)
except ValueError:
return 0
#Print Pascal's triangle to test binomial()
def pascal(m):
for x in range(m + 1):
print([binomial(x, y) for y in range(x + 1)])
def main():
#input = raw_input
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
print(binomial(x, y))
if __name__ == '__main__':
#pascal(8)
main()
...
Here's an alternate version of binomial() I wrote several years ago that doesn't use math.factorial(), which didn't exist in old versions of Python. However, it returns 1 if r is not in range(0, n+1).
def binomial(n, r):
''' Binomial coefficient, nCr, aka the "choose" function
n! / (r! * (n - r)!)
'''
p = 1
for i in range(1, min(r, n - r) + 1):
p *= n
p //= i
n -= 1
return p
So, this question comes up first if you search for "Implement binomial coefficients in Python". Only this answer in its second part contains an efficient implementation which relies on the multiplicative formula. This formula performs the bare minimum number of multiplications. The function below does not depend on any built-ins or imports:
def fcomb0(n, k):
'''
Compute the number of ways to choose $k$ elements out of a pile of $n.$
Use an iterative approach with the multiplicative formula:
$$\frac{n!}{k!(n - k)!} =
\frac{n(n - 1)\dots(n - k + 1)}{k(k-1)\dots(1)} =
\prod_{i = 1}^{k}\frac{n + 1 - i}{i}$$
Also rely on the symmetry: $C_n^k = C_n^{n - k},$ so the product can
be calculated up to $\min(k, n - k).$
:param n: the size of the pile of elements
:param k: the number of elements to take from the pile
:return: the number of ways to choose k elements out of a pile of n
'''
# When k out of sensible range, should probably throw an exception.
# For compatibility with scipy.special.{comb, binom} returns 0 instead.
if k < 0 or k > n:
return 0
if k == 0 or k == n:
return 1
total_ways = 1
for i in range(min(k, n - k)):
total_ways = total_ways * (n - i) // (i + 1)
return total_ways
Finally, if you need even larger values and do not mind trading some accuracy, Stirling's approximation is probably the way to go.
Your program will continue with the second if statement in the case of y == x, causing a ZeroDivisionError. You need to make the statements mutually exclusive; the way to do that is to use elif ("else if") instead of if:
import math
x = int(input("Enter a value for x: "))
y = int(input("Enter a value for y: "))
if y == x:
print(1)
elif y == 1: # see georg's comment
print(x)
elif y > x: # will be executed only if y != 1 and y != x
print(0)
else: # will be executed only if y != 1 and y != x and x <= y
a = math.factorial(x)
b = math.factorial(y)
c = math.factorial(x-y) # that appears to be useful to get the correct result
div = a // (b * c)
print(div)
What about this one? :) It uses correct formula, avoids math.factorial and takes less multiplication operations:
import math
import operator
product = lambda m,n: reduce(operator.mul, xrange(m, n+1), 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(y+1, x) / product(1, x-y)
Also, in order to avoid big-integer arithmetics you may use floating point numbers, convert
product(a[i])/product(b[i]) to product(a[i]/b[i]) and rewrite the above program as:
import math
import operator
product = lambda iterable: reduce(operator.mul, iterable, 1)
x = max(0, int(input("Enter a value for x: ")))
y = max(0, int(input("Enter a value for y: ")))
print product(map(operator.truediv, xrange(y+1, x+1), xrange(1, x-y+1)))
For Python 3, scipy has the function scipy.special.comb, which may produce floating point as well as exact integer results
import scipy.special
res = scipy.special.comb(x, y, exact=True)
See the documentation for scipy.special.comb.
For Python 2, the function is located in scipy.misc, and it works the same way:
import scipy.misc
res = scipy.misc.comb(x, y, exact=True)
I recommend using dynamic programming (DP) for computing binomial coefficients. In contrast to direct computation, it avoids multiplication and division of large numbers. In addition to recursive solution, it stores previously solved overlapping sub-problems in a table for fast look-up. The code below shows bottom-up (tabular) DP and top-down (memoized) DP implementations for computing binomial coefficients.
def binomial_coeffs1(n, k):
#top down DP
if (k == 0 or k == n):
return 1
if (memo[n][k] != -1):
return memo[n][k]
memo[n][k] = binomial_coeffs1(n-1, k-1) + binomial_coeffs1(n-1, k)
return memo[n][k]
def binomial_coeffs2(n, k):
#bottom up DP
for i in range(n+1):
for j in range(min(i,k)+1):
if (j == 0 or j == i):
memo[i][j] = 1
else:
memo[i][j] = memo[i-1][j-1] + memo[i-1][j]
#end if
#end for
#end for
return memo[n][k]
def print_array(memo):
for i in range(len(memo)):
print('\t'.join([str(x) for x in memo[i]]))
#main
n = 5
k = 2
print("top down DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs1(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
print("bottom up DP")
memo = [[-1 for i in range(6)] for j in range(6)]
nCk = binomial_coeffs2(n, k)
print_array(memo)
print("C(n={}, k={}) = {}".format(n,k,nCk))
Note: the size of the memo table is set to a small value (6) for display purposes, it should be increased if you are computing binomial coefficients for large n and k.
Here is a function that recursively calculates the binomial coefficients using conditional expressions
def binomial(n,k):
return 1 if k==0 else (0 if n==0 else binomial(n-1, k) + binomial(n-1, k-1))
It's a good idea to apply a recursive definition, as in Vadim Smolyakov's answer, combined with a DP (dynamic programming), but for the latter you may apply the lru_cache decorator from module functools:
import functools
#functools.lru_cache(maxsize = None)
def binom(n,k):
if k == 0: return 1
if n == k: return 1
return binom(n-1,k-1)+binom(n-1,k)
The simplest way is using the Multiplicative formula. It works for (n,n) and (n,0) as expected.
def coefficient(n,k):
c = 1.0
for i in range(1, k+1):
c *= float((n+1-i))/float(i)
return c
Multiplicative formula
A bit shortened multiplicative variant given by PM 2Ring and alisianoi. Works with python 3 and doesn't require any packages.
def comb(n, k):
# Remove the next two lines if out-of-range check is not needed
if k < 0 or k > n:
return None
x = 1
for i in range(min(k, n - k)):
x = x*(n - i)//(i + 1)
return x
Or
from functools import reduce
def comb(n, k):
return (None if k < 0 or k > n else
reduce(lambda x, i: x*(n - i)//(i + 1), range(min(k, n - k)), 1))
The division is done right after multiplication not to accumulate high numbers.
For everyone looking for the log of the binomial coefficient (Theano calls this binomln), this answer has it:
from numpy import log
from scipy.special import betaln
def binomln(n, k):
"Log of scipy.special.binom calculated entirely in the log domain"
return -betaln(1 + n - k, 1 + k) - log(n + 1)
(And if your language/library lacks betaln but has gammaln, like Go, have no fear, since betaln(a, b) is just gammaln(a) + gammaln(b) - gammaln(a + b), per MathWorld.)
import math
def binomial_coefficients(n,k):
product = 1
for i in range(k):
product = math.floor(((product * (n - i))/(i + 1))
return product
in the calculation of the binomial coefficients, you should not calculate the finite product n(n-1) ... (n - k +1) for (n, k) and k!. This could cause an overflow error. Therefore, using a bit of number theory we can assume that the inputs will always be in integer form (since the combination of (n, k) only accepts integers)) we can see that for an integer 'i' in a product of consecutive integers, any term u in the product will always be divisible by i.
NOTE: you can do this without importing the math module. math.floor(a/b) is equivalent to a // b
I am trying to write a recursive multinacci (basically fibonacci numbers except the rabbits produce k pairs instead of 1 pair with each breeding cycle) function and I want it to work with all n. Here is my code so far:
from functools import lru_cache
from sys import getrecursionlimit, setrecursionlimit
def fibnum(n, k=1):
"""Returns the nth fibonacci number of order k"""
# check if recursionlimit needs increasing
return _fibnum(n, k)
#lru_cache(maxsize=None)
def _fibnum(n, k):
if n <= 0:
return 0
if n == 1:
return 1
return _fibnum(n-1, k) + k * _fibnum(n-2, k)
A few notes about the code: the first function is a wrapper around the second so as to the description text look right. The second function is memoized, which increases performance drastically.
I've noticed that when I try to find increasing values of fibnum in order (100, 400, 1000 etc.) I can get around the recursion limit since the memoization shortcuts the recursion. I want to be able to run my function for any number right off the bat. I've tried testing the bounds of the recursion limit for n and then setting the recursion limit to that, but the only one that seemed to work was n2, but that seems too high of a limit.
Any suggestions?
Note: at a later point, I would like to add a lifespan to the formula (which is basically subtract out fibnum(n- life_span, k)). How would this affect the recursion depth needed?
One way of sidestepping the stack limitations is to set up the Fibonacci recurrence in matrix form and use the matrix version of multiplication by successive halving and squaring. With this approach the stack growth is O(log n), so you can go to gigantic values of fib(n) with no worries. Here's an implementation:
def __matrix_fib__(n):
if n == 1:
return [0, 1]
else:
f = __matrix_fib__(n / 2)
c = f[0] * f[0] + f[1] * f[1]
d = f[1] * (f[1] + 2 * f[0])
if n % 2 == 0:
return [c, d]
else:
return [d, c + d]
def fib(n):
assert (n >= 0)
if n == 0:
return n
else:
return __matrix_fib__(n)[1]
ADDENDUM
This version adds the k parameter as requested...
def __matrix_fib__(n, k):
if n == 1:
return [0, 1]
else:
f = __matrix_fib__(n / 2, k)
c = k * f[0] * f[0] + f[1] * f[1]
d = f[1] * (f[1] + 2 * k * f[0])
if n % 2 == 0:
return [c, d]
else:
return [d, k * c + d]
def fib(n, k=1):
assert (n >= 0)
if n == 0:
return n
else:
return __matrix_fib__(n, k)[1]
I won't swear this is correct because I dashed it off between classes, but my tests produced the same answers as your version when given the same inputs.
Alternatively, you could use a class as a namespace to store the cache, then calculate results iteratively:
class Fib(object):
cache = [1, 1]
#classmethod
def get(cls, n):
if n < 1:
return 0
for i in range(len(cls.cache), n):
cls.cache.append(cls.cache[-1] + cls.cache[-2])
return cls.cache[n - 1]
Usage:
a = Fib()
print a.get(1000)
If you change fibnum to limit the call stack to 100 items by computing the first 100 fibnums, then the next 100 fibnums, then the next 100 fibnums, you can avoid a recursion limit.
This produces very little wasted work since you'll need to compute the first 100 fibnums to compute the last 100 fibnums anyways.
The number 100 is arbitrary, but should be less than sys.recursionlimit.
def fibnum(n, k=1):
"""Returns the nth fibonacci number of order k"""
# build up cache of fib values early in the sequence
for intermediate_n in range(100, n, 100):
_fibnum(intermediate_n, k)
return _fibnum(n, k)
I'm trying to use Fermat's factorization method
http://en.wikipedia.org/wiki/Fermat%27s_factorization_method
to factor a RSA exercise with n = pq = 17113393402958118715148546526344227921781458985077442510282855190555424972779474416264134494113
Here's my python code:
def isSquare(x):
return pow(int(sqrt(x)),2) - x == 0
n = 17113393402958118715148546526344227921781458985077442510282855190555424972779474416264134494113
for i in xrange(10):
print isSquare(n+i*i)
When I execute, it prints all Trues, which isn't correct. I think it's truncation error in python. How should I deal with it? Thanks.
def isqrt(n):
x = n
y = (x + n // x) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
print isqrt(99999999999**2)
for i in xrange(130000,140000):
if isqrt(n + i*i) ** 2 == n + i*i:
print isqrt(n + i*i)
print "done"
math.sqrt uses floating point numbers, which are inexact.
The easiest way is probably to change sqrt to integer isqrt function, and you can just copy decent isqrt implementation from https://stackoverflow.com/a/15391420/220700
You can use Newton's method to find the integer square root of a number:
def isqrt(n):
x = n
y = (x + n // x) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
This returns the largest integer x such that x × x does not exceed n.
But it is highly unlikely that Fermat's method will be able to factor your 95-digit RSA semi-prime. You should look at the quadratic sieve or the number field sieve to factor a number of that size.
You can try to make usage of sqrt() function out of module math. The code could look like:
import math
n = math.sqrt(int(raw_input("Enter a number\n")))
print n