'''3n+1問題,雖然很多文章提到,但是這裡還是簡單的說一下它的運算過程:
任意一個大於0的整數,
(1)假如它是偶數,則除以2;
(2)假如它為奇數,則乘以3再加1;
不斷重複這兩種操作,最後必將得到數字1。
用python 實現的演算法就是: '''
def collatz(n):
if n<0: print("初始值",n,"不符合條件");return
while n != 1:
print(n)
if n%2 ==0:
n=n//2
elif n%2==1:
n=n*3+1
print(n)
''' 不用擔心程式停不下來,因為沒人 can 證明這個猜想不成立,至少我都沒有碰到某個死迴圈。
假如去掉“等於1”的判斷條件,把1放程式序算一下:'''
def test(n):
ct=50
while True:
print(n)
if n%2 ==0:
n=n//2
elif n%2==1:
n=n*3+1
ct-=1
if ct==0:break
'''可以看出程式是在整數1這裡不斷打圈圈的: 1-奇數- 4-偶數-2-偶數-1 》》》,因此1是個黑洞,程式掉進去就出不來了。
嚴格來說,這個黑洞是由1,4,2 這3個整數(3點環)構成的。因此collatz 在 1 這裡做了個踩剎車動作,結果結論就像是總會得到 1 的
最終結果。完整的事實是得到一個[1,4,2]構成的三角形旋轉黑洞...閒話就此打住,下面繼續討論第一個程式的演算法問題.
這個演算法的流程控制,是由數值的奇偶性控制的。也就是說整個過程的暫態結果,處於奇數和偶數交越狀態之中。
當暫態結果是偶數時,它將由第(1)分支處理,直到變成奇數。
當暫態結果是奇數時,它將由第(2)分支處理,直到變成偶數。
為了凸現奇偶性之間的轉變,我們可以把分支(1)提取出來,主要用於實現從偶數到奇數的狀態轉變,新建一個輔助
函式,提取某個整數因子'''
def abstr(n,mod=2):
''' n = kore * (m ** time) '''
time=0
kore=n
while kore%mod==0 and kore > 0 :
kore=kore//mod
time+=1
return kore,time
'''現在改寫原來的collatz函式,這樣列印的出來的數字會少一些'''
def collatz_abstr(n):
if n<0: print("初始值",n,"不符合條件");return
while n != 1:
print(n)
if n%2 ==0:
n=abstr(n)[0]
elif n%2==1:
n=n*3+1
print(n)
'''事實上collatz猜想 等價於 :把任意一個大於1 奇數數丟程式序,最終都會得到 1這個結果。
因為分支(1)總會把偶數變成奇數,也就是不斷抽取2這個因子後得到的核,也就是初始值是偶數並不重要,它將變成奇數核。
因此這個猜想只需考慮:任意奇數最終轉換成 1 就行了。
要想觀察數列,可以開始觀察一下,奇數核時觀察一下,最終結果觀察一下'''
def collatz_abstr(n):
if n<0: print("初始值",n,"不符合條件");return
print(n)
while n != 1:
if n%2 ==0:
n=abstr(n)[0]
print(n)
elif n%2==1:
n=n*3+1
'''下面考慮一個符合條件的奇數q的轉變過程:
q奇數 -> 3*q+1 == 2*q + q+1 偶數
-> q+ (q+1)/2 未知。
現在設q 被分成 大b小a兩部分,且滿足 a+b=q, b-a=1;
那麼 a= (q-1)/2 ; a 為小半部
b= (q+1)/2 ; b 為大半部
那麼 上面 q+ (q+1)/2 未知 == (a+b)+b = 2b+a== 3b-1 ==3a+2,它的奇偶性取決於 a。
即
[1]若a 為奇數,則 q ->> 2b+a 是一個奇數 且大於 q(=b+a);
[2]若a 為偶數,則q ->> 2b+a 是 偶數,然後可以除以2 ->> b+ a/2 = 3a/2 +1 且小於 q 。
從[2]可以推匯出一條公式:一個初始奇數q,若表達成k*(2^n)-1的形式(其中k是奇數,n>=1),那麼會到達一個暫態結果k*3^n-1.
q =k*(2^n)-1
q -> k*3^n-1
進一步推導,會有如果 q=k*(3^m)(2^n)-1 ,會到達一個暫態結果 (k*3^(m+n)-1)/2,其中k是奇數,n>=1。
這個公式也可以看出,好多初始值不同整數被“簡併”到同一個暫態結果上了。
假如你下過飛行棋,那麼這就像投到好的色子點數,在空中直接越過許多區域。
‘’‘‘'''
def fast(q):
v=q+1
k,n=abstr(v,mod=2)
k,m=abstr(k,mod=3)
v=(k*3**(n+m)-1)//2
return v
'''現在把這個快速的暫態放進collatz的第二分支'''
print("----hh--")
def collatz_abstr(n):
if n<0: print("初始值",n,"不符合條件");return
print(n)
while n != 1:
if n%2 ==0:
n=abstr(n)[0]
print(n)
elif n%2==1:
n=fast(n)
'''假如你的運氣特別好,初始值是2^n,那麼這個函式一路到底,運氣次好一點的就是那些一步可以到2^n的奇數,
那麼是哪些奇數?
由 3*q+1=2^n 得 q=(2^n-1)/3,很明顯不是所有的2^n都可以整除以3,但是所有 2^(2*n)-1 可以。n>=1'''
def goodluck(n,show=1):
if show==1:
for i in range(1,n):
v=(4**i-1)//3
print(i,v)
else:
v=(4**n-1)//3
return (v)
'''假如把這些數轉換為二進位制數,你會看到奇特的現象,都是零一相間的數,這樣它的小一半加上它再加一就是4^n次方'''
n=10
for i in range(1,n):
v=goodluck(i,show=0)
a=(v-1)//2
v=bin(v)
a=bin(a)
print("v={0:>20}".format(v[2:]))
print("a={0:>20}".format(a[2:]))
print("-----")
'''我們知道,“整數”這個概念是對“奇數”和“偶數”這兩個概念的簡併,
假如把整數分成奇數和偶數,那就是一種細分結構。
那麼再把奇偶細分,可以得到更細的概念。
因為奇偶代表著兩態,n%2==1 為奇,n%2==0 為偶。
假如我們把整數當成四態的數,類似地,
n%4 ==1 為1態,稱 n 為1態數 (或a數)
n%4 ==2 為2態,稱 n 為2態數 (或b數)
n%4 ==3 為3態,稱 n 為3態數 (或c數)
n%4 ==0 為0態,稱 n 為0態數 (或d數)
那麼這時的整數性質就成為 四態性質,不再是奇偶性質,(當然,此時也可以討論奇偶性,不過會發生簡併)
四態的運算性質和奇偶運算性質類似,不過框架不同了。
比如 1態數 + 1態數 變成 2態數 ,如 5+5=10,5是1態數,10是2態數。
又比如 3態數 x 3態數 變成 1態數, 如 3*7=21
這些性質可以由同餘理論推導。
那麼現在回到collatz上面來,前面我們說它的流程由奇偶行控制,那麼,現在由4態性控制。假如要讓程式能夠執行下去,那麼該如何改寫呢?
為了方便表達,我們把整數轉換成4態數數列,每個整數(正的)由它的態和“態中索引位置”決定,如下:
真值:0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
態值:d0, a0,b0,c0,d1,a1,b1,c1,d2,a2
整數9就是a3,即a態裡排第三,按照列表的記法,可以寫成a[3]。
在collatz變換下,每一次“可推測”的態性變化規律如下:
c[i] -> a[i+j],j=(i+2)/2, 其中 i 為偶數。↑ *3+1)/2
c[i] -> c[i+j],j=(i+1)/2, 其中 i 奇數。 ↑ *3+1)/2
a[i]-> d[i+j], j=(i+1)/2, 其中 i 為奇數。↑ *3+1)/2
a[i]-> b[i+j], j=1/2 其中 i 為偶數。 ↑ *3+1)/2
b[i]-> a[j], j=i/2, 其中 i 偶數。 ↓ /2
b[i]-> c[j], j=(i-1)/2, 其中 i 為奇數。 ↓ /2
d[i]-> abstr(i)-> kore -> a c ↓ '''
def what_is_the4(n):
states="dabc"
st=states[n%4]
i=n//4
return st,i
'''有意思的是,d[i]在向a和c轉換時,形成一種週期自相似結構。比如以4為週期列印轉換關係,則右邊的dom和原來的dom一樣。
d[i]的collatz轉換,在對映上形成分形結構。假如用分形的角度分析,那麼“態數”是尺度。再假如參考物理術語,那麼索引位置是“能級”
可以通過下面的列印觀察出來。'''
ct=0
print(" 數值, ran, dom 4_dom")
print("-----------------------")
for i in range(1,2000):
if i%4==0:
v=what_is_the4(n)
v=v[0]+str(v[1])
w=abstr(i)[0]
w=what_is_the4(w)
w=w[0]+str(w[1])
print ("{0:>5}{1:>5s}{2:>5s}".format(i,v,w))
ct+=1
if ct% 16==0:ct=0;print("---------------------**",w)
elif ct%4==0:print("-----------------------",w)
'''在研究那些轉換規律時,有些帶來數值的爬升,有些則下降,但是能否一直爬升或者一直下降?在同態爬升中
只有c->c 的轉換出現,很明顯,一個有限的能級值i, 它的若要保持後繼的i+j奇性不變,只能 i+1=2^n,經過n次轉轉後,
還是要改變奇偶性,也就是說,一直c態爬升是不可能的。c態最終變成a態。a態又經過b態或者d態下降變回a或c態,
但也有可能變成d態後就一直下降到a0=1了。詳細討論這些路徑應當很有趣,比如 a-> b-> a-> b-> a,最初a 和最終a的升降。
下面考察一下collatz(27)'''
print()
def collatz_see_me(n):
if n<0: print("初始值",n,"不符合條件");return
while n != 1:
if n%2 ==0:
v=what_is_the4(n); v=v[0]+str(v[1])
print(n,v,"↓")
n=n//2
elif n%2==1:
v=what_is_the4(n);v=v[0]+str(v[1])
print(n,v,"\n------")
n=n*3+1
print(n)
'''collatz變換,將對態和能級進行轉變。collatz(27)的峰值達到 9232 d2308 ,d態的2308級
假如態數目(尺度)變大的話,那麼一個整數的能級將變小,比如把它放到128態數上面觀察,峰值是9232 16s:72 ,第16態的72級'''
def states(n,mod=128,s='s'):
nevel=n//mod
st=n%mod
return str(st)+s+":"+str(nevel)
def collatz_see(n):
if n<0: print("初始值",n,"不符合條件");return
while n != 1:
if n%2 ==0:
v=states(n)
print(n,v,"↓")
n=n//2
elif n%2==1:
v=states(n)
print(n,v,"\n------")
n=n*3+1
print(n)
'''可以看出,假如態數數目很大,比如16384=2^14 > 9232,那麼collatz(27)的整個過程將會在0級裡面打轉。
特定態數目下的collatz變換的規律,是和能級之間的轉換(跳躍)密切相關的,但我們也看到,態數目很大的時候,
一定範圍內的整數,沒有能級之間的跳躍,只在0級中進行態的轉變。這說明了什麼?把地圖放大看?'''
for n in range(500):
if n%2 ==0:
v=states(n,mod=10)
w=states(n//2,mod=10)
print(n,v," ",w)
elif n%2 ==1:
v=states(n,mod=10)
w=states((n*3+1)//2,mod=10)
print(n,v," ",w)
'''=============
因為colatz涉及到(同餘)和分形,下面以同樣的方式來構造一個類似的函式。
可以看到其中有一個大的(迴圈)節點,還有類似歸吸引力的0,1,2這三個迴圈節點。
==========='''
def run(w, stop_counter, blackhole):
t=stop_counter
c=[];
ori=w
print(ori)
dangerous= blackhole | {0,1,2,3}
while w not in dangerous:
if w%3==0:
w=w//3
if w not in c:
c+=[w];
else:
p=c.index(w)
return c[p:]
else:
r=w%3
w=5*w-2*r
t-=1
if t==0: return 2, ori,len(c)
return 1
q= [227, 377, 627, 209, 347,
577, 961, 1601, 2667, 889,
1481, 2467, 4111, 6851,
11417, 19027, 31711,
52851, 17617, 29361,
9787, 16311, 5437, 9061,
15101, 25167, 8389, 13981,
23301, 7767, 2589, 863,
1437, 479, 797, 1327, 2211,
737, 1227, 409, 681]
blackhole=set(q)
for i in range(1,20000):
v=run(i, 30*i ,blackhole)
if v is not 1:
print(i,v);
break
'''假如你的cpu很弱,那麼,這個程式會有些卡頓,因此這也是一個測試cpu的程式'''