將與數學相關的演算法, 例如轉轉相除法與連分數演算法, 以海龜移動呈現, 讓學生可以了解數學演算法的執行過程, 算是用海龜繪圖, 實現演算法視覺化.
gcd(a,b) = gcd(b,a % b)
將 a 的大小遞降, 最後達到 最小的非零的 r, 就是 a, b 的最大公因數
此種解題思路, 有的叫 無窮遞降法, 用在很多有名的數學大定理的證明中, 例如 John Nash 的一個證明, 也是用遞歸迭代的方式去遞降一個參數, 叫做 Nash iteration method,
看起來很高深, 其實都是類似的思維.
“Talk is cheap. Show me the code.”
― Linus Torvalds
老子第41章
上德若谷
大白若辱
大方無隅
大器晚成
大音希聲
大象無形
道隱無名
拳打千遍, 身法自然
“There’s no shortage of remarkable ideas, what’s missing is the will to execute them.” – Seth Godin
「很棒的點子永遠不會匱乏,然而缺少的是執行點子的意志力。」—賽斯.高汀
致謝: 非常感謝製作 python turtle demo 的義工群!
從turtle海龜動畫學習Python-高中彈性課程1 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 2 安裝 Python, 線上執行 Python link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 3 烏龜繪圖 所需之Python基礎 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 4 烏龜開始畫圖 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 5 用函數封裝重複性指令-呼叫函數令烏龜畫正 n 邊形 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 6 畫多重旋轉圓,螺旋正方形 link
從turtle海龜動畫 學習 Python - 7 遞歸 recursive - 高中彈性課程系列 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 8 碎形 (分形 fractal) link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 8.1 碎形 L-system link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 9 Python 物件導向介紹 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 9.1 Python 物件導向的練習 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 10 藝術畫 自定義海龜形狀 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 10.1 藝術畫 python繪製天然雪花結晶 https://blog.csdn.net/m0_47985483/article/details/122262036 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 10.2 藝術畫 Python 製作生成式藝術 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 11.1 氣泡排序 - 用 turtle 呈現演算法之執行動作 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 11.2 maze 迷宮 - 用 turtle 呈現演算法之執行動作 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 11.3 連分數演算法與轉轉相除法- 用 turtle 呈現演算法之執行動作 link
從turtle海龜動畫 學習 Python - 高中彈性課程系列 11.4 最短路徑 Dijkstra- 用 turtle 呈現演算法之執行動作 link
連分數演算法其實與轉轉相除法是同一個東西,
轉轉相除法一般是用來求最大公因數, gcd(a, b), 他是提取試除的最終步驟所獲得的非零餘數, 這就是 a,b 的最大公因數
連分數演算法則是提取每次試除的步驟所獲得的商數,
以下我們參考 "理解演算法Python初學者的深度歷險 " 這本書的說明及codes.
歐基理德輾轉相除法求 gcd(a,b), 主要基於以下這個性質或觀察:
Lemma If a= qb + r, then gcd(a,b) = gcd(b,r).
註: a: dividend 被除數, b: divisior 除數, q: quotient 商數, r: remainder 餘數.
Ref: Burton, Elementary Number Thoery, 7th, Sec 2.4.
基於此性質, 可以透過不斷的 執行
a
=
q
b
+
r
a= qb + r
a=qb+r
a
←
b
a \leftarrow b
a←b
b
←
r
b \leftarrow r
b←r
或是濃縮寫成:
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
=
g
c
d
(
a
%
b
,
b
%
(
a
%
b
)
)
=
⋯
gcd(a,b) = gcd(b, \; a \% b)= gcd(a \% b, \; b \% \; (a \%b))=\cdots
gcd(a,b)=gcd(b,a%b)=gcd(a%b,b%(a%b))=⋯
將 a 的大小遞降, 最後達到 最小的非零的 r, 就是 a, b 的最大公因數
此種解題思路, 有的叫 無窮遞降法, 用在很多有名的數學大定理的證明中, 例如 John Nash 的一個證明, 也是用遞歸迭代的方式去遞降一個參數, 叫做 Nash iteration method,
看起來很高深, 其實都是類似的思維.
另外也可以把 Lemma 改成是相減的, 可以叫做 輾轉相減法求 gcd(m,n), 但是效能會較慢.
以下我們先寫一個用輾轉相除的程式求 gcd,
後面我們再寫一個用輾轉相減的程式求 gcd,
如果用Python 內建的 取餘數的指令 r = a % b 或是 r = mod(a, b)
則可以把 Lemma 改寫成:
Lemma (Python Version) If a>=b and r = a % b, then gcd(a,b) = gcd(b,r).
或是
Lemma (Python Version) If a>=b and r = mod(a, b), then gcd(a,b) = gcd(b,r).
或是更直接
Lemma (Python Version) If a>=b , then gcd(a,b) = gcd(b,a % b).
Lemma (Python Version) If a>=b , then gcd(a,b) = gcd(b, mod(a, b)).
# By Prof. P-J Lai MATH NKNU 2020/2/13
def gcd_Euclid_abr(m,n):
''' Find gcd(m.n) with division 用"歐基理德輾轉相除法"求 gcd(m,n)'''
a = max(m, n)
b = min(m, n)
q = a // b
r = a % b
while r >0:
a, b = b, r
r = a % b
return b
print("By gcd_Euclid_abr")
print(gcd_Euclid_abr(4,2))
print(gcd_Euclid_abr(7,3))
print(gcd_Euclid_abr(5,20))
print(gcd_Euclid_abr(72511,1229))
# 楊克昌趣味數學編程及拓展, P.59: 72511 = 59 * 1229
#>>> #gcd_Euclid(7787340436465476554757657804244,7341600567657341#2345766867345250282)
#34
用"相減法"時,
Be careful!
假設 a >= b 且 d = a - b,
It maybe b > d or b < d. (相減之後的 d 不一定比 b 小!)
所以每次都要用 a, b = max(b, d), min(b, d) 重新取大與小,
如果用"歐基理德輾轉相除法", 則 b > r (商數一定比餘數大), 則不須max, min 的動作
# gcd_substract
# By Prof. P-J Lai MATH NKNU 2020/2/13
# 用"相減法"求 gcd(m,n)
# Use max(), min() no need to import math
# 用"相減法"時, # Be careful! It maybe b > d or b < d
# 所以要用 a, b = max(b, d), min(b, d)
# 如果用"歐基理德輾轉相除法", 則 b > r (商數一定比餘數大),
# 則不須max, min 的動作
def gcd_substract_abd(m,n):
''' Find gcd(m.n) with division 用"相減法"求 gcd(m,n)'''
a = max(m, n)
b = min(m, n)
d = a - b
while d >0:
a, b = max(b, d), min(b, d)
# Be careful! It may b > d or b < d
d = a - b
return b
print("gcd_substract_abd")
print(gcd_substract_abd(4,2))
print(gcd_substract_abd(7,3))
print(gcd_substract_abd(5,20))
print(gcd_substract_abd(72511,1229))
# 楊克昌趣味數學編程及拓展, P.59: 72511 = 59 * 1229
33
105
=
3
+
1
5
+
1
2
\displaystyle \frac{33}{ 105} = 3+\frac{1} {5+\frac{1} {2}}
10533=3+5+211
一些特殊的數字, 會有很漂亮的連分數表法,
例如:
黃金分割比例
ϕ
=
1
+
5
2
\phi=\frac{1+\sqrt{5}}{2}
ϕ=21+5,
ϕ
\phi
ϕ 有很漂亮的連分數表法:
ϕ
=
1
+
1
1
+
1
1
+
1
1
+
1
1
+
⋯
−
−
−
−
−
−
−
(
0
)
\displaystyle \phi= 1+\frac{1} {1+\frac{1} { 1+\frac{1} {1+\frac{1}{1+\cdots} } } } -------(0)
ϕ=1+1+1+1+1+⋯1111−−−−−−−(0)
$$\displaystyle
\phi= 1+
\frac{1} {1+
\frac{1} { 1
+\frac{1} {1
+\frac{1}{1+\cdots}
}
}
}$$
因為
ϕ
\phi
ϕ 會滿足方程式
x
2
−
x
−
1
=
0
x^2-x-1=0
x2−x−1=0, 所以有
ϕ
=
1
+
1
ϕ
\phi=1+\frac{1}{\phi}
ϕ=1+ϕ1 -------(1)
繼續以(1) 帶入(1)中的
ϕ
\phi
ϕ, 得到
ϕ
=
1
+
1
1
+
1
ϕ
\phi=1+\frac{1}{1+\frac{1}{\phi}}
ϕ=1+1+ϕ11 -------(2)
如此持續代入, 就得到 (0)式.
以下是原書的codes, 我們加上說明,
後面會再用 divmod(), 寫一個更精簡易讀的版本
# Noted by P-J Lai MATH NKNU 20221110
# Bradford Tuckfield, 陳仁和譯,
# 理解演算法Python初學者的深度歷險, 基峰出版, 2021.
# chapter5, P.96
# 連分數演算法其實就是輾轉相除法,
# gcd(a,b) 是取最後非零的餘數為最大公因數
# 連分數則是取每次得出之商數
##>>> help(divmod)
##Help on built-in function divmod in module builtins:
##
##divmod(x, y, /)
## Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
import math
def continued_fraction(x,y,length_tolerance):
output = []
big = max(x,y)
small = min(x,y)
# 取大小就是做倒數的動作, 發現不是,
# 其實連分數演算法與輾轉相除法是完全一樣的, 每次都是前一次之除數除以前一次之餘數.
# 所以之後不需要再取大小, 因為除數一定比餘數大.
while small > 0 and len(output) < length_tolerance:
quotient = math.floor(big/small)
# 以上可以用 big//small 效果一樣
output.append(quotient)
new_small = big % small
big = small
small = new_small
# 以上可以用 (q, r) = divmod(big, small) 一次得到商數跟餘數 20221111
return(output)
print(continued_fraction(105,33,10))
按 F5 執行編譯:
最後一行為
print(continued_fraction(105,33,10))
得
>>>
=== RESTART: C:\Users\user\Desktop\202302上課\Listing05-4.0 連分數演算法其實就是輾轉相除法.py ===
[3, 5, 2]
如果用Python 內建的 同時取商與餘數的指令 (q, r) = divmod(big, small)