Reputation: 356
Take this code for example
#Find all combinations of five positive integers whose reciprocals sum to 1 under a limit
limit = 30
none = True
reciprocals5 = []
for x in range(1,limit):
for y in range(1,limit):
for z in range(1,limit):
for a in range(1,limit):
for b in range(1,limit):
if(x!=y and x!=z and x!=a and x!=b and y!=z and y!=a and y!=a and y!=b and z!=a and z!=b and a!=b):
if(1/x + 1/y + 1/z + 1/a + 1/b == 1):
reciprocals5.append([x,y,z,a,b])
none = False
If I had to find all 6 positive integer combinations, I would have to add another loop and so on. Lets say I want to find all N positive integer combinations. Since I cannot create N nested for loops, what is the alternative?
Upvotes: 1
Views: 1266
Reputation: 8898
Since I cannot create N nested for loops, what is the alternative?
Recursion!
Have a function taking extra arguments, such as the number of terms to sum and the target number, then in its implementation call itself again with one fewer term.
Your stopping condition is when the number of terms to sum is zero. In that case, if the target number to reach is zero, it means you found a valid sum. If it's non-zero, it means you didn't. (Similarly, you could do the last check at 1 term left, checking whether you can pick a final number to match it.)
Since you only need to find sets of distinct numbers that sum up to one, you can assume x > y > z > a > b (or the opposite ordering), to make sure you're not finding the same sequence over and over again, just in a different order.
Also, iterating down from the limit means the reciprocals will grow as you proceed in the iteration. Which also means you can stop looking once the sum goes past one (or the target gets negative), which should help you quickly prune loops that won't ever yield new values.
Finally, Python also supports fractions, which means you can make these calculations with exact precision, without worrying about the rounding issues of floats.
Putting it all together:
from fractions import Fraction
def reciprocal_sums(n=5, limit=30, target=1, partial=()):
if n == 0:
if target == 0:
yield partial
return
for i in range(limit, 0, -1):
new_target = target - Fraction(1, i)
if new_target < 0:
return
yield from reciprocal_sums(
n - 1, i - 1, new_target, partial + (i,))
Testing it for n=5 (default):
>>> list(reciprocal_sums())
[(30, 20, 12, 3, 2),
(30, 20, 6, 4, 2),
(30, 10, 6, 5, 2),
(28, 21, 12, 3, 2),
(28, 21, 6, 4, 2),
(28, 14, 7, 4, 2),
(24, 12, 8, 4, 2),
(20, 12, 6, 5, 2),
(20, 6, 5, 4, 3),
(18, 12, 9, 4, 2),
(15, 12, 10, 4, 2)]
For n=4:
>>> list(reciprocal_sums(4))
[(24, 8, 3, 2),
(20, 5, 4, 2),
(18, 9, 3, 2),
(15, 10, 3, 2),
(12, 6, 4, 2)]
And n=6:
>>> list(reciprocal_sums(6))
[(30, 28, 21, 20, 3, 2),
(30, 24, 20, 8, 4, 2),
(30, 24, 10, 8, 5, 2),
(30, 20, 18, 9, 4, 2),
(30, 20, 15, 10, 4, 2),
(30, 18, 10, 9, 5, 2),
(30, 12, 10, 5, 4, 3),
(28, 24, 21, 8, 4, 2),
(28, 21, 20, 6, 5, 2),
(28, 21, 18, 9, 4, 2),
(28, 21, 15, 10, 4, 2),
(28, 20, 14, 7, 5, 2),
(28, 14, 12, 7, 6, 2),
(28, 14, 7, 6, 4, 3),
(24, 20, 12, 8, 5, 2),
(24, 20, 8, 5, 4, 3),
(24, 18, 9, 8, 6, 2),
(24, 15, 10, 8, 6, 2),
(24, 12, 8, 6, 4, 3),
(20, 18, 12, 9, 5, 2),
(20, 18, 9, 5, 4, 3),
(20, 15, 12, 10, 5, 2),
(20, 15, 10, 5, 4, 3),
(18, 15, 10, 9, 6, 2),
(18, 12, 9, 6, 4, 3),
(15, 12, 10, 6, 4, 3)]
This solution is pretty fast. Running on a Snapdragon 845 ARM CPU:
%timeit list(reciprocal_sums(4))
365 ms ± 5.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit list(reciprocal_sums(5))
1.94 s ± 8.93 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit list(reciprocal_sums(6))
8.26 s ± 56.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
The ordering (lowering the limit at each level) together with pruning the last levels after going above the target will make this solution much faster than ones that evaluate all possible permutations or combinations.
Upvotes: 2
Reputation: 2092
Try this :
import itertools
myList = []
for combination in itertools.product(range(10), repeat=6):
myList.append(''.join(map(str, combination)))
This will create a list of 6 digit integers in str
format. You can type cast then easily.
Upvotes: 0
Reputation: 6112
You can use itertools.permutations.
import itertools
reciprocals = []
for x in itertools.permutations(range(1, limit), N):
if sum(1/i for i in x) == 1:
reciprocals.append(x)
Upvotes: 0
Reputation: 113940
itertools.product(list(range(N)),repeat=N)
maybe what you want
Upvotes: 0