EDIT: It has been pointed out that the step can't be altered in the way I was hoping, a clearer way of explaining what I'm trying to do is find ways to skip numbers with duplicate digits.
I want to iterate through every possible unique combination of a number using step
, step
being derived from the format: range(start_value, end_value, step)
.
What I want essentially is for i
in for i in range(start_value, end_value, step)
to always be a number where all the characters in that number are unique.
A similar showing of what I want is in the code below. It will only print the numbers in which there are no repeats.
def printUnique(l,r):
for i in range (l, r + 1):
num = i
visited = [0,0,0,0,0,0,0,0,0,0]
while (num):
if visited[num % 10] == 1:
break
visited[num % 10] = 1
num = (int)(num / 10)
if num == 0:
print(i, end = ' ')
l = 123456789;
r = 9876543210;
printUnique(l, r);
The above code is, however, not what I am looking for. It does not utilize step
, instead, it iterates through all the numbers with a default step of one.
An example format of the code I'm looking for:
start_value = 123456789
# this is basically 0123456789 but putting zero at the beginning raises an error
end_value = 9876543210
step = 1
# step will probably be a changing variable outside of the loop, maybe not depending on how you do it
# any other potential variables that need to be stored
for i in range(start_value, end_value, step):
print(i)
# algorithm here that modifies step variable
If start_value
is 98 and end_value
is 0 (01), the output should be as follows, if the variables were stored in a list and not printed. Numbers 11, 22, 33, 44, 55, 66, 77, 88 and 99 are skipped.
savedList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98]
Since I nerd sniped myself with this question, I'm posting here my answer. It works well even for the whole domain of non-repeating digit numbers (it took about 21 s to generate all of then on my machine).
The main idea is to use a generator to create the numbers with non-repeated digits in order. Then, instead of going step by step on the original list and checking if it doesn't have repeated numbers, we do the other way round: we get the next number that has no repeated digits and see if it would be part of our original list (this is easy to do with % step
).
This is a lot more efficient as the numbers grow because the numbers without repeated digits become progressively rarer.
The code below generates the list of numbers with no repeated digits (from smaller to largest). Some drudgery was needed to avoid repeating the permutations that started with 0:
from itertools import permutations
def gen_all_unique():
return (sum(i*10**n for n, i in enumerate(reversed(per))) for j in range(1, 11) for per in permutations(range(10), j) if per[0] != 0 or j == 1)
Then, this functions ensures we only select the numbers that fall in the requested step (last if):
def range_non_repeat(l, r, step=1):
# Works when l <= r
for n in gen_all_unique():
if n < l:
continue
if n >= r:
break
if (n - l) % step == 0:
yield n
This work really well if l <= r
. To have something that works in any case, we have to create a special function that generates numbers with unique digits starting from the largest possible to the smallest one. We will do this by changing gen_all_unique
to return a different generator depending on the direction requested:
def gen_all_unique(direction=1):
if direction >= 0: # Generate a growing sequence
return (sum(i*10**n for n, i in enumerate(reversed(per))) for j in range(1, 11) for per in permutations(range(10), j) if per[0] != 0 or j == 1)
# Generate a diminishing sequence
return (sum(i*10**n for n, i in enumerate(reversed(per))) for j in range(10, 0, -1) for per in permutations(range(9, -1, -1), j) if per[0] != 0 or j == 1)
def range_non_repeat(l, r, step=1):
direction = step // abs(step) # Get step's sign
for n in gen_all_unique(step):
if direction * n < direction * l:
continue
if direction * n >= direction * r:
break
if (n - l) % step == 0:
yield n
I think this code is a good example of the usefulness of generators. On the other hand, if this is an operation that will have to be done several times, especially with large numbers, it might be worth generating all the numbers once and caching it. There are only 8,877,691 numbers without repeated digits, which take about 72 Mb of memory to store (assuming 64 bit ints, since 9876543210 is larger than the largest 32 bit uint). Depending on the problem, that might be a cheap price to pay for the speed.