Recursion Formula for Integer Partitions - python

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.

Related

Triple nested while loop - Big Oh Notation - Count of Primitive Operations

I'm having some trouble figuring out the primitive count of operations for the following lines of code
def question1(n):
n = n # 1 ops
i = 0 # 1 ops
a = 0 # 1 ops
while i < n: # n ops
j = 0 # n ops
while j < n: # n * n ops
k = 0 # n * n ops
while k < 60: # n * n * 60 ops
a = i * j - i * 2 + k # n * n * 60 * 5 ops
k += 1 # n * n * 60 * 2 ops
j += 1 # n * n ops
i += 1 # n ops
# total sum of prim operations = (n * n * 483) + (3 * n) + 3
I'm not sure if
while k < 60: # n * n * 60 ops
a = i * j - i * 2 + k # n * n * 60 * 5 ops
k += 1 # n * n * 60 * 2 ops
Is it really
n * n * 60?
or should it be
n * n * n * 60
"primitive operations" is an ambiguous concept. For instance, a while statement will at some point evaluate the condition as false (which you didn't count) and then make the execution jump to the statement after the loop. One could say those are two operations (evaluation + jump).
Someone could say that k += 1 should count as 3 operations:
load the value of k into a CPU register,
add one to it
store that register's value back in k.
But if Python were compiled into a machine language that has the INC instruction (like NASM), and we deal with fixed-size integers (like 32 bit), it is only one operation.
So this concept is fuzzy, and it is quite useless to sum them up. You should not identify "primitive operations", but identify chunks of code that have a constant time complexity.
Analysis in terms of constant time complexity
First we need to decide whether to think of integer arithmetic operations to be constant in time, or whether we should take into consideration that integers (certainly in Python) can have arbitrary size, and therefore these operations do not have a constant time complexity. See also "bit complexity". I will assume here that you want to regard arithmetic operations as having a constant time complexity.
Then we can identify this chunk of code has having a constant time complexity:
k = 0
while k < 60:
a = i * j - i * 2 + k
k += 1
j += 1
Note here that executing the inner block (that has a constant complexity) 60 times, still means the total has a constant time complexity, since 60 is a constant (independent from the input).
Also the initialisation of integer variables or their incrementing all represent constant time complexity.
There are two nested loops that each iterate š¯‘› times when they get executed. So that means the (above) inner part gets executed š¯‘›Ā² times.
Hence, the overal time complexity is O(š¯‘›Ā²)
I would say it is definitely not n * n * n * 60. You might be confused about asymptotic notation, which may be influencing this question in the first place. The third while loop is executed 60 times, meaning that each operation within it is executed 60 times. This while loop runs 60 times for each of the n iterations of the second while loop, which runs n times for each of the n iterations of the first while loop, yielding n * n * 60.
Although the 60 is involved here, it is still a constant and is therefore of little significance for large values of n. The use of a triple nested loop is more of a trick question in this case, designed to show an example of why the polynomial properties of the algorithm are more important than any constants, just because as n gets large, n * n becomes much larger than 60 does.
Your calculation looks correct, though. The only thing missed is that the following blocks are actually 2 operations each :
j += 1 # n * n * 2 ops, equal to j = j + 1, an assignment AND addition
i += 1 # n * 2 ops ( i = i + 1 )

Taylor series of cos x expansion in python

I want to calculate the summation of cosx series (while keeping the x in radian). This is the code i created:
import math
def cosine(x,n):
sum = 0
for i in range(0, n+1):
sum += ((-1) ** i) * (x**(2*i)/math.factorial(2*i))
return sum
and I checked it using math.cos() .
It works just fine when I tried out small numbers:
print("Result: ", cosine(25, 1000))
print(math.cos(25))
the output:
Result: 0.991203540954667 0.9912028118634736
The number is still similar. But when I tried a bigger number, i.e 40, it just returns a whole different value.
Result: 1.2101433786727471 -0.6669380616522619
Anyone got any idea why this happens?
The error term for a Taylor expansion increases the further you are from the point expanded about (in this case, x_0 = 0). To reduce the error, exploit the periodicity and symmetry by only evaluating within the interval [0, 2 * pi]:
def cosine(x, n):
x = x % (2 * pi)
total = 0
for i in range(0, n + 1):
total += ((-1) ** i) * (x**(2*i) / math.factorial(2*i))
return total
This can be further improved to [0, pi/2]:
def cosine(x, n):
x = x % (2 * pi)
if x > pi:
x = abs(x - 2 * pi)
if x > pi / 2:
return -cosine(pi - x, n)
total = 0
for i in range(0, n + 1):
total += ((-1) ** i) * (x**(2*i) / math.factorial(2*i))
return total
Contrary to the answer you got, this Taylor series converges regardless of how large the argument is. The factorial in the terms' denominators eventually drives the terms to 0.
But before the factorial portion dominates, terms can get larger and larger in absolute value. Native floating point doesn't have enough bits of precision to keep enough information for the low-order bits to survive.
Here's a way that doesn't lose any bits of precision. It's not practical because it's slow. Trust me when I tell you that it typically takes years of experience to learn how to write practical, fast, high-quality math libraries.
def mycos(x, nbits=100):
from fractions import Fraction
x2 = - Fraction(x) ** 2
i = 0
ntries = 0
total = term = Fraction(1)
while True:
ntries += 1
term = term * x2 / ((i+1) * (i+2))
i += 2
total += term
if (total // term).bit_length() > nbits:
break
print("converged to >=", nbits, "bits in", ntries, "steps")
return total
and then your examples:
>>> mycos(25)
converged to >= 100 bits in 60 steps
Fraction(177990265631575526901628601372315751766446600474817729598222950654891626294219622069090604398951917221057277891721367319419730580721270980180746700236766890453804854224688235663001, 179569976498504495450560473003158183053487302118823494306831203428122565348395374375382001784940465248260677204774780370309486592538808596156541689164857386103160689754560975077376)
>>> float(_)
0.9912028118634736
>>> mycos(40)
converged to >= 100 bits in 82 steps
Fraction(-41233919211296161511135381283308676089648279169136751860454289528820133116589076773613997242520904406094665861260732939711116309156993591792484104028113938044669594105655847220120785239949370429999292710446188633097549, 61825710035417531603549955214086485841025011572115538227516711699374454340823156388422475359453342009385198763106309156353690402915353642606997057282914587362557451641312842461463803518046090463931513882368034080863251)
>>> float(_)
-0.6669380616522619
Things to note:
The full-precision results require lots of bits.
Rounded back to float, they identically match what you got from math.cos().
It doesn't require anywhere near 1000 steps to converge.

Efficient algorithm for getting number of partitions of integer with distinct parts (Partition function Q)

I need to create function which will take one argument int and output int which represents the number of distinct parts of input integer's partition. Namely,
input:3 -> output: 1 -> {1, 2}
input:6 -> output: 3 -> {1, 2, 3}, {2, 4}, {1, 5}
...
Since I am looking only for distinct parts, something like this is not allowed:
4 -> {1, 1, 1, 1} or {1, 1, 2}
So far I have managed to come up with some algorithms which would find every possible combination, but they are pretty slow and effective only until n=100 or so.
And since I only need number of combinations not the combinations themselves Partition Function Q should solve the problem.
Does anybody know how to implement this efficiently?
More information about the problem: OEIS, Partition Function Q
EDIT:
To avoid any confusion, the DarrylG answer also includes the trivial (single) partition, but this does not affect the quality of it in any way.
EDIT 2:
The jodag (accepted answer) does not include trivial partition.
Tested two algorithms
Simple recurrence relation
WolframMathword algorithm (based upon Georgiadis, Kediaya, Sloane)
Both implemented with Memoization using LRUCache.
Results: WolframeMathword approach orders of magnitude faster.
1. Simple recurrence relation (with Memoization)
Reference
Code
#lru_cache(maxsize=None)
def p(n, d=0):
if n:
return sum(p(n-k, n-2*k+1) for k in range(1, n-d+1))
else:
return 1
Performance
n Time (sec)
10 time elapsed: 0.0020
50 time elapsed: 0.5530
100 time elapsed: 8.7430
200 time elapsed: 168.5830
2. WolframMathword algorithm
(based upon Georgiadis, Kediaya, Sloane)
Reference
Code
# Implementation of q recurrence
# https://mathworld.wolfram.com/PartitionFunctionQ.html
class PartitionQ():
def __init__(self, MAXN):
self.MAXN = MAXN
self.j_seq = self.calc_j_seq(MAXN)
#lru_cache
def q(self, n):
" Q strict partition function "
assert n < self.MAXN
if n == 0:
return 1
sqrt_n = int(sqrt(n)) + 1
temp = sum(((-1)**(k+1))*self.q(n-k*k) for k in range(1, sqrt_n))
return 2*temp + self.s(n)
def s(self, n):
if n in self.j_seq:
return (-1)**self.j_seq[n]
else:
return 0
def calc_j_seq(self, MAX_N):
""" Used to determine if n of form j*(3*j (+/-) 1) / 2
by creating a dictionary of n, j value pairs "
result = {}
j = 0
valn = -1
while valn <= MAX_N:
jj = 3*j*j
valp, valn = (jj - j)//2, (jj+j)//2
result[valp] = j
result[valn] = j
j += 1
return result
Performance
n Time (sec)
10 time elapsed: 0.00087
50 time elapsed: 0.00059
100 time elapsed: 0.00125
200 time elapsed: 0.10933
Conclusion: This algorithm is orders of magnitude faster than the simple recurrence relationship
Algorithm
Reference
I think a straightforward and efficient way to solve this is to explicitly compute the coefficient of the generating function from the Wolfram PartitionsQ link in the original post.
This is a pretty illustrative example of how to construct generating functions and how they can be used to count solutions. To start, we recognize that the problem may be posed as follows:
Let m_1 + m_2 + ... + m_{n-1} = n where m_j = 0 or m_j = j for all j.
Q(n) is the number of solutions of the equation.
We can find Q(n) by constructing the following polynomial (i.e. the generating function)
(1 + x)(1 + x^2)(1 + x^3)...(1 + x^(n-1))
The number of solutions is the number of ways the terms combine to make x^n, i.e. the coefficient of x^n after expanding the polynomial. Therefore, we can solve the problem by simply performing the polynomial multiplication.
def Q(n):
# Represent polynomial as a list of coefficients from x^0 to x^n.
# G_0 = 1
G = [int(g_pow == 0) for g_pow in range(n + 1)]
for k in range(1, n):
# G_k = G_{k-1} * (1 + x^k)
# This is equivalent to adding G shifted to the right by k to G
# Ignore powers greater than n since we don't need them.
G = [G[g_pow] if g_pow - k < 0 else G[g_pow] + G[g_pow - k] for g_pow in range(n + 1)]
return G[n]
Timing (average of 1000 iterations)
import time
print("n Time (sec)")
for n in [10, 50, 100, 200, 300, 500, 1000]:
t0 = time.time()
for i in range(1000):
Q(n)
elapsed = time.time() - t0
print('%-5d%.08f'%(n, elapsed / 1000))
n Time (sec)
10 0.00001000
50 0.00017500
100 0.00062900
200 0.00231200
300 0.00561900
500 0.01681900
1000 0.06701700
You can memoize the recurrences in equations 8, 9, and 10 in the mathematica article you linked for a quadratic in N runtime.
def partQ(n):
result = []
def rec(part, tgt, allowed):
if tgt == 0:
result.append(sorted(part))
elif tgt > 0:
for i in allowed:
rec(part + [i], tgt - i, allowed - set(range(1, i + 1)))
rec([], n, set(range(1, n)))
return result
The work is done by the rec internal function, which takes:
part - a list of parts whose sum is always equal to or less than the target n
tgt - the remaining partial sum that needs to be added to the sum of part to get to n
allowed - a set of number still allowed to be used in the full partitioning
When tgt = 0 is passed, that meant the sum of part if n, and the part is added to the result list. If tgt is still positive, each of the allowed numbers is attempted as an extension of part, in a recursive call.

Arithmetic precision problems with large numbers

I am writing a program that handles numbers as large as 10 ** 100, everything looks good when dealing with smaller numbers but when values get big I get these kind of problems:
>>> N = 615839386751705599129552248800733595745824820450766179696019084949158872160074326065170966642688
>>> ((N + 63453534345) / sqrt(2)) == (N / sqrt(2))
>>> True
Clearly the above comparision is false, why is this happening?
Program code:
from math import *
def rec (n):
r = sqrt (2)
s = r + 2
m = int (floor (n * r))
j = int (floor (m / s))
if j <= 1:
return sum ([floor (r * i) for i in range (1, n + 1)])
assert m >= s * j and j > 1, "Error: something went wrong"
return m * (m + 1) / 2 - j * (j + 1) - rec (j)
print rec (1e100)
Edit:
I don't think my question is a duplicate of the linked question above because the decimal points in n, m and j are not important to me and I am looking for a solution to avoid this precision issue.
You canā€™t retain the precision you want while dividing by standard floating point numbers, so you should instead divide by a Fraction. The Fraction class in the fractions module lets you do exact rational arithmetic.
Of course, the square root of 2 is not rational. But if the error is less than one part in 10**100, youā€™ll get the right result.
So, how to compute an approximation to sqrt(2) as a Fraction? There are several ways to do it, but one simple way is to compute the integer square root of 2 * 10**200, which will be close to sqrt(2) * 10**100, then just make that the numerator and make 10**100 the denominator.
Hereā€™s a little routine in Python 3 for integer square root.
def isqrt(n):
lg = -1
g = (1 >> n.bit_length() // 2) + 1
while abs(lg - g) > 1:
lg = g
g = (g + n//g) // 2
while g * g > n:
g -= 1
return g
You should be able to take it from there.

Dynamically changing the recursion limit for a fibonnacci program python

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)

Categories

Resources