I\'m look for a way to find all combinations of sums with elements of a Fibonacci sequence with a given limit which equal that same value. I know that combinations()
If I understand correctly what you are trying to achieve then use the following code:
def zeckendorf(n):
seq = fib1(n)
for i in range(1, len(seq)):
for comb in itertools.combinations(seq, i):
if sum(comb) == n:
return list(comb)
return []
If you need further explanation on this code just ask :)
To find all the combinations with the desired sum, append each combination to a result list:
def combinations_with_sum(sequence, desired_sum):
results = []
for i in range(len(sequence)):
results.extend([combination for combination in combinations(sequence, i)
if sum(combination) == desired_sum])
return results
Let's say you want to find all subsets of your input with sum < max_sum
and the number of elements between min_terms
and max_terms
.
Here is a couple of ways to do it, I'm including the whole script to make it easier to test and play around with but basically you only need *LimitedSums
() functions to get the answer.
Brute-force approach is to iterate through all subsets and check the sum and the number of elements for each subset. That's effectively what SlowLimitedSums()
does - although it takes advantage of itertools.combinations()
to iterate through subsets and doesn't consider subsets with more than max_terms
elements.
Potentially more efficient approach is to only consider the subsets which sum up to less than max_sum
. If you are building subsets recursively you can simply stop recursion as soon as the sum of your current subset exceeds max_sum
, assuming all your input numbers are non-negative, or the number of elements exceeds max_terms
. This is implemented in FasterLimitedSums()
.
Note that in the worst case your result will contain all 2^len(v)
subsets - in this case there should be no significant running time difference between the two versions of *LimitedSums()
.
import itertools
import random
def SlowLimitedSums(v, max_sum, min_terms=None, max_terms=None):
min_terms = 0 if min_terms is None else min_terms
max_terms = len(v) if max_terms is None else max_terms
return sorted(set(
sum(c) for nc in range(min_terms, max_terms + 1)
for c in itertools.combinations(v, nc)
if sum(c) <= max_sum))
def FasterLimitedSums(v, max_sum, min_terms=None, max_terms=None):
l = sorted(v)
n = len(v)
min_terms = 0 if min_terms is None else min_terms
max_terms = n if max_terms is None else max_terms
result = set([])
def RecursiveSums(s, n_terms, start_pos):
if start_pos >= n or s > max_sum or n_terms > max_terms:
return
if n_terms >= min_terms:
result.add(s)
for p in range(start_pos, n):
RecursiveSums(s + v[p], n_terms + 1, p + 1)
RecursiveSums(0, 0, -1)
return sorted(result)
def main():
mass_list = [4, 1, 8]
mass = 10
print(sorted(mass_list + SlowLimitedSums(mass_list, mass, min_terms=2)))
print(sorted(mass_list + FasterLimitedSums(mass_list, mass, min_terms=2)))
if __name__ == "__main__":
main()