【问题标题】:Generate all possible numpad/keypad sequences生成所有可能的小键盘/键盘序列
【发布时间】:2011-05-14 02:45:52
【问题描述】:

我正在尝试生成所有可能的键盘序列(现在只有 7 位长度)。例如,如果移动键盘如下所示:

1 2 3
4 5 6
7 8 9
  0

一些可能的序列可以是:

123698
147896
125698
789632

要求数字的每个数字都应该是前一个数字的邻居。

这是我计划开始的方式:

关于邻居的信息从一个小键盘到另一个小键盘变化,所以我们必须像这样对其进行硬编码:

neighbors = {0: 8, 1: [2,4], 2: [1,3,5], 3: [2,6], 4: [1,5,7], 5: [2,4,6,8], 6: [3,5,9], 7: [4,8], 8: [7,5,9,0], 9: [6,8]}

我将遍历所有数字,并将可能的邻居之一附加到它,直到达到所需的长度。

编辑:更新邻居,不允许对角线 编辑 2:数字可以重复使用

【问题讨论】:

  • 那么,您的问题是什么?你似乎理解了这个问题,并且你已经开始着手解决了。你有什么需要帮助的?作为一个可能的提示,请注意可以通过计算所有 6 位序列然后将所有可能的下一步移动添加到每个序列来找到所有 7 位序列。通过计算所有的5位序列可以找到所有的6位序列...
  • 你和你的邻居很矛盾。您表明 0 只有 1 个邻居,而 1 有 3 个。您是否计算对角线?如果是这样,2 应该有 5 个邻居,如果不是,1 应该只有 2 个邻居,等等。
  • 类似于stackoverflow.com/questions/2893470/…,除了邻居的定义不同。
  • 感谢 Jeff B 的更正,不包括对角线。变了!
  • 数字可以在给定的序列中多次使用,还是像 Boggle 一样,每个只能使用一次?

标签: python algorithm numbers


【解决方案1】:

试试这个。

 neighbors = {0: [8], 
             1: [2,4], 
             2: [1,4,3], 
             3: [2,6], 
             4: [1,5,7], 
             5: [2,4,6,8], 
             6: [3,5,9], 
             7: [4,8], 
             8: [7,5,9,0], 
             9: [6,8]}


def get_sequences(n):
    if not n:
        return
    stack = [(i,) for i in  range(10)]
    while stack:
        cur = stack.pop()
        if len(cur) == n:
            yield cur
        else:
            stack.extend(cur + (d, ) for d in neighbors[cur[-1]]) 

print list(get_sequences(3))

这将产生所有可能的序列。你没有提到你是否想要那些有循环的,例如(0, 8, 9, 8)所以我把它们留在里面。如果你不想要它们,那么就使用

 stack.extend(cur + (d, ) 
              for d in neighbors[cur[-1]]
              if d not in cur)

请注意,我将0 的条目设为一个包含一个元素的列表,而不仅仅是一个整数。这是为了一致性。能够索引到字典并知道您将获得一个列表是非常好的。

另请注意,这不是递归的。递归函数在适当支持它们的语言中很棒。在 Python 中,您几乎应该总是像我在这里演示的那样管理堆栈。它就像递归解决方案一样简单,并且回避函数调用开销(python 不支持尾递归)和最大递归深度问题。

【讨论】:

    【解决方案2】:
    neighbors = {0: [8], 1: [2,5,4], 2: [1,4,3], 3: [2,5,6], 4: [1,5,7], 5: [2,4,6,8], 6: [3,5,9], 7: [4,5,8], 8: [7,5,9,0], 9: [6,5,8]}
    
    def gen_neighbor_permutations(n, current_prefix, available_digit_set, removed_digits=set(), unique_digits=False):
        if n == 0:
                print current_prefix
                return
        for d in available_digit_set:
                if unique_digits:
                        gen_neighbor_permutations(n-1, current_prefix + str(d), set(neighbors[d]).difference(removed_digits), removed_digits.union(set([d])), unique_digits=True )
                else:
                        gen_neighbor_permutations(n-1, current_prefix + str(d), set(neighbors[d]).difference(removed_digits) )
    
    gen_neighbor_permutations(n=3, current_prefix='', available_digit_set=start_set)
    

    我也忍不住注意到,在您的示例中,没有一个数字被重复使用。如果你想要,那么你可以使用unique_digits = True 选项;这将禁止对已使用的数字进行递归。

    +1 多么有趣的谜题。我希望这对你有用!

    gen_neighbor_permutations(n=3, current_prefix='', available_digit_set=start_set, unique_digits = True)
    

    【讨论】:

      【解决方案3】:

      递归在这里并不是什么大问题,因为序列相对较短,除了第一个数字之外的每个数字的选择也是如此——所以似乎“只有”4790 种可能性不允许对角线。这被编写为一个迭代器,以消除创建和返回一个包含所有可能性的大型容器的需要。

      我想到,将邻居邻接信息存储在数据结构中的数据驱动方法的另一个好处(正如 OP 所建议的那样)是,除了轻松支持不同的键盘外,它还可以控制是否允许对角线或不是微不足道的。

      我曾简短地讨论过是否将其设为列表而不是字典以加快查找速度,但我意识到这样做会使适应生成数字以外的序列变得更加困难(而且可能不会显着加快速度) )。

      adjacent = {1: [2,4],   2: [1,3,4],   3: [2,6],
                  4: [1,5,7], 5: [2,4,6,8], 6: [3,5,9],
                  7: [4,8],   8: [0,5,7,9], 9: [6,8],
                              0: [8]}
      
      def adj_sequences(ndigits):
          seq = [None]*ndigits  # pre-allocate
      
          def next_level(i):
              for neighbor in adjacent[seq[i-1]]:
                  seq[i] = neighbor
                  if i == ndigits-1:  # last digit?
                      yield seq
                  else:
                      for digits in next_level(i+1):
                          yield digits
      
          for first_digit in range(10):
              seq[0] = first_digit
              for digits in next_level(1):
                  yield digits
      
      cnt = 1
      for digits in adj_sequences(7):
          print '{:d}: {!r}'.format(cnt, ''.join(map(str,digits)))
          cnt += 1
      

      【讨论】:

      • 这可能是比我的问题更好的解决方案。
      【解决方案4】:

      这是一个经典的递归算法。一些伪代码来展示这个概念:

      function(numbers) { 
        if (length(numbers)==7) { 
          print numbers; 
          return; 
        } 
        if (numbers[last]=='1') { 
          function(concat(numbers,  '2')); 
          function(concat(numbers,  '4')); 
          return; 
        } 
        if (numbers[last]==='2') { 
          function(concat(numbers,  '1')); 
          function(concat(numbers,  '3')); 
          function(concat(numbers,  '5')); 
          return; 
        } 
        ...keep going with a condition for each digit..
      } 
      

      【讨论】:

      • 这就是人们在非动态语言中实际编程的方式吗?为什么不像 OP 建议的那样将哈希表用于相邻数字的列表中?你甚至不需要一流的功能。
      • @aaron,有些人就是这样编程的。语言是否是动态的并不重要。例如,您自己的答案不使用任何动态功能。
      • 这个解决方案确实与键盘布局紧密耦合,特别是因为 OP 提到“有关邻居的信息从键盘更改为键盘” 不会 -1 因为它不是严格错误的,但它是对其他键布局不太可重用。
      • 这是伪代码。目的是以易于阅读的格式显示算法,而不是您实际实现它的方式。
      【解决方案5】:
      neighbors = {0: [8], 1: [2,5,4], 2: [1,4,3], 3: [2,5,6], 4: [1,5,7], 5: [2,4,6,8], 6: [3,5,9], 7: [4,5,8], 8: [7,5,9,0], 9: [6,5,8]}
      
      def keyNeighborsRec(x, length):
          if length == 0:
                  print x
                  return
          for i in neighbors[x%10]:
                  keyNeighborsRec(x*10+i,length-1)
      
      
      def keyNeighbors(l):
          for i in range(10):
                  keyNeighborsRec(i,length-1)
      
      keyNeighbors(7)
      

      没有邻居条件真的很容易......

      def keypadSequences(length):
          return map(lambda x: '0'*(length-len(repr(x)))+repr(x), range(10**length))
      
      keypadSequences(7)
      

      【讨论】:

      • 这只是打印所有可能的数字序列。问题中有一些关于序列中下一个数字可以是什么的规则。
      • 是的,请阅读我上面的评论。我注意到邻居的事后记。这将是没有邻居条件的答案
      【解决方案6】:
      states = [
          [8],
          [2, 4],
          [1, 3, 5],
          [2, 6],
          [1, 5, 7],
          [2, 4, 6, 8],
          [3, 5, 9],
          [4, 8],
          [5, 7, 9, 0],
          [6, 8]
      ]
      
      def traverse(distance_left, last_state):
          if not distance_left:
              yield []
          else:
              distance_left -= 1
              for s in states[last_state]:
                  for n in traverse(distance_left, s):
                      yield [s] + n
      
      def produce_all_series():
          return [t for i in range(10) for t in traverse(7, i)]
      
      from pprint import pprint
      pprint(produce_all_series())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-15
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        • 2011-08-03
        • 2020-02-17
        • 2014-01-28
        相关资源
        最近更新 更多