Related to another answer of mine on Project Euler 35, I found the need to calculate all combinations of certain digits, i.e. 1, 3, 7 and 9, below a given n
. I looked around, and didn't find any really good premade solutions, but found some bits and pieces related to Cartesian products, and collated my findings into the following code:
import itertools
def all_digit_combinations(allowed_digits, maximum):
"""Yield all combinations of allowed_digits below maximum.
For allowed_digits being a list of single digits, i.e. [1, 3, 7, 9],
combine all variants of these digits until we pass the maximum value.
"""
no_of_digits = 1
while True:
for digit_tuple in itertools.product(allowed_digits, repeat=no_of_digits):
new_number = reduce(lambda rst, d: rst * 10 + d, digit_tuple)
if new_number < maximum:
yield new_number
else:
raise StopIteration
no_of_digits += 1
if __name__ == '__main__':
print ', '.join(map(str, all_digit_combinations([1, 3, 7, 9], 100)))
print ', '.join(map(str, all_digit_combinations([3, 5], 1000)))
Which indeed prints the expected output of:
1, 3, 7, 9, 11, 13, 17, 19, 31, 33, 37, 39, 71, 73, 77, 79, 91, 93, 97, 99
3, 5, 33, 35, 53, 55, 333, 335, 353, 355, 533, 535, 553, 555
I first tried using itertools.combinations_with_replacement()
and other variations from itertools, but some of those versions failed to include numbers like 31 and 73. But it I could very well simply not use the correct parameters.
Can you review this code, suggesting any optimalisations or improvements?