标题python解决约瑟夫环问题
问题描述:编号为 1-N 的 N 个士兵围坐在一起形成一个圆圈,从编号为 1 的士兵开始依次报数(1,2,3…这样依次报),数到 m 的 士兵会被杀死出列,之后的士兵再从 1 开始报数。直到最后剩下一士兵,求这个士兵的编号。
有用递归函数 f(n,m) 的返回结果是存活士兵的编号,推导出old 与 new 之间的关系为 old = (new + m - 1) % n + 1。即f(n,m)=(f(n - 1, m) + m - 1) % n + 1,号称可以一行代码可以搞定,按着学习的思路。
用python写出代码如下:如果n=6,m=4时:去除的编号为4 2 1 3 6 5;
def f( n, m):
if(n == 1):
return n
# print(n-1,m,f(n - 1, m) )
return (f(n - 1, m) + m - 1) % n + 1
print(f(6,4))
如果去掉#print 的行,一共有5行代码;
但发现有两个问题:
一、推导出最后的数字是对的,但过程中的未显示出来;
二、如果把f(n-1,m);f(n-2,m)…f(1,m)显示出来,发现并不是想要的编码结果;
f(1,4)=1;f(2,4)=1;f(3,4)=2;f(4,4)=2;f(5,4)=1,f(6,4)=5 只有最后的数字是对的;
如何能用比较短的行代码来运行出正确的顺序并显示出来呢
写出代码如下:应该有高手来总结出更简洁的代码
n=6;m=4;a=(list(i for i in range(1,n+1))*3*m);
for j in range(1,n):
print ("删除: ",a[m-1]);b=a[m-1];a=a[m:];
for i in range(len(a)-1,-1,-1):
if a[i]==b:a.pop(i)
print ("剩余: ",a[0])
运行结果如下:
删除: 4
删除: 2
删除: 1
删除: 3
删除: 6
剩余: 5
顺序与编号都是一致的,用了6行代码;
首先建立一个大list,让1到n循环3M次(不是口罩了,都卖脱销了,这里也没有;注:2M次会出错)
然后运行n-1次,从头数M个数,即为要删除的数,在此数前全部不要,后面含此数的也遍历删除,然后再数M个数。。。直到剩余一个数并显示出来。
不会用for循环 和if在一行语句里判断,请高手来指导,争取也能两行完成。
后记:
用第一个递推时,运行f(3811,4)时出错,机器的递归深度达到极限了,虽然用
import sys
sys.setrecursionlimit(1000000) #修改递归深度的值
但没有什么用,第二个程序出错,
import math
n=10000;m=4;a=(list(i for i in range(1,n+1))*int((-1)*math.log(n)/math.log((m-1)/m)+1));
for j in range(1,n):
print ("删除: ",a[m-1],end='');b=a[m-1];a=a[m:];
for i in range(len(a)-1,-1,-1):
if a[i]==b:a.pop(i)
print ("剩余: ",a[0])
这样修改了,不出错了,但代码又长了一点。运行时间也长了。但当n<m时,又出错了,原来是list的定义有了问题,再次修改。
import math
n=10000;m=4;a=(list(i for i in range(1,n+1))*(3*m if m>n else int((-1)*math.log(n)/math.log((m-1)/m)+1)));
for j in range(1,n):
print ("删除: ",a[m-1]);b=a[m-1];a=a[m:];
for i in range(len(a)-1,-1,-1):
if a[i]==b:a.pop(i)
print ("剩余: ",a[0])
运行正常,能不能再把list只用一次,然后循环利用呢,答案是可以的,用截取再相加的方法,循环利用list
n=12;m=4;a=(list(i for i in range(1,n+1)))
for j in range(1,n):
d=(m if m<=len(a) else (len(a) if m%len(a)==0 else m%len(a)));b=a[:d-1];c=a[d-1];print ("删除: ",c);a=a[d:];a=a+b
print ("剩余: ",a[0])
其中的条件语句是这样的:
条件为真的值 if 条件 else 条件为假的值
运行也快,解决,如果能把for 和 print也能写入一行,就再完美了。