前言
在先前的1.0,1.1,1.2三个版本中已经对素数转圈问题进行了一定程度上的处理和优化,所以我们这里打算先放下对运算核心代码的进一步优化,而是选择给原代码套上一层外壳。
注:素数转圈问题1.0 素数转圈问题1.1 素数转圈问题1.2
本文概要
本文的主要目的有两个,一是要在1.2版本的基础上添加简单的GUI处理,使得代码的运行更有交互性;二是提出素数转圈问题的另一种解决措施。
正文
素数转圈问题1.3
我们进行GUI处理,这里借助的是广泛使用的Tkinter库来帮助我们实现的。先import一下:
import tkinter as tk
随后创建主页面,这里命名为main:
main=tk.Tk()
main.title("素数转圈问题1.3")
然后将1.2版本中原先print输出的问题描述移到主页面中显示,先创建一个标签并放置:
tk.Label(main, text="问题:在一平面直角坐标系上小云兮在遛狗狗,一开始的运动方向为x轴正方向。\
现记其走过的\n路程为s,当s为素数时,遛狗的前进方向顺时针旋转90度。现问当总路程x等于某一输入\
值时,\n其在x轴或y轴方向上所达到的最遥远两端的距离。").pack()
随后在下方放置输入内容提示标签以及输入框来获取参数:
tk.Label(main, text="请输入移动总路程:").pack()
pre_num=tk.Entry(main)
pre_num.pack()
再在下方设定触发运算的按钮并命名为“运算”,而这里的mainf就是原来1.2版本的代码主体部分,并为适应GUI做了一点调整:
button_calculate=tk.Button(main, text="运算", command=mainf)
button_calculate.pack()
mainf如下:
def mainf():
num=int(pre_num.get())
if num<3:
xans=ans=num
yans=0
else:
prime_numbers=[i for i in range(num) if is_prime(i)]
delta_list=[prime_numbers[i]-prime_numbers[i-1] for i in range(1,len(prime_numbers))]
if num not in prime_numbers:
delta_list.append(num-prime_numbers[-1])
xans,yans=divide_xy_ans(len(delta_list),delta_list)
ans=max(xans,yans)
text_info.delete('1.0', tk.END)
text_info.insert(tk.END, f"水平方向的极差={xans}\n竖直方向的极差={yans}\n最大的极差={ans}")
在页面也同时设定退出按钮:
button_exit = tk.Button(main, text="退出", command=main.quit)
button_exit.pack()
在页面最后设定多行文本编辑框来输出结果:
text_info = tk.Text(main)
text_info.pack()
最后别忘了写loop:
main.mainloop()
这样一来一个简易的GUI界面就搭建好了,运行效果如下: 已经有了一定的GUI界面,但客观评价美观程度确实不尽如人意,然而时间有限,这将会在以后着手进一步优化。
素数转圈问题1.3整体代码如下:
def is_prime(n):
#判断一个数是否为质数
if n<2:
return False
if n<4:
return True
if n%2==0 or n%3==0:
return False
i=5
while i*i<=n:
if n%i==0 or n%(i+2)==0:
return False
i+=6
return True
def extreme_value(max_val,min_val,x):
# 更新最大值和最小值
return max(x,max_val),min(x,min_val)
def divide_xy_ans(n,delta_list):
# 计算x和y方向的最远距离
x,y,xmax,xmin,ymax,ymin=0,0,0,0,0,0
for idx,delta in enumerate(delta_list):
if idx%4==0:
x+=delta
elif idx%4==1:
y-=delta
elif idx%4==2:
x-=delta
elif idx%4==3:
y+=delta
xmax,xmin=extreme_value(xmax,xmin,x)
ymax,ymin=extreme_value(ymax,ymin,y)
return (xmax-xmin,ymax-ymin)
def mainf():
num=int(pre_num.get())
if num<3:
xans=ans=num
yans=0
else:
prime_numbers=[i for i in range(num) if is_prime(i)]
delta_list=[prime_numbers[i]-prime_numbers[i-1] for i in range(1,len(prime_numbers))]
if num not in prime_numbers:
delta_list.append(num-prime_numbers[-1])
xans,yans=divide_xy_ans(len(delta_list),delta_list)
ans=max(xans,yans)
text_info.delete('1.0', tk.END)
text_info.insert(tk.END, f"水平方向的极差={xans}\n竖直方向的极差={yans}\n最大的极差={ans}")
import tkinter as tk
main=tk.Tk()
main.title("素数转圈问题1.3")
tk.Label(main, text="问题:在一平面直角坐标系上小云兮在遛狗狗,一开始的运动方向为x轴正方向。\
现记其走过的\n路程为s,当s为素数时,遛狗的前进方向顺时针旋转90度。现问当总路程x等于某一输入\
值时,\n其在x轴或y轴方向上所达到的最遥远两端的距离。").pack()
tk.Label(main, text="请输入移动总路程:").pack()
pre_num=tk.Entry(main)
pre_num.pack()
button_calculate=tk.Button(main, text="运算", command=mainf)
button_calculate.pack()
button_exit = tk.Button(main, text="退出", command=main.quit)
button_exit.pack()
text_info = tk.Text(main)
text_info.pack()
main.mainloop()
素数转圈问题another resolution
如果去阅读素数转圈问题1.0,1.1,1.2以及这次的1.3版本不难发现其解决措施都介入了一定的数学因素,直截运算所有素数并计算相邻素数的差值,然后通过对差值的运算以及对最后一个转弯点素数的确认来解决问题的。诚然,这样做的确有一定的优势,那就是快。直截略去了物体中间过程的繁琐的运动轨迹的处理,直接接近最终的结果。但解决这个问题最直接明了的方式其实是一步一步推,直接建立一个平面直角坐标系,只要确认了每一步的运动方向,然后通过坐标表示就好了嘛。运动方向我们依旧可以用模4的方式来确认,坐标的加减通过设立一个包含四个元组的集合即可,这样原先的作差部分就可以舍去,divide_xy_ans()函数部分可以更精简地改写,代码如下:
def divide_xy_ans(list,num):
#区分x,y方向位移并计算
directions=[(1, 0), (0, -1), (-1, 0), (0, 1)]
x,y,xmax,xmin,ymax,ymin,direction_index=0,0,0,0,0,0,0
for factor in range(1,num+1):
if factor-1 in list:
direction_index=(direction_index+1)%4
x, y = x + directions[direction_index][0], y + directions[direction_index][1]
if direction_index%4==0 or factor%4==2:
xmax,xmin=extreme_value(xmax,xmin,x)
else:
ymax,ymin=extreme_value(ymax,ymin,y)
xans=xmax-xmin
yans=ymax-ymin
return(xans,yans)
当然,这样处理的结果无疑会增大代码的运算量,但是却更有了可读性,更便于人们理解这个过程,并且这样处理的方式也更适用于欲研究每一步轨迹的任务;而且该问题真正核心的运算量其实在于素数的判断问题,如果是使用1.0版本的素数判断方式的话,该解决措施在应对x=100000(十万)这样数量级的运算耗时73s,比1.0的处理措施(60s)慢了13s;而在搭建了1.1或1.2版本的优化版素数判断方式,该解决措施在应对x=1000000(一百万)这样数量级的数据才与1.1或1.2版本的运算结果有了较明显的差距(该措施平均耗时12s,后者平均耗时3s),而在十万及以下的数据则差距并不明显,基本都在0.x秒以下。所以该解决措施还是有其一定的实用性的,所以这里也做了保留,完整代码如下,可以看出相较于之前的解决措施在代码长度上缩短了不少,虽然在效率上有所损失:
def is_prime(num,list):
#判断素数并收集到list
if num==0 or num==1:
return()
for factor in range(2,num):
if num%factor==0:
return()
list.append(num)
return()
def extreme_value(max,min,x):
#极值
if x >= max:
max=x
if x <= min:
min=x
return(max,min)
def divide_xy_ans(list,num):
#区分x,y方向位移并计算
directions=[(1, 0), (0, -1), (-1, 0), (0, 1)]
x,y,xmax,xmin,ymax,ymin,direction_index=0,0,0,0,0,0,0
for factor in range(1,num+1):
if factor-1 in list:
direction_index=(direction_index+1)%4
x, y = x + directions[direction_index][0], y + directions[direction_index][1]
if direction_index%4==0 or factor%4==2:
xmax,xmin=extreme_value(xmax,xmin,x)
else:
ymax,ymin=extreme_value(ymax,ymin,y)
xans=xmax-xmin
yans=ymax-ymin
return(xans,yans)
#主体部分
print('问题:在一平面直角坐标系上小云兮在遛狗狗,一开始的运动方向为x轴正方向。\
现记其走过的总路程为x,当x为素数时,遛狗的前进方向顺时针旋转90度。\
现问当x等于某一定值时,其在x轴或y轴方向上所达到的最遥远两端的距离。')
list=[]
num=int(input("请输入爬虫的移动总步数:"))
if num==0 or num==1 or num==2:
ans=xans=num
yans=0
else:
for factor in range(num):
is_prime(factor,list)
xans,yans=divide_xy_ans(list,num)
ans=max(xans,yans)
print('水平方向的极差=',xans,'竖直方向的极差=',yans,'最大的极差=',ans)
小结
通过这次的处理,为1.2版本的“素数转圈问题”的代码搭建了一个GUI页面,脱离开了机械化的计算机终端页面;同时也为该问题的核心解决措施提供了一个新的解决方案。
反思
分两个模块反思,一是在素数转圈问题1.3版本中,GUI界面客观上的粗糙,有待进一步的优化;二是在another resolution模块,我们需意识到代码精简度、代码运算效率与代码适用性三个方面之间既相互牵制,又没有必然联系的特点,这对以后我们的代码书写逻辑以及评判标准有着很大的重要性。