![](/img/trans.png)
[英]The recursive implementation with python for a mathematical puzzle of hailstone sequence or Collatz conjecture?
[英]Longest Collatz (or Hailstone) sequence optimization - Python 2.7
我制作了一個程序,打印出一個數字列表,每個數字都有一個步驟(根據Collatz猜想 )需要比前一個更多的步驟:
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
我緩存了我已經知道的結果以加速程序。 這種方法可以達到10億的限制,我的計算機內存不足(8GB)。
先感謝您。
看起來很難加速Collatz計划; 我所知道的最好的程序是使用全球數百(數千)台PC上的空閑周期進行分發的。
雖然速度和空間優化通常不一致,但是在純CPython中可以使用一些簡單的方法來優化程序。
known
一個列表,而不是一個字典需要顯著的內存更少。 你要為每個號碼存儲一些東西; dicts更適合稀疏映射。 array.array
仍然需要更少的空間 - 盡管比使用列表慢。 n
, 3*n + 1
必然是偶數,所以你可以通過轉到(3*n + 1)//2 == n + (n >> 1) + 1
將2步折疊成(3*n + 1)//2 == n + (n >> 1) + 1
直。 n
了s
的步驟,然后2*n
將s+1
, 4*n
將取s+2
8*n
將取s+3
,依此類推。 這里有一些包含所有這些建議的代碼,雖然我使用的是Python 3(在Python 2中,你至少要將range
改為xrange
)。 請注意,啟動時會有很長的延遲 - 這是用大量的32位無符號零填充大型array
所需的時間。
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)
1992年寫的一份技術報告提供了許多方法來加速這種搜索: “3x + 1搜索程序”,由Leavens和Vermeulen編寫 。 例如,@ Jim Mischel的“基於先前峰值的切斷”理念基本上是論文的引理20。
另一個:對於2的簡單因子,請注意你甚至可以“幾乎總是”忽略起始數字。 為什么:讓s(n)
表示達到1所需的步數。您正在尋找s()
值的新峰值。 假設最近的峰值在n
找到,並且您正在考慮一個均勻的整數i
其中n < i < 2*n
。 然后特別是i/2 < n
,所以s(i/2) < s(n)
(通過“峰值”的定義並且在n
達到新的峰值)。 但是s(i) == s(i/2) + 1
,所以s(i) <= s(n)
: i
不能成為新的峰值。
因此,在n
處找到新峰值后,您可以跳過所有偶數整數(但不包括) 2*n
。
本文還有許多其他有用的想法 - 但它們並不是那么容易;-)
你只需要緩存奇數。 在你的程序中考慮當你開始編寫一個數字時會發生什么。
如果您使用起始編號X,並執行mod 4
,則最終會出現以下四種情況之一:
這可能會使您的程序稍微減慢一點,因為您有兩個除以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個。所以你可以停下來。
這可能會花費您更多的處理時間,但它會極大地擴展您的緩存。 您只有44個條目一直到837799.請參閱https://oeis.org/A006877 。
有趣的是,如果您對這些數字進行對數散點圖,則會得到非常接近的直線近似值。 請參閱https://oeis.org/A006877/graph 。
您可以通過保留第二個緩存來組合方法,對於數字大於當前最大值的數字,將該數字降低到當前最大值所需的步數。 因此,在上面的情況中,27有當前最大值,你將為數字35存儲26,因為它需要6次操作(106,53,160,80,40,20)才能得到35到20。你不能超過20步才能達到1,最多可能有26步。 因此,如果任何其他值減少到35,則將當前步數添加到26,如果該數字小於111,那么您知道使用此數字可能無法獲得新的最大值。 如果數字大於111,則必須繼續計算整個序列。
每當您找到新的最大值時,都會將生成它的數字添加到第一個緩存中,並清除第二個緩存。
這比較慢(我的直覺是在最壞的情況下它可能會使處理時間加倍),而不是緩存每個值的結果,但它會大大擴展你的范圍。
關鍵在於擴展你的范圍將以某種速度為代價。 這是一個常見的權衡。 正如我在上面所指出的,你可以做很多事情來保存每個第n項,這將為你提供一個更大的緩存。 因此,如果保存每個第4個值,則緩存基本上是保存每個值的4倍。 但是你很快就會達到收益遞減的程度。 也就是說,比原始緩存大10倍的緩存並不比9倍緩存大很多。
我的建議基本上給你一個指數增加的緩存空間,代價是一些處理時間。 但它不應該是處理時間的巨大增加,因為在最壞的情況下,具有下一個最大值的數字將是先前最大值的兩倍。 (想想27,有111步,有54步,有112步。)需要更多的代碼來維護,但它應該擴展你的范圍,目前只有30位,遠遠超過40位。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.