1. 程式人生 > >考拉茲猜想(改版)

考拉茲猜想(改版)

考拉茲猜想

考拉茲猜想是一個數學上的未解之謎,至今仍未解決,考拉茲猜想的內容如下:

  • 對於自然數 n 迴圈執行如下操作
  • nn 是偶數,用 nn 除以 22
  • nn 是奇數,用 nn 乘以 33 後加 11
  • 如此迴圈操作,無論初始值是什麼數字,最終都會得到 11 。2009年驗證到了數字5,764,607,523,034,234,8805,764,607,523,034,234,880仍然滿足這一猜想,但沒有得到數學上的證明,就無法斷言對於任何一個自然數都滿足該猜想。

問題

這裡我們的考拉茲猜想的改版為:若初始值 nn 是一個偶數,也對 nn 進行 n

n 乘以 3311 的操作;後面數字的計算,按照考拉茲猜想來,考慮這樣最後能夠回到初始值的數字。

例如:413402010516844 \to 13 \to 40 \to 20 \to 10 \to 5 \to 16 \to 8 \to 4,但初始值為 6 ,最終就不會是 1 。

求小於 1000010000 的偶數中,像上述的 22 或者 44 這樣“能回到初始值的數”有多少個?

分析

  • 雖然本題在初始值時,改變了考拉茲猜想的規則,但只要繼續計算下去,其最終的數字還是會像考拉茲猜想一樣,最終值歸為數字11。這是因為,在對某一個數字進行一系列的計算時,我們將第一步分離出去,那麼剩下的 n
    1n-1
    步驟就全都滿足考拉茲猜想,因此我們可以將第一步計算的結果作為一個考拉茲猜想計算的輸入值,那麼接下來的計算就是純粹的考拉茲猜想計算了,所以最終數字還是 11
  • 對於任何一個數字的考拉茲猜想計算而言,最終都會歸於這樣一個數字鏈:...421...4 \to 2 \to 1,若繼續計算下去,就會有迴圈數字鏈: ...421421421...... 4 \to 2 \to 1 \to 4 \to 2 \to 1 \to 4 \to 2 \to 1 \to ...,若計算的數字是大於 44 的,則一旦結果達到1
    1
    ,就不會再出現該數字了
  • 因此,對於本題而言,若某個數字能回到初始值,那麼就意味著該數字一定出現在數字 11 之前;否則,該數字就無法再計算鏈中回到初始值。

演算法

  • 對每一個數字進行 num3+1num * 3 + 1 的計算
  • 當結果不是 11 的時候,就進行考拉茲猜想的計算;其中考拉茲猜想計算中,先判斷是否是偶數,然後再進行不同的計算,這裡用三運算子實現更簡潔
  • 每個數字最終都會達到 11 ,在每個數字到達 11 之前,若出現與原來的初始輸入的引數相等,則說明該數字計算鏈中出現了返回初始值的情況,此時跳出迴圈返回TrueTrue

Python程式碼實現

def is_loop(num):
    n = num * 3 + 1
    while n != 1:
        n =  n * 3 + 1 if n % 2 else n // 2
        if n == num:
            return True

count = 0
for num in range(2, 10000, 2):
    count += 1 if is_loop(num) else 0
print(count)

# 34

遞迴演算法

分析

  • 基於我們上述分析可知,只需要將改版過後的考拉茲猜想計算的第一步的計算結果,視為一個輸入,則接下來的計算就是考拉茲猜想的計算了。
  • 考拉茲猜想的計算,本質上就是一個遞迴的過程。其遞迴部分是:若nn 是偶數,則用 nn 除以 22;若nn 是奇數,則用 nn 乘以 33 後加 11。結合本題的特點,這個遞迴的終止條件是:若計算結果為 11 ,則表示沒有回到數字的初始值;若計算結果為初始值,則表示該數字存在回到初始值的情況

演算法

  • 因為是遞迴函式,所以引數n會不斷變化,需要宣告一個global全域性變數用於儲存當前傳入的數字。
  • 遞迴函式只計算 n1n-1 步驟的考拉茲猜想部分,第一步的計算結果由函式的引數直接傳入。
  • 若函式計算結果出現了11,則表示該數字不存在回到初始值特性,返回FalseFalse;若函式計算結果出現了全域性變數初始值,則返回TrueTrue;若兩者都不是,則繼續遞迴下去。

Python程式碼實現

def is_loop(n):
    global num
    if n == 1:
        return False
    elif n == num:
        return True
    else:
        return is_loop(n=n*3+1 if n%2 else n//2)

num = None
count = 0
for n in range(2, 10000, 2):
    num = n
    if is_loop(n*3+1):      #將第一步的結果作為輸入
        print(n, end=' ')   #列印符合要求的數字
        count += 1
print('\n', count)

# 2 4 8 10 14 16 20 22 26 40 44 52 106 184 206 244 274 322 526 650 668 790 866 976 1154 1300 1438 1732 1780 1822 2308 2734 3238 7288 
# 34