使用python中的一组特定规则生成电话号码

时间:2018-12-09 22:13:37

标签: python function random generator

我想编写一个函数,该函数使用以下规则集从标准电话键盘生成所有可能的号码(图1):

  • 电话号码以数字2开头
  • 电话号码长10位数字
  • 当骑士下棋时,选择每个电话号码的连续数字

在国际象棋中,骑士(有时称为马)会垂直移动两步,水平移动一步,或者水平移动两步,垂直移动一步。

enter image description here

电话号码中只能使用数字-即不允许使用(#)和(*)键。

该功能必须以电话号码的长度和初始位置作为输入,而输出则给出唯一的电话号码。

我是一个新手,在构建逻辑上面临困难。 我尝试按照以下步骤进行操作,这绝对不是正确的方法。

def genNumbers(len, initpos):
numb = list('2xxxxxxxxx')

#index 1
numb[1] = 7 or 9

if numb[1] == 7:
    numb[2] == 2 or 6
elif numb[1] == 9:
    numb[2] == 2 or 4

#index 2
if numb[2]== 2:
    numb[3] == 7 or 9
elif numb[2]== 4:
    numb[3] == 3 or 9
elif numb[2]== 6:
    numb[3] == 1 or 7

#index 3
if numb[3]== 1:
    numb[4] == 6 or 8  
elif numb[3]== 3:
    numb[4] == 4 or 8 
elif numb[3]== 7:
    numb[4] == 2 or 6 
elif numb[3]== 9:
    numb[4] == 2 or 4 

#index 4
if numb[4] == 8:
    numb[5]== 1 or 3
elif numb[4] == 2:
    numb[5]== 7 or 9
elif numb[4] == 4:
    numb[5]== 3 or 9
elif numb[4] == 6:
    numb[5]== 1 or 7

#index 5
if numb[5] == 1:
    numb[6]== 6 or 8
elif numb[5] == 3:
    numb[6]== 4 or 8
elif numb[5] == 7:
    numb[6]== 2 or 6 
elif numb[5] == 9:
    numb[6]== 2 or 4

#index 6 
if numb[6] == 2:
    numb[7]== 7 or 9
elif numb[6] == 4:
    numb[7]== 3 or 9 
elif numb[6] == 6:
    numb[7]== 1 or 7
elif numb[6] == 8:
    numb[7]== 1 or 3

#index 7 
if numb[7] == 1:
    numb[8]== 6 or 8
elif numb[7] == 3:
    numb[8]== 4 or 8
elif numb[7] == 7:
    numb[8]== 2 or 6   
elif numb[7] == 9:
    numb[8]== 2 or 4

#index 8
if numb[8] == 6:
    numb[9]== 1 or 7
elif numb[8] == 8:
    numb[9]== 1 or 3
elif numb[8] == 4:
    numb[9]== 3 or 9
elif numb[8] == 2:
    numb[9]== 7 or 9


return numb

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:3)

功能签名

  

该功能必须将电话号码的长度和初始位置作为输入,而输出则给出唯一电话号码的数量

关键思想

您的问题可以通过图论和线性代数(这些学科相遇的一个有趣的地方是离散数学)来解决。

首先,我们创建一个Adjacency Matrix来表示电话键盘上的合法动作:

import numpy as np

A = np.zeros((10, 10))
A[0,4]=1
A[0,6]=1
A[1,6]=1
A[1,8]=1
A[2,7]=1
A[2,9]=1
A[3,4]=1
A[3,8]=1
A[4,0]=1
A[4,3]=1
A[4,9]=1
A[6,0]=1
A[6,1]=1
A[6,7]=1
A[7,2]=1
A[7,6]=1
A[8,1]=1
A[8,3]=1
A[9,2]=1
A[9,4]=1

我们可以检查矩阵是否对称(不是必需的,但这是系统的属性):

np.allclose(A, A.T) # True

邻接矩阵的条目如下:A[0,4]=1表示从顶点0到顶点4的移动,而A[0,5]=0表示从{{ 1}}到0

5

然后我们计算[[0. 0. 0. 0. 1. 0. 1. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 1. 0. 1. 0.] [0. 0. 0. 0. 0. 0. 0. 1. 0. 1.] [0. 0. 0. 0. 1. 0. 0. 0. 1. 0.] [1. 0. 0. 1. 0. 0. 0. 0. 0. 1.] [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [1. 1. 0. 0. 0. 0. 0. 1. 0. 0.] [0. 0. 1. 0. 0. 0. 1. 0. 0. 0.] [0. 1. 0. 1. 0. 0. 0. 0. 0. 0.] [0. 0. 1. 0. 1. 0. 0. 0. 0. 0.]] 的幂A的乘积,这得出了长度为9的{​​{3}}个数字(这对应于长度为唯一的电话号码的计数在两个给定顶点之间的9(以数字10开始,以数字x结束):

y

路径长度为W = np.linalg.matrix_power(A, 9) ,因为顶点是数字,边缘在键盘上是移动,因此要拨n-1位电话号码,您需要10个移动(长度为{{ 1}})。

它给我们:

9

矩阵9的条目读为:[[ 0. 0. 336. 0. 544. 0. 544. 0. 336. 0.] [ 0. 0. 264. 0. 432. 0. 448. 0. 280. 0.] [336. 264. 0. 264. 0. 0. 0. 280. 0. 280.] [ 0. 0. 264. 0. 448. 0. 432. 0. 280. 0.] [544. 432. 0. 448. 0. 0. 0. 432. 0. 448.] [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [544. 448. 0. 432. 0. 0. 0. 448. 0. 432.] [ 0. 0. 280. 0. 432. 0. 448. 0. 264. 0.] [336. 280. 0. 280. 0. 0. 0. 264. 0. 264.] [ 0. 0. 280. 0. 448. 0. 432. 0. 264. 0.]] 表示W个电话号码,长度为W[2,1] = 264,以264开头,以10结尾

现在,我们总结从顶点2开始的步行:

1

您提供的一组规则中有2个电话,长度为np.sum(W[2,:]) # 1424.0 ,以数字1424开头。

功能

该函数编写起来很简单:

10

工作的大部分内容是对矩阵进行编码,该矩阵描述了规则集(允许在键盘上移动)。

检查

基于观察到的问题,我们可以从问题描述中得出,例如@SpghttCd,我们可以检查是否从2开始没有包含数字def phoneNumbersCount(n=10, i=2, A=A): return np.sum(np.linalg.matrix_power(A, n-1)[i,:]) 的长度10

2

我们可以检查是否从5开始不能写入长度为W[2,5] # 0.0 的数字:

10

对于给定的规则,实际上数字5完全不可用。

我们还可以检查另一个不明显的属性,例如:不存在长度为phoneNumbersCount(10, 5) # 0.0 并以510结尾的长度2245

6

提示

由于图没有定向(每个边都在两个方向上都存在),因此邻接矩阵是对称的。因此,矩阵创建可以简化为:

8

参考文献

一些有用的参考资料,以了解其工作方式和原因:

答案 1 :(得分:2)

序曲

让我们提出另一种解决问题的方法,该方法不涉及线性代数,但仍然依赖于图论。

表示形式

问题的自然表示是一个如下图所示的图形:

enter image description here

等于:

enter image description here

我们可以用字典来表示该图:

G = {
    0: [4, 6],
    1: [6, 8],
    2: [7, 9],
    3: [4, 8],
    4: [0, 3, 9],
    5: [],  # This vertex could be ignored because there is no edge linked to it
    6: [0, 1, 7],
    7: [2, 6],
    8: [1, 3],
    9: [2, 4],
}

这种结构将使您无需编写if语句。

邻接矩阵

上面的表示包含与邻接矩阵相同的信息。此外,我们可以从上面的结构(将布尔稀疏矩阵转换为积分矩阵)生成它:

def AdjacencyMatrix(d):
    A = np.zeros([len(d)]*2)
    for i in d:
        for j in d[i]:
            A[i,j] = 1
    return A

C = AdjacencyMatrix(G)
np.allclose(A, C) # True

Aanother answer中定义的邻接矩阵。

递归

现在我们可以使用递归生成所有电话号码:

def phoneNumbers(n=10, i=2, G=G, number='', store=None):
    if store is None:
        store = list()
    number += str(i)
    if n > 1:
        for j in G[i]:
            phoneNumbers(n=n-1, i=j, G=G, number=number, store=store)
    else:
        store.append(number)
    return store

然后我们建立电话号码列表:

plist = phoneNumbers(n=10, i=2)

它返回:

['2727272727',
 '2727272729',
 '2727272760',
 '2727272761',
 '2727272767',
 ...
 '2949494927',
 '2949494929',
 '2949494940',
 '2949494943',
 '2949494949']

现在只需要考虑列表的长度:

len(plist) # 1424

检查

我们可以检查是否有重复项:

len(set(plist)) # 1424

我们可以检查比我们对另一个答案中的最后一位数字所做的观察在该版本中仍然适用的情况:

d = set([int(n[-1]) for n in plist]) # {0, 1, 3, 7, 9}

电话号码不能以:

结尾
set(range(10)) - d # {2, 4, 5, 6, 8}

比较

第二个答案:

  • 不需要numpy(不需要线性代数),它仅使用Python标准库;
  • 使用Graph表示形式是因为它是您问题的自然表示形式;
  • 在对所有电话号码进行计数之前先生成所有电话号码,上一个答案并未生成所有电话号码,我们仅以x********y的形式获取有关电话号码的详细信息;
  • 可以使用递归来解决该问题,并且似乎具有指数级的时间复杂度,如果我们不需要生成电话号码,则应使用Matrix Power版本。

基准

递归函数的复杂度应限制在O(2^n)O(3^n)之间,因为递归树的深度为n-1(所有分支的深度相同),并且每个内部节点都创建最少2个边缘,最多3个边缘。这里的方法不是分而治之算法,而是 Combinatorics 字符串生成器,这就是我们期望复杂度呈指数级的原因。

对两个函数进行基准测试似乎可以验证这一说法:

enter image description here

递归函数以对数刻度显示线性行为,该行为证实了指数复杂度并受其限制。更糟糕的是,除了计算之外,将需要越来越多的内存来存储列表。除了n=23我什么都做不到,然后笔记本电脑在冻结MemoryError之前就冻结了。对复杂度的更好估计是O((20/9)^n),其中基数等于mean of vertices degrees(断开的顶点将被忽略)。

相对于问题大小n,矩阵乘方方法似乎具有恒定的时间。 numpy.linalg.matrix_power文档中没有实现细节,但这是known eigenvalues problem。因此,我们可以解释为什么复杂度在n之前似乎是恒定的。这是因为矩阵形状独立于n(它仍然是10x10矩阵)。大部分的计算时间专用于解决特征值问题,而不是将对角特征矩阵提高到第n次幂,这是一个微不足道的运算(也是唯一的依赖项)到n)。这就是为什么该解决方案以“恒定时间”执行的原因。此外,它还需要准恒定数量的内存来存储Matrix及其分解,但这也独立于n

奖金

在用于基准测试功能的代码下面找到:

import timeit

nr = 20
ns = 100
N = 15
nt = np.arange(N) + 1
t = np.full((N, 4), np.nan)
for (i, n) in enumerate(nt):
    t[i,0] = np.mean(timeit.Timer("phoneNumbersCount(n=%d)" % n, setup="from __main__ import phoneNumbersCount").repeat(nr, number=ns))
    t[i,1] = np.mean(timeit.Timer("len(phoneNumbers(n=%d, i=2))" % n, setup="from __main__ import phoneNumbers").repeat(nr, number=ns))
    t[i,2] = np.mean(timeit.Timer("len(phoneNumbers(n=%d, i=0))" % n, setup="from __main__ import phoneNumbers").repeat(nr, number=ns))
    t[i,3] = np.mean(timeit.Timer("len(phoneNumbers(n=%d, i=6))" % n, setup="from __main__ import phoneNumbers").repeat(nr, number=ns))
    print(n, t[i,:])