More Related Content
More from hungchiayang1 (20)
Ch5 範例
- 1. 5 串列(二)
數值求根法 (一) :二分逼近法與牛頓迭代法
51
二分逼近法與牛頓迭代法是微積分中的兩種簡單數值求根法,前者要求函
數為連續函數,後者則需利用函數的一次微分。假設求根函數為 f(x),
以下簡單介紹這兩種方法:
二分逼近法:
二分逼近法需先找到根的所在範圍 (a,b),同時滿足
f(a)f(b) < 0。然後計算中間點 c ,檢查 f(a)f(c) 與
f(b)f(c) 哪個乘積小於 0 來確認根是在 (a,c) 或是 (c,b)
之間,重複前述步驟直到 f(c) 很小,函數根 r 的近似值即是 c。
牛頓迭代法:
牛頓迭代法利用切線來估算函數根,假設 a 點接近函數根 r,則穿過
(a,f(a)) 點的切線為 ,整理後得 。
當 y = 0 ,切線與 X 軸相交於 b ,即 ,b 會 比 a
更接近 r ,讓 b 取代 a ,重複前述步驟直到 f(b) 很小,此時 b
即是 r 的近似根。以上步驟可改寫成以下迭代公式:
i ≥ 0
- 3. 5 串列(二)
數值求根法 (三)
53
import math
# 使用 lambda 設定函數
# 函數 : x**2 - 2 與一次微分: 2x
f = lambda x : x**2 - 2
df = lambda x : 2*x
# 根
r = math.sqrt(2)
# 二分逼近法
a , b , k = 1 , 2 , 0
fa , fb = f(a) , f(b)
tol1 = 1.e-5
print( ”> 二分逼近法: 起始區間 (”, a , ”,” , b , ”)” , sep=”” )
while True :
c = (a + b)/2
err = abs(c-r)
k += 1
# 迭代次數 近似根 誤差,以下 10e 代表以 10 格與科學記號呈現數字
print( "{:<2} : {:<10e} {:<10e}".format(k,c,err) , sep="" )
fc = f(c)
- 4. 5 串列(二)
數值求根法 (四)
54
# 函數絕對值小於 tol1 才跳離迭代
if abs(fc) < tol1 : break
if fc * fa < 0 :
b = c
fb = fc
else :
a = c
fa = fc
print()
# 牛頓迭代法
a , k , tol2 = 2 , 0 , 1.e-14
err = abs(a-r)
print( "> 牛頓迭代法:" )
print( "{:<2} : {:<10e} {:<10e}".format(k,a,err) , sep="" )
while True :
b = a - f(a)/df(a)
err = abs(b-r)
k += 1
# 迭代次數 近似根 誤差,以下 10e 代表以 10 格與科學記號呈現數字
print( "{:<2} : {:<10e} {:<10e}".format(k,b,err) , sep="" )
# 函數絕對值小於 tol2 才跳離迭代
if abs(f(b)) < tol2 : break
a = b
- 5. 5 串列(二)
數值求根法 (五)
55
程式輸出:
> 二分逼近法: 起始區間 [1,2]
1 : 1.500000e+00 8.578644e-02
2 : 1.250000e+00 1.642136e-01
3 : 1.375000e+00 3.921356e-02
4 : 1.437500e+00 2.328644e-02
5 : 1.406250e+00 7.963562e-03
6 : 1.421875e+00 7.661438e-03
7 : 1.414062e+00 1.510624e-04
8 : 1.417969e+00 3.755188e-03
9 : 1.416016e+00 1.802063e-03
10 : 1.415039e+00 8.255001e-04
11 : 1.414551e+00 3.372189e-04
12 : 1.414307e+00 9.307825e-05
13 : 1.414185e+00 2.899206e-05
14 : 1.414246e+00 3.204310e-05
15 : 1.414215e+00 1.525518e-06
> 牛頓迭代法:
0 : 2.000000e+00 5.857864e-01
1 : 1.500000e+00 8.578644e-02
2 : 1.416667e+00 2.453104e-03
3 : 1.414216e+00 2.123901e-06
4 : 1.414214e+00 1.594724e-12
5 : 1.414214e+00 0.000000e+00
- 9. 5 串列(二)
數值積分法 (三)
59
本題分別利用迴圈與公式來估算 的積分,觀察
程式碼可知使用迴圈的計算方式遠比公式來得笨拙許多,主要原因在公
式計算積分中,我們利用了 python 的串列截取語法迅速取得部份串
列,使用一列式子即能求得梯形法的積分估算值,例如以下的式子雖然
看不到迴圈,但卻有滿滿的「迴圈」精神:
# 梯形法係數:1,2,2,2,...,2,2,1
isum2 = h * sum( [ ys[0] , 2*sum(ys[1:-1]) , ys[-1] ] ) / 2
以上的 ys 為函數值串列。
由程式執行結果可知,三種數值積分法的積分誤差:
Simpson 積分法 < 梯形積分法 < 矩形積分法
執行結果完全符合數值積分的理論預期。
- 10. 5 串列(二)
數值積分法 (四)
60
import pylab
pi = pylab.pi
# [a,b] 100 等份
a , b , n = pi/4 , pi , 100
# 定義函式
fn = lambda x : abs( pylab.sin(x) - pylab.cos(x) )
# 取等份點成 xs ,向量式運算得 ys
xs , h = pylab.linspace(a,b,n+1,retstep=True)
ys = abs( pylab.sin(xs) - pylab.cos(xs) )
# rsum : 矩形面積
# lsum : 下矩形面積 , usum : 上矩形面積 , tsum : 梯形面積
rsum , lsum , usum , tsum = 0 , 0 , 0 , 0
y1 = ys[0]
# 迴圈計算:矩形、上矩形、下矩形、梯形
for y2 in ys[1:] :
rsum += y1
if y1 < y2 :
lsum += y1
usum += y2
else :
lsum += y2
usum += y1
tsum += y1 + y2
y1 = y2
- 11. 5 串列(二)
數值積分法 (五)
61
rsum *= h
lsum *= h
usum *= h
tsum *= h/2
isum = 1 + pylab.sqrt(2) # 正確解
print( "數學積分 :" , round(isum,9) , end="nn" )
print( "迴圈求積:" )
print( "矩形積分 :" , round(usum,9) , " 誤差:" , round(abs(isum-rsum),10) )
print( "上矩形積分 :" , round(usum,9) , " 誤差:" , round(abs(isum-usum),10) )
print( "下矩形積分 :" , round(lsum,9) , " 誤差:" , round(abs(isum-lsum),10) )
print( "梯形積分法 :" , round(tsum,9) , " 誤差:" , round(abs(isum-tsum),10) )
print()
# 公式計算:矩形、梯形、Simpson
# 矩形法係數:1,1,1,1,...,1,1
isum1 = h * sum( ys[:-1] )
# 梯形法係數:1,2,2,2,...,2,2,1
isum2 = h * sum( [ ys[0] , 2*sum(ys[1:-1]) , ys[-1] ] ) / 2
# Simpson 1/3 rule 係數:1,4,2,4,2,...,2,4,1
isum3 = h * sum([ ys[0], 4*sum(ys[1:-1:2]), 2*sum(ys[2:-1:2]), ys[-1] ]) / 3
- 12. 5 串列(二)
數值積分法 (六)
62
print( "公式求積:" )
print( "矩形積分法 :" , round(isum1,9) , " 誤差:" , round(abs(isum-isum1),10) )
print( "梯形積分法 :" , round(isum2,9) , " 誤差:" , round(abs(isum-isum2),10) )
print( "Simpson積分:" , round(isum3,9) , " 誤差:" , round(abs(isum-isum3),10) )
程式輸出:
數學積分 : 2.414213562
迴圈求積:
矩形積分 : 2.435641493 誤差: 0.0118926641
上矩形積分 : 2.435641493 誤差: 0.0214279302
下矩形積分 : 2.392562249 誤差: 0.0216513135
梯形積分法 : 2.414101871 誤差: 0.0001116917
公式求積:
矩形積分法 : 2.402320898 誤差: 0.0118926641
梯形積分法 : 2.414101871 誤差: 0.0001116917
Simpson積分: 2.414213567 誤差: 4.1e-09
- 15. 5 串列(二)
旋轉的三角形 (三)
65
import pylab
xs = [ 0 , 1 , 0.5 ]
ys = [ 0 , 0 , pylab.sqrt(3)/2 ]
colors = ”rgbcmyk”
pylab.figure(facecolor=’w’)
pi = pylab.pi
# 六個旋轉三角形
for k in range(6) :
cosk = pylab.cos(k*pi/3)
sink = pylab.sin(k*pi/3)
# 大三角形
px = [ *xs[:] , xs[0] ]
py = [ *ys[:] , ys[0] ]
# 先旋轉大三角形到相關位置
for i in range(len(px)) :
px[i],py[i] = cosk*px[i] - sink*py[i] ,
sink*px[i] + cosk*py[i]
qx , qy = px[:] , py[:]
r = 0.9 if k%2==0 else 0.1
# 再對每個大三角形內部旋轉 25 次
for i in range(25) :
pylab.plot(px,py,colors[k])
for j in range(3) :
qx[j] = r * px[j] + (1-r) * px[j+1]
qy[j] = r * py[j] + (1-r) * py[j+1]
qx[-1] = qx[0]
qy[-1] = qy[0]
px , py = qx[:] , qy[:]
pylab.axis(’off’)
pylab.show()
- 17. 5 串列(二)
畫出來的數字 (二)
67
import pylab
bmap = ( (15,9,9,9,15), (2,2,2,2,2), (15,1,15,8,15), (15,1,15,1,15),
(9,9,15,1,1), (15,8,15,1,15), (15,8,15,9,15), (15,9,1,1,1),
(15,9,15,9,15), (15,9,15,1,15) )
#每個點矩陣的橫列數與直行數
R , C = len(bmap[0]) , 4
# 方框長度
s = 2
# 每一點所構成的方格座標
xs = pylab.array( [ 0 , s , s , 0 , 0 ] )
ys = pylab.array( [ 0 , 0 , s , s , 0 ] )
while True :
# 輸入數字
num = input("> ")
pylab.figure(facecolor=’w’)
# 每一列:由上到下
for r in range(R) :
- 18. 5 串列(二)
畫出來的數字 (三)
68
# 每一個數字
for k in range(len(num)) :
n = int(num[k])
# 每一行:由右向左
for c in range(C-1,-1,-1) :
# 如果點存在
if bmap[n][r] & ( 1 << c ) :
# 將方格座標移到點的所在位置
xs2 = xs + ( k*(C+1) + C-1-c ) * s
ys2 = ys + ( R-1-r ) * s
# 在 xs2 , ys2 方格以 red 填滿
pylab.fill(xs2,ys2,color=’r’)
pylab.axis(’off’)
pylab.show()
- 19. 5 串列(二)
階梯函式 (一):使用 Fourier Series 逼近
69
數學上一些上下振盪的不連續階梯函式可使用 sin(x)、sin(2x)、
sin(3x)、 · · · 、 cos(x)、cos(2x)、 cos(3x)、 · · · 的線
性組合來逼近,其中最常使用的線性組合為傅立葉級數(Fourier Series)。
例如下圖的階梯函數(實線)可透過傅立葉級數(虛線)來逼近。本範例即是
計算階梯函數與傅立葉級數的差距並以圖形顯示出來。
- 21. 5 串列(二)
階梯函式 (三):
71
上圖的階梯函數可用以下 n 項傅立葉級數來逼近:
程式執行時需輸入傅立葉級數的計算項數 n 用來計算各點的
逼近值,然後將所有的逼近值連同階梯函式畫在圖紙上。本題特別
利用 subplots 函式產生共用 X 軸的圖形來呈現計算結果。上
圖為使用 10 項所產生的傅立葉級數圖形與階梯函數,下圖用來突
顯傅立葉級數在逼近階梯函式時所產生的上下振盪現象。
- 22. 5 串列(二)
階梯函式 (四):
72
import pylab
# 區分圖形為上下兩圖,共用 x 軸
f , gs = pylab.subplots(2, sharex=True,facecolor=’w’)
# hspace 設定縱向兩圖間的間距(平均縱軸高度的比例)
# wspace 設定橫向兩圖間的間距(平均橫軸寬度的比例)
pylab.subplots_adjust(hspace=0.4,wspace=0)
n = int( input("> 傅立葉項數 : ") )
a , b , np = 0 , 2 , 401
xs , dx = pylab.linspace(a,b,np,retstep=True)
# ss:階梯函數 ys:傅立葉串列
ss = [ 1 if x<=1 else 0 for x in xs ]
ys = [ None ] * np
# 計算傅立葉 n 項的估算值
for i , x in enumerate(xs) :
y , pix = 0 , pylab.pi * x
for j in range(n) : y += pylab.sin((2*j+1)*pix)/(2*j+1)
ys[i] = 0.5 + 2*y/pylab.pi
- 23. 5 串列(二)
階梯函式 (五):
73
# 上圖
gs[0].plot(xs,ys,’:r’,label="Fourier series",lw=2)
gs[0].plot(xs,ss,’k’,label="step function",lw=1)
gs[0].grid()
gs[0].set_xlabel("X")
gs[0].set_ylabel("Y")
gs[0].legend()
gs[0].set_title("Step function vs Fourier series with " + str(n) + " terms")
# 下圖
gs[1].fill_between(xs,ys,ss,color=’r’)
gs[1].grid()
gs[1].set_xlabel("X")
gs[1].set_ylabel("Y")
gs[1].set_title("Error of Fourier Series")
pylab.show()