【问题标题】:Iterate through a Number With Only Unique Characters遍历只有唯一字符的数字
【发布时间】:2021-04-20 20:49:11
【问题描述】:

编辑:有人指出,该步骤不能以我希望的方式更改,解释我想要做的更清晰的方法是找到跳过重复数字的数字的方法。

我想使用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]

【问题讨论】:

  • 明确您的问题。您是否要跳过重复数字的数字?
  • 是的,这就是我想要做的。我会把它添加进去。
  • 那你为什么不直接给printUnique()添加一个step参数,然后传递给range()呢?

标签: python algorithm range unique


【解决方案1】:

因为我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)

然后,这个函数确保我们只选择落在请求步骤中的数字(最后一个如果):

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 &lt;= r,这项工作真的很好。为了让某些东西在任何情况下都能正常工作,我们必须创建一个特殊的函数来生成具有唯一数字的数字,从可能的最大数字开始到最小的数字。我们将通过更改 gen_all_unique 以根据请求的方向返回不同的生成器来做到这一点:

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)。根据问题的不同,这可能是为速度付出的廉价代价。

【讨论】:

    【解决方案2】:

    您似乎只想跳过具有重复数字的数字。您根本不需要更改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)
    

    如果您想返回列表而不是打印它,这是一个单行

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

    从 1 到 30 进行测试可以得到预期的结果:11 和 22 缺失。

    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]
    

    【讨论】:

      【解决方案3】:

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

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-30
        • 2010-11-17
        • 2014-12-30
        • 2015-05-22
        • 1970-01-01
        • 2015-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多