利用python测试圆周率是否包含所有6位数密码,及其首次出现的位数
先使用y-cruncher生成圆周率的前2500万位,数据保存在txt文件中,文件大小24MB,修改文件只保留小数点后的数字,接下来就可以对数据进行测试。
1,最直接的办法查找
index()函数可返回开始的索引值,即首次出现的位数,代码如下:
pi_file = r'E:\测试\Pi-25000000.txt' # 读取文件
with open(pi_file, 'r') as f:
pi = f.read()
#print(len(pi))
password_dict = {} # 字典保存,密码-首次出现位数
for num in range(1000000): # 所有6位数字密码
key = ('%06d' % num)
password_dict[key] = pi.index(key)
if num % 100000 == 0: # 查看进度
print(key)
save_file = r'E:\测试\Pi-password.txt' # 保存路径
with open(save_file, 'w') as f:
for key, value in password_dict.items():
f.write('%s:%s\n' % (key, value))
测试结果:
000000-999999一共100万种密码都在圆周率前2500万位中出现,最后出现的密码是569540,位数14118306(从0开始计数)
密码:首次出现的位数(从0开始计数)
不过代码运行比较慢,圆周率2500万位查找100万种密码,共耗时1059秒。
2,改进代码分段查找
这是由于不断使用index()函数对2500万位数字从左向右查找,重复查找过多,因此可以从数字中查找密码,这样每6位数字只需查找一次,改进代码如下:
pi_file = r'E:\测试\Pi-25000000.txt' # 读取文件
with open(pi_file, 'r') as f:
pi = f.read()
password_dict = {} # 字典保存,密码-首次出现位数,初始位数设为-1
for num in range(1000000): # 所有6位数字密码
password_dict['%06d' % num] = -1
k = 100*10000 # 分段查找,每段设置位数
n_max = len(pi) // k # 防止死循环,最好整除
n = 1
while -1 in password_dict.values() and n <= n_max:
for i in range((n-1)*k, n*k):
password = pi[i:i+6]
if password_dict[password] == -1:
password_dict[password] = i
elif i < password_dict[password]:
password_dict[password] = i
n = n + 1
save_file = r'E:\测试\Pi-password.txt' # 保存路径
with open(save_file, 'w') as f:
for key, value in password_dict.items():
f.write('%s:%s\n' % (key, value))
测试结果是一样的,不过运行速度大幅提高,改进代码仅耗时8.5秒,相差120倍。
3,测试是否包含日期
既然6位数密码都在圆周率的前2500万位中,那么接下来测试一下1921年至2020年所有日期是否也在其中。
按照惯例,先用最直接的办法查找,不过index()函数未找到会报错,改为find()函数查找,代码如下:
pi_file = r'E:\测试\Pi-25000000.txt' # 读取文件
with open(pi_file, 'r') as f:
pi = f.read()
birthday_dict = {} # 生日字典
mmdd = []
for m in range(1, 13):
for d in range(1, 32):
mmdd.append('%02d%02d' % (m, d))
temp = ['0229', '0230', '0231', '0431', '0631', '0931', '1131']
for i in temp:
mmdd.remove(i)
# 直接查找
for year in range(1921, 2021):
for md in mmdd:
yyyymmdd = str(year) + md
birthday_dict[yyyymmdd] = pi.find(yyyymmdd)
if (year % 4) == 0:
yyyymmdd = str(year) + '0229'
birthday_dict[yyyymmdd] = pi.find(yyyymmdd)
print(year)
save_file = r'E:\测试\Pi-birthday.txt' # 保存路径
with open(save_file, 'w') as f:
for key, value in birthday_dict.items():
f.write('%s:%s\n' % (key, value))
测试结果:
1921年至2020年一共36525天只有8020天在圆周率前2500万位中出现,最先出现的日期是20190914,位数243,最后出现的日期是19660505,位数24999789(从0开始计数)
日期:首次出现的位数(从0开始计数)
直接查找自然很慢,耗时731秒,按照之前的方法进行改进,改进代码如下:
# 分段查找
for year in range(1921, 2021):
for md in mmdd:
birthday_dict[str(year) + md] = -1
if (year % 4) == 0:
birthday_dict[str(year) + '0229'] = -1
k = 100*10000
n_max = len(pi) // k # 防止死循环,最好整除
n = 1
while -1 in birthday_dict.values() and n <= n_max:
for i in range((n-1)*k, n*k):
birthday = pi[i:i+8]
if birthday in birthday_dict: # 判断是否为待查找日期
if birthday_dict[birthday] == -1:
birthday_dict[birthday] = i
elif i < birthday_dict[birthday]:
birthday_dict[birthday] = i
n = n + 1
改进代码仅耗时4.3秒,相差170倍。
既然2500万位没有测试出所有日期出现的位数,那么再试试看1亿位,结果一共36525天有23163天出现了,最后出现的日期是19640421,位数99995337(从0开始计数)