Okay so background: I'm solving a question which requires me to find a number 'n' such that n-9, n-3, n+3, n+9 are consecutive prime numbers and n-8, n-4, n+4, n+8 are practical numbers and then I have to add the first four n that satisfy this condition.
The problem: Whether the logic of the code is correct or incorrect is irrelevant here because my code crashes before it reaches 100 million. I can't even check the output of the code, it works fine for 1million but doesn't scale to well for larger numbers.
What i did:
I used the sieve of erath... to get the prime numbers up to 100 million which we will call M. And since practical numbers are divisible by 6 or by 4, I created another set to store those numbers and from that list i then created a set that contained the numbers that satisfy this condition: 'n-8, n-4, n+4, n+8 are practical numbers' which we will call N. Finally I iterate through each element, a, in N and check whether a - 9, a - 3, a + 3, a + 9 are part of the prime number set.
If anyone has any tips on how i can speed this up or any better algorithms it would be greatly appreciated
code
def SieveOfEratosthenes(n):
m = set()
prime = [True for i in range(n + 1)]
p = 2
while (p * p <= n):
if (prime[p] == True):
for i in range(p * 2, n + 1, p):
prime[i] = False
p += 1
prime[0]= False
prime[1]= False
for p in range(n + 1):
if prime[p]:
m.add(p)
return m
#creates set that stores multiples of 4 and 6
def ps1(n):
s = set()
for i in range(1, n+1):
if i%4 == 0 and i%6 == 0:
s.add(i)
return s
#checks whether any number satisfies n -8, n-4, n+4, n+8 must be practical and stores it in a set
def ps2(l):
q = set()
for i in l:
if ((i-8) in l) and ((i-4) in l) and ((i+4) in l) and ((i+8) in l):
q.add(i)
return q
#using the numbers stored in the prev set, i check the last condition n-9, n-3, n+3, n+9 must be in the
prime list
def TotalSieve(k, l):
q = set()
inc = 0
for i in k:
if inc != 4:
if ((i-9) in l) and ((i-3) in l) and ((i+3) in l) and ((i+9) in l):
inc = inc + 1
q.add(i)
else:
print("Found 4")
return q
# driver program
if __name__=='__main__':
n = 1000000000
m = SieveOfEratosthenes(n)
p = ps1(n)
p = ps2(p)
f = TotalSieve(p, m)
elem1 = f.pop()
elem2 = f.pop()
elem3 = f.pop()
elem4 = f.pop()
#add the first four numbers that satisfy the conditions
tot = elem1 + elem2 + elem3 + elem4
print(tot)
First, ps1 is wrong. The test should say or, not and.
Next, if n is divisible by 4, all n-8, n-4, n+4, n+8 are also divisible by 4. If n is not divisible by 4, none of them are divisible by 4, and some of them are also not divisible by 4. Which means you are only interested in n being a multiple of 4.
Finally, I know that this problem implies some serious number-theoretical homework. Brute force wouldn't do it.
I am trying to complete Project Euler question #30, I decided to verify my code against a known answer. Basically the question is this:
Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.
Here is the known answer I am trying to prove with python:
1634 = 1^4 + 6^4 + 3^4 + 4^4
8208 = 8^4 + 2^4 + 0^4 + 8^4
9474 = 9^4 + 4^4 + 7^4 + 4^4
As 1 = 1^4 is not a sum it is not included.
The sum of these numbers is 1634 + 8208 + 9474 = 19316.
When I run my code I get all three of the values which add up to 19316, great! However among these values there is an incorrect one: 6688
Here is my code:
i=1
answer = []
while True:
list = []
i=i+1
digits = [int(x) for x in str(i)]
for x in digits:
a = x**4
list.append(a)
if sum(list) == i:
print(sum(list))
answer.append(sum(list))
The sum of list returns the three correct values, and the value 6688. Can anybody spot something I have missed?
You are checking the sum too early. You check for a matching sum for each individual digit in the number, and 6 ^ 4 + 6 ^ 4 + 8 ^ 4 is 6688. That's three of the digits, not all four.
Move your sum() test out of your for loop:
for x in digits:
a = x**4
list.append(a)
if sum(list) == i:
print(sum(list))
answer.append(sum(list))
At best you could discard a number early when the sum already exceeds the target:
digitsum = 0
for d in digits:
digitsum += d ** 4
if digitsum > i:
break
else:
if digitsum == i:
answer.append(i)
but I'd not bother with that here, and just use a generator expression to combine determining the digits, raising them to the 4th power, and summing:
if sum(int(d) ** 4 for d in str(i)) == i:
answer.append(i)
You haven't defined an upper bound, the point where numbers will always be bigger than the sum of their digits and you need to stop incrementing i. For the sum of nth powers, you can find such a point by taking 9 ^ n, counting its digits, then taking the number of digits in the nth power of 9 times the nth power of 9. If this creates a number with more digits, continue on until the number of digits no longer changes.
In the same vein, you can start i at max(10, 1 + 2 ** n), because the smallest sum you'll be able to make from digits will be using a single 2 digit plus the minimum number of 1 and 0 digits you can get away with, and at any power greater than 1, the power of digits other than 1 and 0 is always greater than the digit value itself, and you can't use i = 1:
def determine_bounds(n):
"""Given a power n > 1, return the lower and upper bounds in which to search"""
nine_power, digit_count = 9 ** n, 1
while True:
upper = digit_count * nine_power
new_count = len(str(upper))
if new_count == digit_count:
return max(10, 2 ** n), upper
digit_count = new_count
If you combine the above function with range(*<expression>) variable-length parameter passing to range(), you can use a for loop:
for i in range(*determine_bounds(4)):
# ...
You can put determining if a number is equal to the sum of its digits raised to a given power n in a function:
def is_digit_power_sum(i, n):
return sum(int(d) ** n for d in str(i)) == i
then you can put everything into a list comprehension:
>>> n = 4
>>> [i for i in range(*determine_bounds(n)) if is_digit_power_sum(i, n)]
[1634, 8208, 9474]
>>> n = 5
>>> [i for i in range(*determine_bounds(n)) if is_digit_power_sum(i, n)]
[4150, 4151, 54748, 92727, 93084, 194979]
The is_digit_power_sum() could benefit from a cache of powers; adding a cache makes the function more than twice as fast for 4-digit inputs:
def is_digit_power_sum(i, n, _cache={}):
try:
powers = _cache[n]
except KeyError:
powers = _cache[n] = {str(d): d ** n for d in range(10)}
return sum(powers[d] for d in str(i)) == i
and of course, the solution to the question is the sum of the numbers:
n = 5
answer = sum(i for i in range(*determine_bounds(n)) if is_digit_power_sum(i, n))
print(answer)
which produces the required output in under half a second on my 2.9 GHz Intel Core i7 MacBook Pro, using Python 3.8.0a3.
Here Fixed:
i=1
answer = []
while True:
list = []
i=i+1
digits = [int(x) for x in str(i)]
for x in digits:
a = x**4
list.append(a)
if sum(list) == i and len(list) == 4:
print(sum(list))
answer.append(sum(list))
The bug I found:
6^4+6^4+8^4 = 6688
So I just put a check for len of list.
Suppose we have a list of numbers, l. I need to COUNT all tuples of length 3 from l, (l_i,l_j,l_k) such that l_i evenly divides l_j, and l_j evenly divides l_k. With the stipulation that the indices i,j,k have the relationship i<j<k
I.e.;
If l=[1,2,3,4,5,6], then the tuples would be [1,2,6], [1,3,6],[1,2,4], so the COUNT would be 3.
If l=[1,1,1], then the only tuple would be [1,1,1], so the COUNT would be 1.
Here's what I've done so far, using list comprehensions:
def myCOUNT(l):
newlist=[[x,y,z] for x in l for y in l for z in l if (z%y==0 and y%x==0 and l.index(x)<l.index(y) and l.index(y)<l.index(z))]
return len(newlist)
>>>l=[1,2,3,4,5,6]
>>>myCOUNT(l)
3
This works, but as l gets longer (and it can be as large as 2000 elements long), the time it takes increases too much. Is there a faster/better way to do this?
We can count the number of triples with a given number in the middle by counting how many factors of that number are to its left, counting how many multiples of that number are to its right, and multiplying. Doing this for any given middle element is O(n) for a length-n list, and doing it for all n possible middle elements is O(n^2).
def num_triples(l):
total = 0
for mid_i, mid in enumerate(l):
num_left = sum(1 for x in l[:mid_i] if mid % x == 0)
num_right = sum(1 for x in l[mid_i+1:] if x % mid == 0)
total += num_left * num_right
return total
Incidentally, the code in your question doesn't actually work. It's fallen into the common newbie trap of calling index instead of using enumerate to get iteration indices. More than just being inefficient, this is actually wrong when the input has duplicate elements, causing your myCOUNT to return 0 instead of 1 on the [1, 1, 1] example input.
Finding all tuples in O(n2)
You algorithm iterates over all possible combinations, which makes it O(n3).
Instead, you should precompute the division-tree of your list of numbers and recover triples from the paths down the tree.
Division tree
A division tree is a graph which nodes are numbers and children are the multiples of each number.
By example, given the list [1, 2, 3, 4], the division tree looks like this.
1
/|\
2 | 3
\|
4
Computing the division tree requires to compare each number against all others, making its creation O(n2).
Here is a basic implementation of a division-tree that can be used for your problem.
class DivisionTree:
def __init__(self, values):
values = sorted(values)
# For a division-tree to be connected, we need 1 to be its head
# Thus we artificially add it and note whether it was actually in our numbers
if 1 in values:
self._has_one = True
values = values[1:]
else:
self._has_one = False
self._graph = {1: []}
for v in values:
self.insert(v)
def __iter__(self):
"""Iterate over all values of the division-tree"""
yield from self._graph
def insert(self, n):
"""Insert value in division tree, adding it as child of each divisor"""
self._graph[n] = []
for x in self:
if n != x and n % x == 0:
self._graph[x].append(n)
def paths(self, depth, _from=1):
"""Return a generator of all paths of *depth* down the division-tree"""
if _from == 1:
for x in self._graph[_from]:
yield from self.paths(depth , _from=x)
if depth == 1:
yield [_from]
return
if _from != 1 or self._has_one:
for x in self._graph[_from]:
for p in self.paths(depth - 1, _from=x):
yield [_from, *p]
Usage
Once we built a DivisionTree, it suffices to iterate over all paths down the graph and select only those which have length 3.
Example
l = [1,2,3,4,5,6]
dt = DivisionTree(l)
for p in dt.paths(3):
print(p)
Output
[1, 2, 4]
[1, 2, 6]
[1, 3, 6]
This solution assumes that the list of number is initially sorted, as in your example. Although, the output could be filtered with regard to the condition on indices i < j < k to provide a more general solution.
Time complexity
Generating the division-tree is O(n2).
In turn, there can be up to n! different paths, although stopping the iteration whenever we go deeper than 3 prevents traversing them all. This makes us iterate over the following paths:
the paths corresponding to three tuples, say there are m of them;
the paths corresponding to two tuples, there are O(n2) of them;
the paths corresponding to one tuples, there are O(n) of them.
Thus this overall yields an algorithm O(n2 + m).
I suppose this solution without list comprehension will be faster (you can see analogue with list comprehension further):
a = [1, 2, 3, 4, 5, 6]
def count(a):
result = 0
length = len(a)
for i in range(length):
for j in range(i + 1, length):
for k in range(j + 1, length):
if a[j] % a[i] == 0 and a[k] % a[j] == 0:
result += 1
return result
print(count(a))
Output:
3
In your solution index method is too expensive (requires O(n) operations). Also you don't need to iterate over full list for each x, y and z (x = a[i], y = a[j], z = a[k]). Notice how I use indexes in my loops for y and z because I know that a.index(x) < a.index(y) < a.index(z) is always satisfied.
You can write it as one liner too:
def count(a):
length = len(a)
return sum(1 for i in range(length)
for j in range(i + 1, length)
for k in range(j + 1, length)
if a[j] % a[i] == 0 and a[k] % a[j] == 0)
P.S.
Please, don't use l letter for variables names because it's very similar to 1:)
There is a way to do this with itertools combinations:
from itertools import combinations
l=[1,2,3,4,5,6]
>>> [(x,y,z) for x,y,z in combinations(l,3) if z%y==0 and y%x==0]
[(1, 2, 4), (1, 2, 6), (1, 3, 6)]
Since combinations generates the tuples in list order, you do not then need to check the index of z.
Then your myCOUNT function becomes:
def cnt(li):
return sum(1 for x,y,z in combinations(li,3) if z%y==0 and y%x==0)
>>> cnt([1,1,1])
1
>>> cnt([1,2,3,4,5,6])
3
This is a known problem.
Here are some timing for the solutions here:
from itertools import combinations
class DivisionTree:
def __init__(self, values):
# For a division-tree to be connected, we need 1 to be its head
# Thus we artificially add it and note whether it was actually in our numbers
if 1 in values:
self._has_one = True
values = values[1:]
else:
self._has_one = False
self._graph = {1: []}
for v in values:
self.insert(v)
def __iter__(self):
"""Iterate over all values of the division-tree"""
yield from self._graph
def insert(self, n):
"""Insert value in division tree, adding it as child of each divisor"""
self._graph[n] = []
for x in self:
if n != x and n % x == 0:
self._graph[x].append(n)
def paths(self, depth, _from=1):
"""Return a generator of all paths of *depth* down the division-tree"""
if _from == 1:
for x in self._graph[_from]:
yield from self.paths(depth , _from=x)
if depth == 1:
yield [_from]
return
if _from != 1 or self._has_one:
for x in self._graph[_from]:
for p in self.paths(depth - 1, _from=x):
yield [_from, *p]
def f1(li):
return sum(1 for x,y,z in combinations(li,3) if z%y==0 and y%x==0)
def f2(l):
newlist=[[x,y,z] for x in l for y in l for z in l if (z%y==0 and y%x==0 and l.index(x)<l.index(y) and l.index(y)<l.index(z))]
return len(newlist)
def f3(a):
result = 0
length = len(a)
for i in range(length):
for j in range(i + 1, length):
for k in range(j + 1, length):
if a[j] % a[i] == 0 and a[k] % a[j] == 0:
result += 1
return result
def f4(l):
dt = DivisionTree(l)
return sum(1 for _ in dt.paths(3))
def f5(l):
total = 0
for mid_i, mid in enumerate(l):
num_left = sum(1 for x in l[:mid_i] if mid % x == 0)
num_right = sum(1 for x in l[mid_i+1:] if x % mid == 0)
total += num_left * num_right
return total
if __name__=='__main__':
import timeit
tl=list(range(3,155))
funcs=(f1,f2,f3,f4,f5)
td={f.__name__:f(tl) for f in funcs}
print(td)
for case, x in (('small',50),('medium',500),('large',5000)):
li=list(range(2,x))
print('{}: {} elements'.format(case,x))
for f in funcs:
print(" {:^10s}{:.4f} secs".format(f.__name__, timeit.timeit("f(li)", setup="from __main__ import f, li", number=1)))
And the results:
{'f1': 463, 'f2': 463, 'f3': 463, 'f4': 463, 'f5': 463}
small: 50 elements
f1 0.0010 secs
f2 0.0056 secs
f3 0.0018 secs
f4 0.0003 secs
f5 0.0002 secs
medium: 500 elements
f1 1.1702 secs
f2 5.3396 secs
f3 1.8519 secs
f4 0.0156 secs
f5 0.0110 secs
large: 5000 elements
f1 1527.4956 secs
f2 6245.9930 secs
f3 2074.2257 secs
f4 1.3492 secs
f5 1.2993 secs
You can see that f1,f2,f3 are clearly O(n^3) or worse and f4,f5 are O(n^2). f2 took more than 90 minutes for what f4 and f5 did in 1.3 seconds.
Solution in O(M*log(M)) for a sorted list containing positive numbers
As user2357112 has answered, we can count the number of triplets in O(n^2) by calculating for every number the number of its factors and multiples. However, if instead of comparing every pair we go over its multiples smaller than the largest number and check whether they are in the list, we can change the efficiency to O(N+M*log(N)), when M is the largest number in the list.
Code:
def countTriples(myList):
counts = {} #Contains the number of appearances of every number.
factors = {} #Contains the number of factors of every number.
multiples = {} #Contains the number of multiples of every number.
for i in myList: #Initializing the dictionaries.
counts[i] = 0
factors[i] = 0
multiples[i] = 0
maxNum = max(myList) #The maximum number in the list.
#First, we count the number of appearances of every number.
for i in myList:
counts[i] += 1
#Then, for every number in the list, we check whether its multiples are in the list.
for i in counts:
for j in range(2*i, maxNum+1, i):
if(counts.has_key(j)):
factors[j] += counts[i]
multiples[i] += counts[j]
#Finally, we count the number of triples.
ans = 0
for i in counts:
ans += counts[i]*factors[i]*multiples[i] #Counting triplets with three numbers.
ans += counts[i]*(counts[i]-1)*factors[i]/2 #Counting triplets with two larger and one smaller number.
ans += counts[i]*(counts[i]-1)*multiples[i]/2 #Counting triplets with two smaller numbers and one larger number.
ans += counts[i]*(counts[i]-1)*(counts[i]-2)/6 #Counting triplets with three copies of the same number.
return ans
While this solution will work quickly for lists containing many small numbers, it will not work for lists containing large numbers:
countTriples(list(range(1,1000000)) #Took 80 seconds on my computer
countTriples([1,2,1000000000000]) #Will take a very long time
Fast solution with unknown efficiency for unsorted lists
Another method to count the number of multiples and factors of every number in the list would be to use a binary tree data structure, with leaves corresponding to numbers. The data structure supports three operations:
1) Add a number to every position which is a multiple of a number.
2) Add a number to every position which is specified in a set.
3) Get the value of a position.
We use lazy propagation, and propagate the updates from the root to lower nodes only during queries.
To find the number of factors of every item in the list, we iterate over the list, query the number of factors of the current item from the data structure, and add 1 to every position which is a multiple of the item.
To find the number of multiples of every item, we first find for every item in the list all its factors using the algorithm described in the previous solution.
We then iterate over the list in the reverse order. For every item, we query the number of its multiples from the data structure, and add 1 to its factors in the data structure.
Finally, for every item, we add the multiplication of its factors and multiples to the answer.
Code:
'''A tree that supports two operations:
addOrder(num) - If given a number, adds 1 to all the values which are multiples of the given number. If given a tuple, adds 1 to all the values in the tuple.
getValue(num) - returns the value of the number.
Uses lazy evaluation to speed up the algorithm.
'''
class fen:
'''Initiates the tree from either a list, or a segment of the list designated by s and e'''
def __init__(this, l, s = 0, e = -1):
if(e == -1): e = len(l)-1
this.x1 = l[s]
this.x2 = l[e]
this.val = 0
this.orders = {}
if(s != e):
this.s1 = fen(l, s, (s+e)/2)
this.s2 = fen(l, (s+e)/2+1, e)
else:
this.s1 = None
this.s2 = None
'''Testing if a multiple of the number appears in the range of this node.'''
def _numGood(this, num):
if(this.x2-this.x1+1 >= num): return True
m1 = this.x1%num
m2 = this.x2%num
return m1 == 0 or m1 > m2
'''Testing if a member of the group appears in the range of this node.'''
def _groupGood(this, group):
low = 0
high = len(group)
if(this.x1 <= group[0] <= this.x2): return True
while(low != high-1):
mid = (low+high)/2;
if(group[mid] < this.x1): low = mid
elif(group[mid] > this.x2): high = mid
else: return True
return False
def _isGood(this, val):
if(type(val) == tuple):
return this._groupGood(val)
return this._numGood(val)
'''Adds an order to this node.'''
def addOrder(this, num, count = 1):
if(not this._isGood(num)): return
if(this.x1 == this.x2): this.val += count
else :this.orders[num] = this.orders.get(num, 0)+count
'''Pushes the orders to lower nodes.'''
def _pushOrders(this):
if(this.x1 == this.x2): return
for i in this.orders:
this.s1.addOrder(i, this.orders[i])
this.s2.addOrder(i, this.orders[i])
this.orders = {}
def getValue(this, num):
this._pushOrders()
if(num < this.x1 or num > this.x2):
return 0
if(this.x1 == this.x2):
return this.val
return this.s1.getValue(num)+this.s2.getValue(num)
def countTriples2(myList):
factors = [0 for i in myList]
multiples = [0 for i in myList]
numSet = set((abs(i) for i in myList))
sortedList = sorted(list(numSet))
#Calculating factors.
tree = fen(sortedList)
for i in range(len(myList)):
factors[i] = tree.getValue(abs(myList[i]))
tree.addOrder(abs(myList[i]))
#Calculating the divisors of every number in the group.
mxNum = max(numSet)
divisors = {i:[] for i in numSet}
for i in sortedList:
for j in range(i, mxNum+1, i):
if(j in numSet):
divisors[j].append(i)
divisors = {i:tuple(divisors[i]) for i in divisors}
#Calculating the number of multiples to the right of every number.
tree = fen(sortedList)
for i in range(len(myList)-1, -1, -1):
multiples[i] = tree.getValue(abs(myList[i]))
tree.addOrder(divisors[abs(myList[i])])
ans = 0
for i in range(len(myList)):
ans += factors[i]*multiples[i]
return ans
This solution worked for a list containing the numbers 1..10000 in six seconds on my computer, and for a list containing the numbers 1..100000 in 87 seconds.
Imagine you're trying to allocate some fixed resources (e.g. n=10) over some number of territories (e.g. t=5). I am trying to find out efficiently how to get all the combinations where the sum is n or below.
E.g. 10,0,0,0,0 is good, as well as 0,0,5,5,0 etc., while 3,3,3,3,3,3 is obviously wrong.
I got this far:
import itertools
t = 5
n = 10
r = [range(n+1)] * t
for x in itertools.product(*r):
if sum(x) <= n:
print x
This brute force approach is incredibly slow though; there must be a better way?
Timings (1000 iterations):
Default (itertools.product) --- time: 40.90 s
falsetru recursion --- time: 3.63 s
Aaron Williams Algorithm (impl, Tony) --- time: 0.37 s
Possible approach follows. Definitely would use with caution (hardly tested at all, but the results on n=10 and t=5 look reasonable).
The approach involves no recursion. The algorithm to generate partitions of a number n (10 in your example) having m elements (5 in your example) comes from Knuth's 4th volume. Each partition is then zero-extended if necessary, and all the distinct permutations are generated using an algorithm from Aaron Williams which I have seen referred to elsewhere. Both algorithms had to be translated to Python, and that increases the chance that errors have crept in. The Williams algorithm wanted a linked list, which I had to fake with a 2D array to avoid writing a linked-list class.
There goes an afternoon!
Code (note your n is my maxn and your t is my p):
import itertools
def visit(a, m):
""" Utility function to add partition to the list"""
x.append(a[1:m+1])
def parts(a, n, m):
""" Knuth Algorithm H, Combinatorial Algorithms, Pre-Fascicle 3B
Finds all partitions of n having exactly m elements.
An upper bound on running time is (3 x number of
partitions found) + m. Not recursive!
"""
while (1):
visit(a, m)
while a[2] < a[1]-1:
a[1] -= 1
a[2] += 1
visit(a, m)
j=3
s = a[1]+a[2]-1
while a[j] >= a[1]-1:
s += a[j]
j += 1
if j > m:
break
x = a[j] + 1
a[j] = x
j -= 1
while j>1:
a[j] = x
s -= x
j -= 1
a[1] = s
def distinct_perms(partition):
""" Aaron Williams Algorithm 1, "Loopless Generation of Multiset
Permutations by Prefix Shifts". Finds all distinct permutations
of a list with repeated items. I don't follow the paper all that
well, but it _possibly_ has a running time which is proportional
to the number of permutations (with 3 shift operations for each
permutation on average). Not recursive!
"""
perms = []
val = 0
nxt = 1
l1 = [[partition[i],i+1] for i in range(len(partition))]
l1[-1][nxt] = None
#print(l1)
head = 0
i = len(l1)-2
afteri = i+1
tmp = []
tmp += [l1[head][val]]
c = head
while l1[c][nxt] != None:
tmp += [l1[l1[c][nxt]][val]]
c = l1[c][nxt]
perms.extend([tmp])
while (l1[afteri][nxt] != None) or (l1[afteri][val] < l1[head][val]):
if (l1[afteri][nxt] != None) and (l1[i][val]>=l1[l1[afteri][nxt]][val]):
beforek = afteri
else:
beforek = i
k = l1[beforek][nxt]
l1[beforek][nxt] = l1[k][nxt]
l1[k][nxt] = head
if l1[k][val] < l1[head][val]:
i = k
afteri = l1[i][nxt]
head = k
tmp = []
tmp += [l1[head][val]]
c = head
while l1[c][nxt] != None:
tmp += [l1[l1[c][nxt]][val]]
c = l1[c][nxt]
perms.extend([tmp])
return perms
maxn = 10 # max integer to find partitions of
p = 5 # max number of items in each partition
# Find all partitions of length p or less adding up
# to maxn or less
# Special cases (Knuth's algorithm requires n and m >= 2)
x = [[i] for i in range(maxn+1)]
# Main cases: runs parts fn (maxn^2+maxn)/2 times
for i in range(2, maxn+1):
for j in range(2, min(p+1, i+1)):
m = j
n = i
a = [0, n-m+1] + [1] * (m-1) + [-1] + [0] * (n-m-1)
parts(a, n, m)
y = []
# For each partition, add zeros if necessary and then find
# distinct permutations. Runs distinct_perms function once
# for each partition.
for part in x:
if len(part) < p:
y += distinct_perms(part + [0] * (p - len(part)))
else:
y += distinct_perms(part)
print(y)
print(len(y))
Make your own recursive function which do not recurse with an element unless it's possible to make a sum <= 10.
def f(r, n, t, acc=[]):
if t == 0:
if n >= 0:
yield acc
return
for x in r:
if x > n: # <---- do not recurse if sum is larger than `n`
break
for lst in f(r, n-x, t-1, acc + [x]):
yield lst
t = 5
n = 10
for xs in f(range(n+1), n, 5):
print xs
You can create all the permutations with itertools, and parse the results with numpy.
>>> import numpy as np
>>> from itertools import product
>>> t = 5
>>> n = 10
>>> r = range(n+1)
# Create the product numpy array
>>> prod = np.fromiter(product(r, repeat=t), np.dtype('u1,' * t))
>>> prod = prod.view('u1').reshape(-1, t)
# Extract only permutations that satisfy a condition
>>> prod[prod.sum(axis=1) < n]
Timeit:
>>> %%timeit
prod = np.fromiter(product(r, repeat=t), np.dtype('u1,' * t))
prod = prod.view('u1').reshape(-1, t)
prod[prod.sum(axis=1) < n]
10 loops, best of 3: 41.6 ms per loop
You could even speed up the product computation by populating combinations directly in numpy.
You could optimize the algorithm using Dynamic Programming.
Basically, have an array a, where a[i][j] means "Can I get a sum of j with the elements up to the j-th element (and using the jth element, assuming you have your elements in an array t (not the number you mentioned)).
Then you can fill the array doing
a[0][t[0]] = True
for i in range(1, len(t)):
a[i][t[i]] = True
for j in range(t[i]+1, n+1):
for k in range(0, i):
if a[k][j-t[i]]:
a[i][j] = True
Then, using this info, you could backtrack the solution :)
def backtrack(j = len(t)-1, goal = n):
print j, goal
all_solutions = []
if j == -1:
return []
if goal == t[j]:
all_solutions.append([j])
for i in range(j-1, -1, -1):
if a[i][goal-t[j]]:
r = backtrack(i, goal - t[j])
for l in r:
print l
l.append(j)
all_solutions.append(l)
all_solutions.extend(backtrack(j-1, goal))
return all_solutions
backtrack() # is the answer