Python 生成所有可能的numpad/键盘序列

Python 生成所有可能的numpad/键盘序列,python,algorithm,numbers,Python,Algorithm,Numbers,我正在尝试生成所有可能的键盘序列(目前仅7位长度)。例如,如果移动键盘看起来像这样: 1 2 3 4 5 6 7 8 9 0 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]} 一些可能的序列可以是: 123698 147896 125698 789632 要求数字的每一位都应该是前一位的相邻数字

我正在尝试生成所有可能的键盘序列(目前仅7位长度)。例如,如果移动键盘看起来像这样:

1 2 3
4 5 6
7 8 9
  0
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]}
一些可能的序列可以是:

123698
147896
125698
789632

要求数字的每一位都应该是前一位的相邻数字

以下是我计划如何开始这项工作:

关于邻居的信息会随着键盘的变化而变化,因此我们必须对其进行如下硬编码:

1 2 3
4 5 6
7 8 9
  0
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:数字可以重复使用

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

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..
} 
这真的很容易没有邻居的条件

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

keypadSequences(7)
我也情不自禁地注意到,在您的示例中,没有一个数字被重用。如果需要,那么可以使用
unique\u digits=True
选项;这将禁止对已使用的数字进行递归

+多有趣的拼图啊。我希望这对你有用

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

 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不支持尾部递归)以及最大递归深度问题一样简单。

递归在这里并不是一个真正的问题,因为序列相对较短,除了第一个数字外,每个数字的选择也相对较短,因此似乎“只有一个”be 4790不允许使用对角线。这是作为迭代器编写的,以消除创建和返回大型容器以及其中生成的所有可能性的需要

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())
我突然想到,在数据结构中存储邻居邻接信息的数据驱动方法的另一个好处(正如OP所建议的)是,除了可以轻松支持不同的键盘外,它还使得控制是否允许使用对角线变得微不足道

我简短地讨论了是否将其作为一个列表而不是一个字典来加快查找速度,但我意识到这样做会使生成数字以外的序列变得更加困难(而且可能不会显著加快查找速度)


那么,你的问题是什么?你似乎理解这个问题,并且已经开始着手解决这个问题。你需要什么帮助?作为一个可能的提示,请注意,所有7位序列都可以通过计算所有6位序列,然后将所有可能的下一步动作添加到每个序列中来找到。所有的6位序列都可以通过计算所有的5位序列得到…你和你的邻居非常不一致。显示0只有1个邻居,而1有3个邻居。你算不算对角线?如果是,2应该有5个邻居,如果不是,1应该只有2个邻居,以此类推。除了邻居定义不同之外,与类似。感谢Jeff B的更正,不计算对角线。改变!在一个给定的序列中,数字可以使用不止一次吗?或者这就像Boggle,每个数字只能使用一次?这只是打印所有可能的数字序列。问题中有一些规则,关于序列中的下一个数字是什么。是的,请阅读我上面的评论。我注意到邻居的事。这就是没有邻居条件的答案。这就是人们用非动态语言编程的方式吗?为什么不像OP建议的那样在相邻数字的列表中使用哈希表呢?你甚至不需要第一类函数来实现它。@aaron,有些人就是这样编程的。语言是否是动态的并不重要。例如,您自己的答案没有使用任何动态功能。此解决方案与键盘布局紧密耦合,特别是因为OP提到“关于邻居的信息从一个键盘变为另一个键盘”不会变为-1,因为它不是严格错误的,但对于其他键盘布局来说,它不是非常可重用的。它是psuedo代码。其目的是以易于阅读的格式显示算法,而不是实际如何实现它。这可能是比我更好的解决方案。
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