遍历只有唯一字符的数字

Iterate through a Number With Only Unique Characters

编辑:有人指出,无法按照我希望的方式更改该步骤,更清晰地解释我正在尝试做的事情的方法是找到跳过具有重复数字的数字的方法。

我想使用 step 遍历每个可能的唯一数字组合,step 派生自以下格式:range(start_value, end_value, step).

我想要的基本上是 for i in range(start_value, end_value, step) 中的 i 始终是一个数字,其中该数字中的所有字符都是唯一的。

我想要的类似显示在下面的代码中。它只会打印没有重复的数字。

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);

但是,上面的代码不是我要找的。它不使用 step,而是使用默认步长 1 遍历所有数字。

我正在寻找的代码示例格式:

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

如果start_value是98并且end_value是0(01),如果变量存储在列表中而不打印,输出应该如下。跳过数字 11、22、33、44、55、66、77、88 和 99。

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]

您似乎只想跳过具有重复数字的数字。您根本不需要更改 step!相反,只需在循环顶部检查此条件,并在需要时使用 continue 跳过循环的其余部分。

更改 step 无论如何都行不通,因为 range() 调用仅评估一次 循环开始之前,因此循环开始像 for i in range(1, 10, 2): 等同于:

temp_range_object = range(1, 10, 2)
for i in temp_range_object:
    ...

首先,让我们编写一个函数来检查数字是否重复。它通过将数字转换为字符串、遍历每个字符并跟踪它已经遇到的字符来实现。

def contains_repeated_digits(num):
    proc_digits = set()
    for digit in str(num):
        if digit in proc_digits:
            return True
        proc_digits.add(digit)
    return False

接下来,我们在printUnique函数中使用它。 要添加 step 参数,您可以简单地对 printUnique 函数执行此操作并将其传递给 range() 调用。

def printUnique(l, r, step=1):
    for i in range (l, r + 1, step):
        if contains_repeated_digits(i): continue
        # Do the rest of your program here
        print(i)

如果您想 return 列表而不是打印它,这是单行本

def printUnique(l, r, step=1):
    return [i for i in range(l, r + 1, step) if not contains_repeated_digits(i)]

从 1 到 30 测试这个给了我们预期的结果:缺少 1122

printUnique(1, 30)
[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]

这是一个基于生成器的解决方案,可在范围非常大时降低内存使用率:

import math

def has_unique_digits(num):
    # calculate the number of digits in `num`
    n_digits = int(math.log(num, 10)) + 1
    # use a set comprehension to check for duplicate digits
    digits = {(num//10**x) % 10 for x in range(n_digits)}
    return n_digits == len(digits)

def unique_digit_numbers(begin=0, end=None, step=1):
    num = begin
    while end is None or num < end:
        if has_unique_digits(num):
            yield num
        num += step


>>> list(unique_digit_numbers(98, 121))
[98, 102, 103, 104, 105, 106, 107, 108, 109, 120]
>>> for num in unique_digit_numbers(98, 121, 2):
        print(num)
98
103
105
107
109

因为我 nerd sniped 自己有这个问题,所以我在这里发布我的答案。它甚至适用于整个非重复数字域(在我的机器上生成所有这些数字大约需要 21 秒)。

主要思想是使用生成器按顺序创建具有非重复数字的数字。然后,我们不是在原始列表上一步步检查它是否没有重复数字,而是反过来:我们得到下一个没有重复数字的数字,看看它是否是我们原始数字的一部分列表(使用 % step 很容易做到)。

随着数字的增长,这种方法效率更高,因为没有重复数字的数字会越来越少。

下面的代码生成没有重复数字的数字列表(从小到大)。需要做一些苦差事来避免重复以 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)

然后,此函数确保我们仅 select 落在请求步骤中的数字(最后一个如果):

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

如果l <= r,这个工作真的很好。为了在任何情况下都能正常工作,我们必须创建一个特殊的函数来生成具有唯一数字的数字,从可能的最大数字到最小数字。我们将通过根据请求的方向将 gen_all_unique 更改为 return 不同的生成器来实现此目的:

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

我认为这段代码很好地说明了生成器的用途。另一方面,如果这是一个必须多次执行的操作,尤其是对于大数字,则可能值得一次生成所有数字并将其缓存。没有重复数字的数字只有 8,877,691 个,需要大约 72 Mb 的内存来存储(假设 64 位整数,因为 9876543210 大于最大的 32 位 uint)。根据问题的不同,这可能是为速度付出的廉价代价。