最长Collatz(或冰雹)序列优化-Python 2.7
我制作了一个程序,打印出一个数字列表,每一个数字都需要更多的步骤(根据)才能达到1,而之前的步骤是:最长Collatz(或冰雹)序列优化-Python 2.7,python,algorithm,caching,optimization,collatz,Python,Algorithm,Caching,Optimization,Collatz,我制作了一个程序,打印出一个数字列表,每一个数字都需要更多的步骤(根据)才能达到1,而之前的步骤是: limit = 1000000000 maximum = 0 known = {} for num in xrange(2, limit): start_num = num steps = 0 while num != 1: if num < start_num: steps += known[num]
limit = 1000000000
maximum = 0
known = {}
for num in xrange(2, limit):
start_num = num
steps = 0
while num != 1:
if num < start_num:
steps += known[num]
break;
if num & 1:
num = (num*3)+1
steps += 1
steps += 1
num //= 2
known[start_num] = steps
if steps > maximum:
print start_num,"\t",steps
maximum = steps
limit=100000000
最大值=0
已知={}
对于xrange中的num(2,限制):
start_num=num
步数=0
而num!=1:
如果num最大值:
打印开始编号“\t”,步骤
最大值=步数
我缓存我已经知道的结果以加速程序。此方法最多可工作10亿次,我的计算机内存不足(8GB)
提前感谢您。要大幅提高Collatz程序的速度,似乎有其固有的困难;我所知道的最好的程序,在全世界数百(数千…)台PC上使用空闲周期 在纯CPython中,您可以做一些简单的事情来稍微优化您的程序,尽管速度和空间优化常常是不一致的:
- 速度:Python中计算量大的程序应该始终作为函数编写,而不是作为主程序编写。这是因为局部变量访问比全局变量访问快得多
- 空间:使
列表而不是dict所需的内存大大减少。您正在为每个数字存储一些内容;dict更适合于稀疏映射成为已知的
- 空间:一个
数组。数组
仍然需要更少的空间-尽管比使用列表慢
- 速度:对于奇数
,n
必然是偶数,因此您可以通过直接转到3*n+1
(3*n+1)//2==n+(n>>1)+1将两个步骤折叠为1
- 速度:给定一个最终结果(数字和步数),你可以跳到前面,填写该数字乘以2的所有幂的结果。例如,如果
采取n
步骤,那么s
将采取2*n
,s+1
将采取4*n
,s+2
将采取8*n
,依此类推s+3
range
更改为xrange
),但这里有一些代码包含所有这些建议。请注意,启动时有很长的延迟-这是用十亿个32位无符号零填充大型数组所需的时间
def coll(limit):
from array import array
maximum = 0
known = array("L", (0 for i in range(limit)))
for num in range(2, limit):
steps = known[num]
if steps:
if steps > maximum:
print(num, "\t", steps)
maximum = steps
else:
start_num = num
steps = 0
while num != 1:
if num < start_num:
steps += known[num]
break
while num & 1:
num += (num >> 1) + 1
steps += 2
while num & 1 == 0:
num >>= 1
steps += 1
if steps > maximum:
print(start_num, "\t", steps)
maximum = steps
while start_num < limit:
assert known[start_num] == 0
known[start_num] = steps
start_num <<= 1
steps += 1
coll(1000000000)
def coll(限制):
从数组导入数组
最大值=0
已知=数组(“L”,(0表示范围内的i(限制)))
对于范围内的数值(2,限制):
步骤=已知[num]
如果步骤:
如果步骤>最大值:
打印(数字“\t”,步数)
最大值=步数
其他:
start_num=num
步数=0
而num!=1:
如果num>1)+1
步数+=2
当num&1==0时:
数值>>=1
步数+=1
如果步骤>最大值:
打印(开始编号“\t”,步骤)
最大值=步数
当开始数量<限制时:
断言已知[start_num]==0
已知[开始数量]=步数
start_num您只需要缓存奇数。在程序中考虑当你开始处理一个数字时会发生什么。
如果您使用起始编号X,然后执行mod 4
,则最终会出现以下四种情况之一:
- 0或2:重复除以2最终会得到一个小于X的奇数。您已缓存该值。因此,只需将除数除以2,将其添加到缓存值中,就可以得到序列长度
- 1:(3x+1)/2将得到一个偶数,再除以2将得到一个小于X的数。如果结果是奇数,那么您已经有了缓存的值,因此您可以向其中添加3。如果结果为偶数,则重复除以2,直到得到奇数(已缓存),将3和除以2的数目添加到缓存的值中,即得到序列长度
- 3:进行标准Collatz序列计算,直到得到一个小于起始数的数字。然后,要么缓存该值,要么该数字为偶数,然后重复除以2,直到得到奇数
这可能会使您的程序慢一点,因为您还有几次除以2,但它会使您的缓存容量加倍
只需为x mod 4==3
的数字保存序列长度,就可以使缓存容量再次翻倍,但这样做的代价是处理时间更长
这些只会使缓存空间线性增加。您真正需要的是一种整理缓存的方法,这样您就不必保存这么多结果。以一些处理时间为代价,您只需要缓存产生迄今为止发现的最长序列的数字
考虑到当您计算27有111个步骤时,您已经保存了:
starting value, steps
1, 0
2, 1
3, 7
6, 8
7, 16
9, 19
18, 20
25, 23
27, 111
所以当你看到28,你除以2得到14。搜索缓存时,您会看到14到1的步骤数不能超过19(因为任何小于18的步骤数都不能超过19)。所以最大可能的序列长度是20。但您已经有了最多111个。所以你可以停下来
这可能会花费更多的处理时间,但会大大扩展缓存。到837799为止,您只有44个条目。看
有趣的是,如果你做一个对数散点图