從零開始學習PYTHON3講義(十一)計算器升級啦

<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>html

(內容須要,本講中再次使用了大量在線公式,若是由於轉帖網站不支持公式沒法顯示的狀況,歡迎訪問原始博客。)python

《從零開始PYTHON3》第十一講

第二講的時候,咱們經過Python的交互模式來入門Python基本知識。當時把Python當成了一個計算器使用。隨後從第三講開始,一直到第十講,咱們進入了編程的方式,而且不斷的深刻,到第九講,咱們已經完成了Python基本語言、語法部分的學習。linux

每一講都有大量的編程練習,估計你們也累了,這一講休息一下,咱們回到把Python當作計算器的狀態。固然內容仍是要更深刻一些,介紹一些經常使用的高級數學運算功能。ajax


Python的標準數學庫

標準庫、內置庫、官方庫這些詞其實說的都是一個意思,就是這個庫來自Python官方開發團隊,隨開發語言一同安裝無需另外下載的庫。編程

第二講的時候咱們已經發現了,Python自己彷佛只能作一些簡單的數學運算,加、減、乘、除、乘方。隨後整數運算還額外有取餘數、整除等幾個特別的運算。實際上Python更復雜的數學運算都在標準數學庫math之中。一直到第九講咱們介紹了「庫」的概念,咱們才能更多的介紹Python更高級的計算能力。windows

如同上一講說到sys庫的那樣,咱們也可使用Python的內部幫助來查看math庫的詳細狀況:app

>>> import math
>>> dir(math)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
>>> help(math)
...   #將有大量詳細的幫助信息,這裏略去

下面咱們介紹一些經常使用的math內置數學函數:函數

函數 功能
math.pi 數學常數π= 3.141592……
math.e 數學常數e = 2.718281….
math.tau 數學常數τ= 6.283185……
math.ceil(x) 返回x的上限,返回最小的整數A (A>=x)。如math.ceil(3.14)返回的整數爲4官網math庫
math.fabs(x) 返回絕對值x。
math.factorial(x) 返回 x!。若是x不是積分或者是負的,就會產生ValueError。
math.floor(x) 返回x的下限,返回一個值最大整數A (A<=x)。如math.floor(3.14)返回的整數爲3
math.exp(x) 返回 ex也就是 math.e ** x
math.pow(x,y) 返回x的y次方,即返回 x ** y
math.sqrt(x) 返回 $$ \sqrt x $$
math.degrees(x) 將角x從弧度轉換成角度。
math.radians(x) 把角x從度轉換成弧度。
math.acos(x) 返回 x 的反餘弦
math.asin(x) 返回 x 的反正弦。
math.atan(x) 返回 x 的反正切。
math.cos(x) 返回 x 的餘弦。
math.sin(x) 返回 x 的正弦。
math.tan(x) 返回 x 的正切。
math.log(x,a) 返回 $$ log;a^x $$,若不提供a參數,默認使用e

有了這些函數的幫助,咱們一下從小學水平上升到了高中:),來看幾個使用的例子:工具

>>> import math	#全部math的函數,使用以前必須引入庫,引入一次便可
>>> math.sin(1)    #1的正弦
0.8414709848078965
>>> math.pi 	#π常量
3.141592653589793
>>> math.sqrt(3) #計算3的平方根
1.7320508075688772
>>>

擴展庫和各類函數的學習,一般不須要你一次都記住,而是用的時候查資料會用便可。經常使用的函數,用的多的天然就記住了。
隨用隨查資料這種形式,不一樣於之前的課堂筆記,通常都是用網頁書籤來記錄下來經常使用的資料地址,這樣才能快速的查詢。好比math庫的部分經常使用函數的中文資料:http://www.javashuo.com/article/p-opuhwcxn-ho.html
中文資料通常都更新慢一些,而且一般不是很完整,官方的英文資料則更快,可是須要你能閱讀英文。立志但願從事信息技術行業的同窗,英語的學習也要同時增強。官方英文資料地址:https://docs.python.org/3/library/math.html學習


第三方數學庫numpy

「第三方」是在計算機行業中很經常使用的概念,指的既不是開發者官方提供的,也不是用戶本身開發的。是由其它組織開發並提供服務的內容。能夠把二者作一個比較:

標準庫 第三方擴展庫
同爲軟件庫,相同的使用方法 同爲軟件庫,相同的使用方法
由PYTHON官方或承認的開發團隊開發維護 一般由世界範圍內許多不一樣公司或組織開發維護
一般只有一個最穩定的版本 同一個功能,可能有不少個團隊的不一樣產品,質量良莠不齊
主要完成經常使用、基本、必備功能 解決各類各樣問題
隨PYTHON安裝,直接就可使用,稱爲標準庫 須要額外安裝,跟不一樣操做系統可能還有兼容性問題,稱爲第三方擴展庫
開發規範、命名習慣基本統一 各自有各自的標準、規範,互相之間有可能習慣差異很大

一般能生存並傳播很普遍的第三方擴展庫都有驚人強大的功能。在享受這些「超級」功能的同時,每一個第三方擴展庫都須要安裝以後才能被Python程序「引用」和「使用」,是第三方擴展庫最大的障礙。
爲此Python發展出了不少擴展庫的管理工具來幫助開發人員安裝、管理、刪除擴展庫。咱們第一講介紹了使用最多的pip管理工具。
使用pip管理工具安裝numpy數學庫的方法以下:

#在Windows中,首先退出當前的Python軟件
#使用管理員模式執行cmd命令行,而後執行以下命令:
pip install numpy
#某些windows系統須要使用pip3
pip3 install numpy

#linux和mac在命令行執行:
sudo pip3 install numpy

使用習慣以後,這樣一行的安裝命令根本不會對你使用擴展庫有什麼影響,並且只須要安裝一次,不換電腦就能夠一直使用。

numpy的使用跟math的使用幾乎是相同的,可是相較於只有50多個預置數學函數的math,numpy包含了600多個。只要跟數學相關的,幾乎全部須要用到的函數和常量都已經有了。咱們舉幾個例子:

#首先使用以前同樣是必須先引用
import numpy as np
    #as np表示引入後使用np的名字來調用,這樣每次均可以少敲幾個字母

np.sin(1)    #正弦函數
 => 0.8414709848078965
np.pi    #π常量
 => 3.141592653589793
np.sqrt(3)  #平方根
 => 1.7320508075688772
np.arccos(0)   #反餘弦函數  
 => 1.5707963267948966

#查看幫助
help(np)       #第一次幫助會從網上獲取,速度比較慢

第九講咱們曾經講過了使用列表類型保存矩陣的方式,惋惜就基本Python的功能來說,也只是能保存而已,想要計算,須要本身使用複雜的循環嵌套來完成。但矩陣運算在numpy是直接內置的,好比: $$ A = \left{ \begin{matrix} 2 & 3 & 4 \ 5 & 6 & 7 \ 8 & 9 & 10 \end{matrix} \right} \times 3\tag{1} $$

咱們直接看numpy的計算方式:

>>> np.array([[2,3,4],[5,6,7],[8,9,10]])*3
array([[ 6,  9, 12],
       [15, 18, 21],
       [24, 27, 30]])

np.array函數,實際是numpy中的列表類型。列表的定義跟標準Python很像,是用嵌套的「[]」完成的。隨後numpy的類型直接就支持矩陣乘法,因此最後「*3」。執行後輸出了矩陣的計算結果。對比的若是使用標準的Python,確定要使用兩個循環嵌套,而後逐項的進行乘法計算。速度會慢不少,編程也複雜不少。

再比較一個例子。第六講中咱們講了range函數,是跟for循環一塊兒介紹的,你們應當不陌生。當時重點說明了range返回的是一個整數的序列類型,那碰到須要使用小數的序列類型的時候怎麼辦呢?一般的辦法只能在循環體中增長一次整數同浮點小數的乘法運算來生成每次循環使用的小數。好比:

step=0.11
r=[]
for i in range(10):
    r.append(i * step)
#結果爲:
[0.0, 0.11, 0.22, 0.33, 0.44, 0.55, 0.66, 0.77, 0.88, 0.99]

而numpy中,直接有支持小數序列的類型:

#Python內置的range函數
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

#numpy中支持小數序列的linspace
>>> np.linspace(1,2,10)
array([1.        , 1.11111111, 1.22222222, 1.33333333, 1.44444444,
       1.55555556, 1.66666667, 1.77777778, 1.88888889, 2.        ])

linspace函數的三個參數跟range函數區別比較大,須要注意:第一個參數是指起始數值;第二個參數是指結束數值,注意這裏會包含結束數值,而range中是不包含結束數值;第三個參數是指從開始到結束,分爲多少份,也就是最後序列的長度。

咱們至今所看到的Python數學計算,都屬於數值計算的範疇。所謂「數值計算」就是指無論計算過程多麼複雜,最終以數值的形式得出計算結果。
數值計算在實際應用中使用的最多,但缺陷也比較明顯。好比從上面linspace的例子就能看出來,看起來所生成的浮點小數序列,並非很整齊,幾乎能夠肯定有被省略的部分。計算機內部的存儲是2進制數,咱們日常習慣的計算方式是10進制數,二者之間的轉換會有偏差,無理數的屢次截取也會形成偏差。咱們能夠再舉一個更明顯的例子:

import math
math.sqrt(8)
結果:2.8284271247461903
math.sqrt(8)*math.sqrt(8)
結果:8.000000000000002

上例中,由於對8開平方的時候數據作了截取,相乘計算回平方值以後,沒法作到精確的得出8,只是一個很近似的值。這在存在大量計算而精度又要求比較高的狀況下,仔細的考慮化簡的時機和計算的方法將會耗費大量的精力。
這種狀況在第三方的numpy數學庫中一樣是存在的:

import numpy as np
np.sqrt(8)
結果:2.8284271247461903
np.sqrt(8)*np.sqrt(8)
結果:8.000000000000002

實際上只要是使用數值計算就會出現這種狀況,尚沒法避免。
爲了應對這種方式,在數學中大量採用了符號計算。咱們目前數學課上學到的方程式、多項式基本都屬於這個範疇。每每並不須要求出最終的計算結果。化簡到一些包含簡單符號和算式的結果就能夠知足應用。所以符號計算在科研、工程領域都有普遍應用。

Python有一個第三方的符號計算擴展庫,名爲sympy。安裝方式爲(之後的安裝介紹均以windows爲例,再也不介紹linux及mac,相信參考windows的方法,在linux和mac安裝都不該當有問題):

#首先使用管理員模式打開cmd命令行,而後執行:
pip install sympy  
#實際上pip工具能夠同時安裝多個擴展庫,好比:
pip install numpy sympy

相對熟悉的數值計算,sympy符號計算庫理解起來會難一些。Sympy試圖創建一整套運算體系,對每次的結果進行符號計算,盡力保持計算的精確度。試圖創建一整套體系的緣由是這樣:在Python中,加、減、乘、除包括等號等等全部字符,基本都已經有了默認的功能,好比一般的數學數值計算。
咱們前面也講過了,這些符號自己屬於保留字的一種,是不能被咱們用於其它用途的。所以在不會歧義的位置,會繼續使用原有計算符和函數,有歧義的位置,須要使用Sympy本身的函數,好比分數函數Rational(稍後會有講解)。
只要算式會被化簡從而成爲小數的狀況,都應當考慮使用Sympy本身的函數,一般都是分數、除法、數學函數的位置,不然就等於使用了原有的數值計算,可能致使精度下降。

sympy的使用方法,先來看一個例子:

#使用內置的數學庫
import math
math.sqrt(8)
結果:2.8284271247461903

3*5*math.sin(7) #numpy.sin(7)也是相同的
結果:9.854798980781837

#下面使用sympy
import sympy
sympy.sqrt(8)
結果:2*sqrt(2)   #注意結果的樣式

3*5*sympy.sin(7)
結果:15*sin(7)   #注意結果

乍看起來,sympy的結果彷佛很怪異。其實若是把計算機函數翻譯爲數學函數,這個結果很是相似咱們學習數學的時候公式化間的結果。繼續看示例:

import sympy

#平方根
sympy.sqrt(8)
結果:2*sqrt(2)

sympy.sqrt(8)*sympy.sqrt(8)
結果:8

2*sympy.sqrt(2)
結果:2*sqrt(2)

#正弦函數
sympy.sin(1)
結果:sin(1)

#π常數
sympy.pi
結果:pi

#分數
sympy.Rational(1,2)
結果:1/2

注意上面的計算結果,都沒有前綴的sympy,而是直接的sin/sqrt這樣的結果。這說明,其實sympy使用的時候,最好使用from sympy import *,還記得嗎?這至關於從sympy把全部可用資源都導入到了當前文件做用域,所以調用的時候能夠徹底省略sympy前綴。
繼續說符號計算。上面使用的例子,你會發現使用符號計算的方法,由於可能會變成無理數的部分都使用了符號或者公式來表達了。因此兩個平方根相乘這樣的運算,是能夠精確還原到原始值的。

既然是符號計算,直接使用符號量在數學表達式中也是頗有特點的功能:

#符號聲明
#在第二講說變量的時候,
#咱們特別說明變量是「已知數」
#這裏建立的符號變量,其實就是
#表明數學公式中的未知數
#固然最後這個未知數,仍是使用Python變量來表示的,
#sympy.Symbol就是一個sympy庫中的類型。
x = sympy.Symbol('x')   #定義未知數x
y = sympy.Symbol('y')	#未知數y
m,n,z = sympy.symbols('m n z') #同時定義多個未知數

#如下是使用定義的未知數,進行帶未知數的數學符號計算
m*x*3+8
結果:3*m*x + 8

(x+y)*3
結果:3*x + 3*y

再強調一下,在sympy中定義的未知數類型,變量的確是Python的變量。所表明的含義但是sympy符號計算中的未知數,而不是咱們常見的Python變量。


挑戰

下面咱們利用強大的符號計算來進行一個多項式的化簡: $$ (x + (2xy)^\frac{1}{2}+y)(x - (2xy)^\frac{1}{2}+y) $$
建議你本身動手化簡一下,雖然咱們不是數學課,但數學技能仍是很重要的。 隨後你應當能獲得正確答案:
$$ = x^2 + y^2 $$
上面是手工來化簡的結果。下面到了讓sympy上場的時間了:

#引入擴展庫
from sympy import *

#定義x/y兩個符號
x,y = symbols("x y")

#化簡函數simplify()
simplify((x+(2*x*y)**Rational(1, 2)+y)*(x-(2*x*y)**Rational(1, 2)+y))
#執行結果
 x**2 + y**2

其實同第二章同樣,這一章的難度,一樣是要用Python語言來描述數學公式。上例中的simplify函數式sympy中的一個函數,表示把參數當作數學表達式,而後進行化簡操做。加法、乘法、乘方都不會形成小數,也沒有語法上的歧義,因此直接使用了標準的數學運算符。1/2這種除法會有可能致使小數,從而有二進制到十進制轉換的偏差風險;而且1/2會直接使用數值計算,會致使算式過快的求值,致使最後化簡失敗,因此這裏使用sympy內置的分數函數Rational,這個函數有兩個參數,分別表明分子和分母。
通過這些解釋,你是否是能看懂了?最後看化簡的結果,跟咱們手工的過程如出一轍。這些新的函數,但願你本身給本身找一些算式多練習,才能更快的掌握。


解方程

解方程在數學中簡直佔了半壁江山啊。咱們仍然從第二講的老例子開始:

甲、乙兩人相距36公里,相向而行,若是甲比乙先走2小時,那麼他們在乙出發2.5小時後相遇;若是乙比甲先走2小時,那麼他們在甲出發3小時後相遇,甲、乙兩人每小時各走多少公里?(假設甲乙的速度均勻穩定)

但願你還記得原來的結題過程,我直接列出來吧,畢竟咱們要學習的是Python不是數學:

  • 假設甲的速度爲x公里/小時,假設乙的速度爲y公里/小時
  • 列方程式(2.5+2)x+2.5y=36,3x+(3+2)y=36
  • 根據方程2推導爲:x=(36-5y)/3,代入方程1
  • y=(124.5-36)/(4.55/3-2.5)
  • 最後得:y=3.6,x=6

解方程首先的問題是,「= 」已經被用做了賦值操做,跟前面/的緣由同樣,不能直接用來描述等式。否則Python會直接報錯。

sympy定義了sympy.Eq()函數來描述等式,以上面的兩個方程爲例,能夠寫成這個樣子:sympy.Eq((2.5+2) * x+2.5 * y,36)sympy.Eq(3 * x+(3+2) * y,36)。逗號隔開的,就是等式兩端。其它的注意事項,跟上面「化簡」的時候講的同樣。下面看看咱們解方程的過程:

#引入擴展庫
from sympy import *

#定義兩個未知數符號
x=Symbol('x')
y=Symbol('y')

#定義兩個等式
a = Eq((2.5+2)*x+2.5*y,36)
b = Eq(3*x+(3+2)*y,36)

#使用sympy.solve函數解方程組
solve([a,b],[x,y])

#運行結果:
{x: 6.00000000000000, y: 3.60000000000000}

嗯,說不編程序了,實際最後仍是編了,好在比較簡單:)
程序中定義未知數符號、描述等式,重點是使用了sympy.solve函數來解方程。函數接受兩個參數,兩個參數都是列表。第一個列表中是方程式(等式),第二個列表是要求解的未知數。

咱們再把程序簡化一下:

#引入擴展庫
from sympy import *

#在一行中直接定義兩個未知數符號
x,y = symbols("x y")

#使用sympy.solve函數解方程組
solve([Eq((2.5+2)*x+2.5*y,36),Eq(3*x+(3+2)*y,36)],[x,y])

結果:{x: 6.00000000000000, y: 3.60000000000000}

這樣看起來更清楚了。有沒有以爲sympy符號計算很強大?


練習時間

  1. 使用symbol解方程組:
    $$ \begin{equation} \begin{cases} 2x-y=5 \ 3x+4y=2 \end{cases} \end{equation} $$

  2. 化簡表達式:
    $$ \frac {\sin(60+\theta)+\cos(120)\sin(\theta)}{ cos(\theta)} $$
    這道題有一些提示:

    $$ \theta $$ 在Python中很難輸入,可使用一個未知數x代替,不影響結果。

    Python的數學庫只接受$$\pi$$角度,也既咱們習慣的180度,因此題目中的60度能夠表示爲$$\pi/3$$;120度則表示爲$$\pi/3*2$$。

    式子中的分子、分母由於都有未知數,不會引發即時計算影響計算結果,也不會有歧義,因此就是用「/」計算符便可,不用使用Rational函數。

做爲一門編程課,本講並未提供太多的數學算式來幫助你記憶新學的數學函數。建議你從本身的數學學習中尋找一些算式來多作一些練習。


本講小結

  • 複雜的數學計算是理工科必備的基本能力,也是最頻繁須要的
  • 數學計算在計算機應用中,分爲求得結果的數值計算及公式化簡爲主的符號計算,各有用途,都很重要
  • Python在多種擴展庫的幫助下有強大的計算能力
  • 本講的重點是公式化簡、解方程,要學會使用語言中的運算符、函數等來描述數學公式

學習資源

numpy: https://docs.scipy.org/doc/numpy/user/quickstart.html sympy: http://docs.sympy.org/latest/tutorial/index.html


練習答案

  1. solve([Eq(2*x-y,5),Eq(3*x+4*y,2)])
  2. simplify((sin(pi/3+x)+cos(pi/3*2)*sin(x))/cos(x))

最終結果請在Python中試着運行看看,別忘了引入sympy庫。

相關文章
相關標籤/搜索