edited by 陳柏因

※這個網站是提供給有修中興普通物理學的學生,學習python2.7.10版的參考資料使用。我會盡量把每次上課的內容都整理在這個網誌裡。上課我會用PPT和個人網頁搭配使用。這算是我第一次教課,上課的速度如果太快或是聽不懂我在說什麼的話,請多包涵,之後也可以跟我反應。課堂外要聯絡我就用facebook吧~
學python的理由,我想老師大概上課都說明了。對我而言因為我不必進行系統的維護,所以python大概就只是一個可以幫我做運算、整理數據或是做圖之類的工具。這個課大概只有6週的時間,所以只會說明一些基本的東西和程式碼編寫的概念,後面各位要製作專題的時候,應該會用到不少課堂上沒教過的指令,這些可能就要自己上網查詢怎麼使用或是問助教。
成績分配與作業繳交
	- 作業10分和期末專題5分,共佔總成績15分
    
- 作業繳交狀況連結,如果有誤跟助教聯絡
    
- 程式執行的時間不得大於60秒,依情況做調整
	
- 作業請在隔週上課之前交,交上程式碼.py檔,至助教陳師漢的信箱(citrus2717@gmail.com)。
	
- 作業的檔案命名為410XXXXXXX(自己學號)+姓名-?.py,如「4102054036陳柏因-1.py」
	
- 郵件的主旨請打python_作業?,「python_作業一」
    
- 違反以上幾點造成助教不便者,後果自負
做作業的注意事項
基本上作業有問題都可以找助教討論,但請盡量不要直接說不會,或是甩一大串程式碼過來,然後說「程式怎麼跑不動?」,這樣我們會不知道怎麼引導各位思考。建議是還不習慣編寫程式的人可以使用附註的功能,標示自己到底寫了什麼,想一下為什麼程式是如何運行的。如果這樣還是不行的話,請盡快聯絡助教,幫你們debug。
計算程式執行的時間
由於第一次作業有部分的人製作出來的程式碼執行的時間過久,造成助教閱讀上的負擔。因此這邊教一下如何使用計算程式執行時間的function。如果想了解內部怎麼運作可以自己google或找助教。請以後交上來的功課都要附上程式運行的時間,輸出的時間不得大於60,會依情況做調整。
import time
tStart=time.time()
待測時間的程式碼
tEnd=time.time()
print 'total time='+str(tEnd-tStart)
只要最後輸出的 total time 小於60即可。
	
	
		- 進入Python(x,y)
		
- 點選右上角的 進入下載頁面 進入下載頁面
- 點選Python(x,y)-2.7.10.0.exe下載安裝檔
- 開啟安裝檔後,會進入Choose Users的介面,選擇Install just for me,接著點選Next
		
- 進入Choose Components的介面後,將Select the type of install那一欄改為Full,點Next就開始安裝
		
- 安裝完桌面應該會有 ,點開它 ,點開它
- 開啟後會出現如下圖的視窗,選擇紅黑圖案的鈕進入python
 
	
		- 概念一
程式在運行的時候,進行的是數學上的運算,因此想要利用變數來協助編寫程式碼的時候,需要指定一個數值給變數。指定的方式就是利用「=」這個符號。範例如下
		a=2
		範例代表的意思即「把2這個值指定給a這個變數」。另一方面,可以利用另一個變數來與這個變數a做連結,例如b=a。此時b和a會被程式視作一體的,當b的數值更動時就會影響a,反之亦然。
		- 概念二
利用概念一,可以衍伸出類似加減乘除的計算方式。範例如下
		a=a+3
		意思是「a+3得到的值指定給a這個變數」。也就是說,假如原本a的值為2,那麼加上3之後就會得到5。程式把5這個值指定給a這個變數。
	
    
    
    標示附註的文字大多是給programer看的,可作為筆記使用,電腦在運行程式碼的時候不會去讀取。附註的類型有兩種,一種是只附註一行的「#」,以及附註多行的「'''」,使用方法如下:
#附註一行
    
'''
附註多行
多行
'''
	
	儲存資料有很多種方式,包含數字、數列、字串等等。不同類型的資料有時候不能直接做疊加或運算,需要將待處理的資料轉換成相同的類型,才能一起運算。資料之間的類型轉換方式如int(1.1),就能把原本是浮點數的1.1轉換成整數形式的1。
	以下我整理一下比較常用的幾種type。
	
		- 數字
數字分為int(integer整數)、float(floating-point number浮點數)和complex(complex number複數)。這邊因為複數沒辦法對應相應的實數,所以先主要討論整數和浮點數的部分。
		我以10為例,整數形式int(10)為10,而浮點數形式float(10)為10.0,接著以10.6為例,整數形式int(10.6)為10而浮點數float(10.6)為10.6。初學者容易犯的錯就是把兩個整數做運算後,得出的結果並非自己所想的。範例如下:
		程式碼
		print 3/6
		顯示為
		0
		一般而言雖然我們知道3/6等於0.5,但在這邊程式認知你的運算都是整數的類型,因此他把答案0.5取整數,就是程式所顯示的0了。解決的方法很簡單,只要把運算改成3.0/6或3/6.0或是3.0/6.0就能顯示0.5了。
		- 數列與矩陣
可以處理數列的形式分為list和array這兩種,用np.arange叫出來的就是array的形式。這兩者都是儲存多個資料的方式,建立list的方法只要打上a=[0,1,2]或是a=range(3)就是一個list了!另一方面,array的話,幾乎用numpy建立出來的數列或矩陣都是這個形式的資料類型。
		- 字串string
字串就是用來儲存文字資料的一種資料形式,使用方法只要在要儲存的文字兩側放上「'」或是「”」即可,如「'文字'」
		範例: print 'hello world!'
	
for迴圈是拿來執行固定次數的一個迴圈,執行的次數取決於你給的數列的長短。要特別注意的是在python裡使用像是for之類的工具時,必須使用縮排而非特殊字元或關鍵字來決定開始與結束。預設是用4個空白鍵(或是按一下Tab鍵),來決定其內容包含哪些部分。範例如下:
	程式碼
	a=[1,3,5,7,9]
	for i in a:
	    print i
	    print 'next'
	輸出為
	1
	next
	3
	next
	5
	next
	7
	next
	9
	next
	
    
    	- def function
        function是一個能自己設定好功能,便於重複使用的一個工具。如果在程式碼中有一很常用到的計算式的話,也能自己利用def function,讓整個程式碼看起來更簡潔。以下為範例
        
def velocity(x1,x2):
	y=x1*x2    
    return y
    
for t in range(5):
    v=velocity(9.8,t)
    print 't='+str(t)+'s','v='+str(v)+'m/s'
		- import library
        在python的世界裡,已經有許多人編寫好的函式可供引入並使用,如SciPy.org這裡面提供的,其中包含各種數學運算或繪圖等不同的功能。以下就來說明如何使用
	
		- 載入函式庫並取代稱
		使用的方式為「import 函式庫名稱 as 代稱」這樣的形式
		範例:
 import numpy as np
		打上如上列式子的程式碼後,就完成引入函式庫的動作了。此時當我們要引用函式庫numpy中的函式pi的功能時,只要打np.pi。當然覺得取代稱很麻煩也可以直接打import numpy就好,而引入函式的形式就變為numpy.pi。
		- 自動尋找函式位在哪個函式庫中
		這個引入函式庫的方式比較偷懶。表示的方式是
		「from 函式庫 import *」
		範例:from numpy import *
		這個指令會自動幫你搜尋你打的函式在哪個函式庫中,也就是本來要打np.pi的情況,你只要打pi就可以直接使用。這雖然對編寫者很方便,但會讓電腦多花一些時間尋找你的函式在哪個函式庫,程式執行的速度相比直接告訴他位置會來的慢。
		- 直接從函式庫中引入函式
這種引入方式就是直接從函式庫中引入所需的函式,可以簡單的只打pi就直接使用pi這個函式,也不會造成像上一種方法一樣,必須讓電腦去找函式在哪個函式庫中,表示的形式如下
		from numpy import pi,sin
		a=sin(pi/2)
        
	
    - numpy
			| 功能 | 範例 | 
			| 全零數列 | np.zeros(2) | 
			| 全一數列 | np.ones(3) | 
			| 全零矩陣 | np.zeros((2,2)) | 
			| 呼叫矩陣中的元素 | a[0,0] | 
			| 呼叫所有行 | a[:,0] | 
			| 呼叫所有列 | a[0,:] | 
			| 開根號 | np.sqrt(5) | 
			| 新增元素 | np.append(a,0) | 
            
			| 重排 | a.reshape((3,2)) | 
            | π | np.pi | 
    
    
		- 產生隨機數
import random as rd
		r=rd.random()
		- 洗牌
import random as rd
		a=range(10)
		rd.shuffle(a)
		- 隨機矩陣
import numpy.random as nrd
		r=nrd.random((5,5))
	
判斷條件是通過判斷一個或多個語句的結果,並輸出True或False。判斷式中大概包含等於(==)、不等於(!=)、大於(>)、小於(<)和大於等於(>=)等種類。
	當符合判斷式條件的時候,Python會給出True的回應,反之則為False。大家可以用以下程式碼進行嘗試
	程式碼
	print 1==1
	print 1!=1
	輸出為
	True
	False
	另外如果有多個條件的話,則可以使用聯集(or)或是交集(and)的概念,範例如下
	print 1==1 or 1!=1
	結果為
	True
    
    
    條件語句指的是假設一些條件,符合條件(True)就執行code,不符合(False)就不執行。設定的條件可以不只一個,以下做個範例:
	程式碼
a=[0,1,5,6,4,2]
for i in a:
    if i<2:
        print str(i)+'<2'
    elif i<4:
        print str(i)+'<4'
    else:
        print str(i)+' other'
	這邊特別注意到其實在a數列中1是同時符合i<2以及i<4的條件,但是這邊程式只顯示符合i<2的結果。這是因為我們設定i<4這個條件是用elif,也就是else if,也就是不符合先前條件且符合elif設定的條件的時候就執行code,如果前面條件符合的話就不執行。而如果有要顯示「1 matches condition2」的話,就把elif i<4:改為if i<4:就行了。
	
    while語句使用上有點像條件語句結合for迴圈。以下為範例
	程式碼
	i=0
	while i<3:
	    print i
	    i=i+1 
	輸出為
	0
	1
	2
	這組code表示的是當條件i<3符合的時候,就執行print 和i=i+1,一直執行到條件不符合i<3的時候就跳出while之外,執行下一步程式碼。
    
    使用者輸入這個指令可以依據使用者輸入的資料,讓程式進行相應的動作,其中指令分為兩類,一個是輸入數字或字串等的input(),一個是將所有輸入值轉為字串的raw_input(),使用範例如下:
	
		- input程式碼
a=input('type anything: ')
		print a,type(a)
		使用方法及結果如圖
		
		點擊綠色框框的區塊,使用者就能給a一個數值(int, float...)或是字串(string)了
		另一方面,使用指令raw_input的話,不管輸入的東西是哪一種,他都會將這些東西轉成字串。範例如下:
		- raw_input程式碼
a=raw_input('type anything: ')
		print a,type(a)
		輸入
 
		4
		輸出為
		4 <type 'str'>
	
time是一個提供各種與時間相關的function(函式)的library(函式庫),可用來記錄程式碼執行的時間,輸出的數字為以秒當作單位。
        - 用clock function計時
clock這個函式紀錄的是從程式開始運行後經過的時間,輸出的是以秒為單位,type為float的資料,以下為使用範例
            import time as t
            tstart=t.clock()
            要執行的程式碼
            tend=t.clock()
            print 'The program takes '+str(tend-tstart)+' seconds.'
        - 用time function計時
使用time來計時基本上和使用clock相同。不同的點在於function定義0s的時間。clock定義程式開始執行的時候為0s,time則定義該版本(本版本為4.9.10的python)發布的時間為0s。
- Homework
製作一款在30秒內,猜一個小於100的數字的遊戲。
    功能包含:
    
    	- 計時30s。超時程式就結束
        
- 產生隨機數給玩家猜
        
- 猜到數後,程式就會結束
    
hint1:
t=time.clock()可計錄程式執行的時間
n=random.randrange(1,100,1)會隨機產生一個在一到一百之間,以一為公差的數
break能中止while的進行
hint2(可用可不用):
關於input括號內的字,由於預設編碼的關係,只能顯示英文字,如果使用中文的話會顯示亂碼。
因此如果想要使用中文的話,可以在字串旁篇加上一個u,將編碼切換成能支援中文的編碼─unicode。
例如:
a=input(u'哈囉')
hint3(可加可不加):
程式執行完畢或是出現錯誤的時候,py檔通常是直接關閉了!
因此如果想保留畫面顯示遊戲結果的話,可以利用以下指令
q=0
while q==0:
    q=raw_input('Input any key to quit: ')
    
    
		- 矩陣運算
矩陣的運算有兩種指令來運算,一種是把直接建立矩陣來運算,範例如下
		import numpy as np
		a=np.matrix([[1,2],[3,4]])
			
		b=np.matrix([[1,2],[3,4]])
		c=a*b
		print c
		另一種方式是建立一個array後再用dot的指令進行矩陣運算。範例如下
		import numpy as np
		x=np.array([[1,2],[3,4]])
		y=np.array([[1,2],[3,4]])
		z1=x*y
		z2=np.dot(x,y)
		print 'z1='
		print z1
		print 'z2='
		print z2
		由範例的結果z1和z2我們可以觀察到,前者不使用dot的指令進行相乘的話,僅只有對應位置的元素相乘,而非我們所理解的矩陣乘法。當如後者使用dot的指令進行運算的話則為一般的矩陣運算。
		
		取行列式值
		np.linalg.det(a)
		取反矩陣
		np.linalg.inv(a)
        全零矩陣
        np.zeros((2,2))
        全一矩陣
        np.ones((2,2))
        對角矩陣
        np.eye(2)
        呼叫所有行
        a[:,0]
        呼叫所有列
        a[0,:]
	
靜圖的函式庫主要是使用matplotlib這個函式庫。首先將函式庫引入
	import matplotlib.pyplot as plt
	接著如果要畫一個2D圖的話,我們需要先建立兩個長度相同的數列,如
	x=[1,2,3,4]
	y=[4,5,2,3]
	這兩個數列代表著2D圖上的每一個x座標與y座標,跟excel畫圖有類似。畫圖方式如下
	plt.plot(x,y,'-o',label="note")
	plt.legend()
	plt.show()
	第一條指令裡的第一和第二個元素代表x座標與y座標,第三個元素代表畫線的方式是用圓圈標示點並用線相連,第四個元素代表這條線的標籤(圖中右上角那個框框中的東西)。
	而為了要在圖中建立右上角那個框框,我們需要使用第二條指令來建立
	plt.xlabel('xlabel')
	plt.ylabel('ylabel')
	這樣你在括號中打的字串就會出現在圖中的座標軸旁邊了!
	靜圖相關函式
	
		- plt.plot()的可添加功能
			| 功能 | 範例 | 
			| 連結資料點的圖案 | '-o' | 
			| 資料線或點的顏色 | color=[0.2,0.3,0.4] | 
			| 資料標籤 | label='string' | 
		- 標示坐標軸
plt.xlabel('string')
		plt.ylabel('string')
		- 分割視窗
假設要畫六張圖,分布為上下兩張圖左右三張圖的排列方式: ( 2 x 3 )
		
		如果要畫第(5)張圖的位置輸入plt.subplot(235)
        - 儲存圖片
plt.savefig('檔案名.png')
範例:解  -cv-kx=ma,阻尼力+彈力=合力
    v= x'= x對時間一次微分項,a= x''= x對時間二次微分項
    使用上首先就是給定初始條件。例如說如果解到二次微分項的話,一次和零次微分項的初始值。接著準備一個function,輸入的值包含初始條件以及要計算的時間範圍,要回傳的值則是一次微分以及二次微分項。最後開始計算時,先令一個時間範圍,使用odeint()這個function,括號內依序置入剛剛設定的function、初始條件以及時間。
    
import scipy.integrate as sci
import numpy as np
import matplotlib.pyplot as plt
m=5 #(kg)
k=2 #(N/m)
c=1.5 #(Ns/m)
ic=[0,5] #initial condition=[x0(m),v0(m/s)]
def fun(inp,t):
    #x0=inp[0],v0=inp[1]
    out=[inp[1],(-c*inp[1]-k*inp[0])/m]  #out=[x',x'']
    return out
t=np.linspace(0.0,30.0,101) #(second)
x=sci.odeint(fun,ic,t)  #sci.odeint(function,intitial condition,time)
plt.plot(t,x[:,0]) #x-t graph
    
    最後輸出的x會是一個2 X n的陣列。第0行x[:,0]代表的是每個時間點的位置,第1行x[:,1]則代表的是每個時間點的速度。
要學習Vpython要怎麼用的話,基本上可以參考Vpython、Vpython Help和VPhysics NTU這些網站就好。裡面也沒什麼太難的技巧,就只是在將各種參數輸入模型裡就行。
參數表
from visual import *
'''
ball=sphere()
ball.color=color.red
ball.pos=(-5,0,0)
ball.radius=0.5
'''
'''
wall = box()
wall.color = color.green
wall.size = (0.2,4,4)
wall.pos=(6,0,0)
'''
'''
v1 = vector(1,2,3)
v2 = vector(2,1,5)
print v1.x,v1.y,v1.z
'''
'''
ball=sphere()
ball.pos=(-5,0,0)
ball.radius=0.5
wall1 = box()
wall1.color = color.green
wall1.size = (0.2,4,4)
wall1.pos=(6,0,0)
wall2 = box()
wall2.color = color.green
wall2.size = (0.2,4,4)
wall2.pos=(-6,0,0)
ball.velocity = vector(2,0,0)
while (1==1):
    rate(100)
    dt = 0.05
    ball.pos = ball.pos + ball.velocity*dt
    if ball.x > wall1.x: 
        ball.velocity.x = -ball.velocity.x
    if ball.x < wall2.x: 
        ball.velocity.x = -ball.velocity.x
'''
#sphere()    
#box() 
#cylinder()
#arrow()
#ellipsoid()
#helix()
#sphere(pos=(-1,0,0),radius=0.5,color=color.green)
#arrow(pos=(-1,0,0),axis=(+1,+3,0),color=color.red)
#c=curve(pos=(1, 0, 0))
#for i in arange(0, 10, 0.1):
#    c.append(pos=(cos(i), sin(i), i))
#c = gcurve()
#for i in arange(0, 10, 0.1):
#    c.plot(pos = (i, cos(i)))
    
  二維碰撞
  
from visual import *
size = 0.1          
scene = display(width=800, height=800,  background=(0.5,0.5,0))             	
b1 = sphere(radius = size,  color=color.yellow)         				
b2 = sphere(radius = size,  color=color.green)          				
b1.pos = vector( 0.6 , 0.1, 0)                        	
b2.pos = vector( 0 , 0 , 0)                           	
b1.v = vector(-0.2, 0, 0)                              	
b2.v = vector(0 , 0, 0)                                	
dt = 0.001                                                          
while True:
    rate(1000)
					
    b1.pos += b1.v * dt 
    b2.pos += b2.v * dt
    if abs(b1.pos - b2.pos)>=2*size and dot(b1.v, b2.v) >= 0 :
        v1prime = b1.v - (b1.pos-b2.pos)  * dot (b1.v-b2.v, b1.pos-b2.pos) / abs(b1.pos-b2.pos)**2
        v2prime = b2.v - (b2.pos-b1.pos)  * dot (b2.v-b1.v, b2.pos-b1.pos) / abs(b2.pos-b1.pos)**2
        b1.v, b2.v = v1prime, v2prime
	
from visual import *
g = 9.8          
size = 0.05         
r = 0.5             
k = 10             
m = 0.2             
'-----------------------------------------------------------------------------'
scene = display(width=800, height=800, center=(0, -r*0.6, 0), background=(0.5,0.5,0))  
ceiling = box(length=0.8, height=0.005, width=0.8, color=color.blue)                   
ball = sphere(radius = size,  color=color.red)                              
spring = helix(radius=0.02)                                
ball.pos0 = vector(0, -r, 0) 					
ball.pos = vector(0, -r , 0)        				
ball.v = vector(0, 0, 0)            					
'------------------------------------------------------------------------------'
dt = 0.001   
while True :
    rate(1000)
    spring.axis = ball.pos - spring.pos 		
    ball.a = vector(0, - g - k * (ball.pos.y - ball.pos0.y )/m , 0)
						
    ball.v +=   ball.a*dt
    ball.pos += ball.v*dt
    
    
    二維碰撞
    
from visual import *
size = 0.1          
scene = display(width=800, height=800,  background=(0.5,0.5,0))             	
b1 = sphere(radius = size,  color=color.yellow)         				
b2 = sphere(radius = size,  color=color.green)          				
b1.pos = vector( 0.6 , 0.1, 0)                        	
b2.pos = vector( 0 , 0 , 0)                           	
b1.v = vector(-0.2, 0, 0)                              	
b2.v = vector(0 , 0, 0)                                	
dt = 0.001                                                          
while True:
    rate(1000)
					
    b1.pos += b1.v * dt 
    b2.pos += b2.v * dt
    if abs(b1.pos - b2.pos)<=2*size and dot(b1.v, b2.v) <= 0 :
        v1prime = b1.v - (b1.pos-b2.pos)  * dot (b1.v-b2.v, b1.pos-b2.pos) / abs(b1.pos-b2.pos)**2
        v2prime = b2.v - (b2.pos-b1.pos)  * dot (b2.v-b1.v, b2.pos-b1.pos) / abs(b2.pos-b1.pos)**2
        b1.v, b2.v = v1prime, v2prime
    
    彈簧運動向量
    
from visual import *
g = 9.8            
size = 0.05       
r = 0.5            
k = 10            
m = 0.2             
scene = display(width=800, height=800, center=(0, -r*0.6, 0), background=(0.5,0.5,0))  
ceiling = box(length=0.8, height=0.005, width=0.8, color=color.blue)                    
ball = sphere(radius = size,  color=color.red)                             
spring = helix(radius=0.02)                               
ball.pos0 = vector(0, -r, 0) 					
ball.pos = vector(0, -r , 0)        					
ball.v = vector(0, 0, 0)            					
ball_v_arrow = arrow(shaftwidth = 0.01)
ball_a_arrow = arrow(shaftwidth = 0.01)  #軸
dt = 0.001   
while True:
    rate(100)
    spring.axis = ball.pos - spring.pos 		
    ball.a = vector(0, - g - k * (ball.pos.y - ball.pos0.y )/m , 0)
						
    ball.v +=   ball.a*dt
    ball.pos += ball.v*dt
    ball_v_arrow.pos = ball.pos + vector(size, 0, 0)
    ball_v_arrow.axis = ball.v / 5.0
    ball_a_arrow.pos = ball.pos - vector(size, 0, 0)
    ball_a_arrow.axis = ball.a / 50.0