def partition(n, buckets): if buckets == 1: yield [n] elif n >= buckets: for i in range(1, n): for rest in partition(n - i, buckets - 1): yield [i] + rest def solutions(n, buckets): return filter(all_coprime, partition(n, buckets))This gives me these for 8, 3:
from fractions import gcd
import sys
def partition(n, buckets, min=1):
if n < min:
return
if buckets == 1:
yield [n]
elif n >= buckets:
for i in range(min, n):
for rest in partition(n - i, buckets - 1, i):
yield [i] + rest
def all_coprime(values):
for i in range(len(values)):
for j in range(i+1, len(values)):
if gcd(values[i], values[j]) > 1:
return False
return True
def solutions(n, buckets):
return filter(all_coprime, partition(n, buckets))
def main():
n = int(sys.argv[1])
buckets = int(sys.argv[2])
for sol in solutions(n, buckets):
print sol
main()
I added a bit to the partition() function to only generate the increasing-order partitions (and it already generated the partitions themselves in the correct order). The coprime check is just brute-force, though, and there is certainly a faster way to do that, if needed. Only adding elements to a growing partition that are coprime with the values already included would probably be much more efficient. There are probably smarter ways to generate the partitions (avoiding lots of "dead ends") as well.
from fractions import gcd
import sys
coprimes = []
def partition(n, buckets, allowed):
global coprimes
if buckets == 1: # Have to include n at this point
if n in allowed:
yield [n]
return
for i in allowed:
# Prune remaining search if no solution is possible
if i*buckets > n:
return
# Remove values that can't be used any more if we include i
newallowed = [ x for x in allowed if ((i <= x <= n-i) and coprimes[i][x]) ]
for rest in partition(n - i, buckets - 1, newallowed):
yield [i] + rest
def main():
n = int(sys.argv[1])
buckets = int(sys.argv[2])
# Precalculate GCDs
global coprimes
for i in range(n+1):
coprimes.append([False]*(n+1))
for j in range(i,n+1):
coprimes[i][j] = (gcd(i,j) == 1)
for sol in partition(n, buckets, allowed=range(1,n+1)):
print sol
main()
This does what I described above, maintaining a list of values that are still coprime with all earlier values in order to avoid a lot of dead ends. It also removes values that cannot be used due to being less than a value chosen already (i) or more than the amount that remains (n-i). On top of that, it prunes further by stopping whenever no solution can exist because the remaining choices would have to sum to more than n. With any sort of branching, recursive algorithm, pruning is really important. Any time you can prove that a given branch will produce no solutions, you can prune it and save time.You are not logged in, either login or create an account to post comments
This seems like a recursive problem to me. That is, if function f(a, z) solves the problem, it would do something like this:
f(a, z, primes = empty list) returns list of coprime partitions as described {
for each number i between 1 and a {
if i is coprime with all numbers in primes {
partial result = f(a-I, z-1, primes.append(i))
for r in partial result {
add result r. append (i)
}
}
}
sort results list
return results list
}
Three things are left to do, but they are relatively simple:
A. How to tell if i is a coprime with numbers in a given list.
B. How to sort the results list as described.
C. How to optimize, including memorization, sorting at the end, or just better algorithms.
posted by ethidda at 12:07 AM on November 21, 2012