2022年8月25日 星期四

Python 學習筆記 : 數學模組 math

Python 標準函式庫中的 math 模組提供大部分科學運算所需之常數與函式, 不過它只支援純量運算, 不支援向量運算, 向量運算須使用第三方套件 Numpy. 

本篇測試參考了如下書籍 :
  1. Python 零基礎入門班 (碁峰 2018, 文淵閣工作室)
  2. Python 函式庫語法範例字典 (旗標 2019)
  3. Python 程式設計學習經典 (碁峰 2018)
  4. The Python 3 Standard Library by Examples (Edison Wesley 2017)
math 模組教學文件參考 :


Python 內建數值處理相關函式參考 :


首先來檢視 random 模組的公開成員, 以下使用一個自訂模組 members, 其 list_members() 函式會列出模組或套件中的公開成員 (即屬性與方法), 參考 : 


math 使用前須先用 import 匯入 : 

>>> import math 
>>> import members   
>>> members.list_members(math)    
acos <class 'builtin_function_or_method'>
acosh <class 'builtin_function_or_method'>
asin <class 'builtin_function_or_method'>
asinh <class 'builtin_function_or_method'>
atan <class 'builtin_function_or_method'>
atan2 <class 'builtin_function_or_method'>
atanh <class 'builtin_function_or_method'>
ceil <class 'builtin_function_or_method'>
copysign <class 'builtin_function_or_method'>
cos <class 'builtin_function_or_method'>
cosh <class 'builtin_function_or_method'>
degrees <class 'builtin_function_or_method'>
e <class 'float'>
erf <class 'builtin_function_or_method'>
erfc <class 'builtin_function_or_method'>
exp <class 'builtin_function_or_method'>
expm1 <class 'builtin_function_or_method'>
fabs <class 'builtin_function_or_method'>
factorial <class 'builtin_function_or_method'>
floor <class 'builtin_function_or_method'>
fmod <class 'builtin_function_or_method'>
frexp <class 'builtin_function_or_method'>
fsum <class 'builtin_function_or_method'>
gamma <class 'builtin_function_or_method'>
gcd <class 'builtin_function_or_method'>
hypot <class 'builtin_function_or_method'>
inf <class 'float'>
isclose <class 'builtin_function_or_method'>
isfinite <class 'builtin_function_or_method'>
isinf <class 'builtin_function_or_method'>
isnan <class 'builtin_function_or_method'>
ldexp <class 'builtin_function_or_method'>
lgamma <class 'builtin_function_or_method'>
log <class 'builtin_function_or_method'>
log10 <class 'builtin_function_or_method'>
log1p <class 'builtin_function_or_method'>
log2 <class 'builtin_function_or_method'>
modf <class 'builtin_function_or_method'>
nan <class 'float'>
pi <class 'float'>
pow <class 'builtin_function_or_method'>
radians <class 'builtin_function_or_method'>
remainder <class 'builtin_function_or_method'>
sin <class 'builtin_function_or_method'>
sinh <class 'builtin_function_or_method'>
sqrt <class 'builtin_function_or_method'>
tan <class 'builtin_function_or_method'>
tanh <class 'builtin_function_or_method'>
tau <class 'float'>
trunc <class 'builtin_function_or_method'>

可見 math 模組的成員包含了 5 個數學常數與許多數學函數. 

 
1. 常數 :   

math 模組內建了五個科學計算中最常用的常數 : 

 math 模組的常數 說明
 e 自然指數=2.718281828459045
 pi 圓周率=3.141592653589793
 tau 圓常數 (圓周與半徑之比, 即 2*pi)=6.283185307179586
 inf 無限大=inf (很大的浮點數)
 nan 非數值=nan (不是數值的一個 float 型態值)

例如 : 

>>> print("math.pi=", math.pi)     
math.pi= 3.141592653589793
>>> print("math.e=", math.pi)        
math.e= 3.141592653589793
>>> print("math.tau=", math.pi)    
math.tau= 3.141592653589793
>>> print("math.nan=", math.nan)    # 非數值
math.nan= nan
>>> print("math.inf=", math.inf)        # 無窮大
math.inf= inf  
>>> math.tau    
6.283185307179586    
>>> math.pi * 2    
6.283185307179586

注意, math.nan 與 math.inf 的資料型態均為 float : 

>>> print("type(math.nan)=", type(math.nan))   
type(math.nan)= <class 'float'>
>>> print("type(math.inf)=", type(math.inf))    
type(math.inf)= <class 'float'>

可以將字串 'nan' 與 'inf' 傳給 float() 函式以產生 nan 與 inf, 例如 :

>>> float('nan')    
nan
>>> float('inf')   
inf

可以用 math.isnan() 函式來檢驗是否為 nan, 例如 :

>>> math.isnan(math.nan)    
True
>>> math.isnan(float('nan'))      
True
>>> math.isinf(math.inf)    
True
>>> math.isinf(float('inf'))   
True

如果要用到負無窮大, 就直接加個負號即可, 即 -math.inf. 


2. 函式 :   

math 模組提供的函式如下表 : 

 math 模組的函式 說明
 fabs(x) 傳回 x 的絕對值
 fsum(x) 傳回 x (可迭代物件, 串列或元組, 集合) 的元素總和
 remainder(x, y) 傳回 x/y 的餘數=x-n*y, n 為最接近商之整數 (Python 3.7 版新增)
 fmod(x, y) 傳回 x/y 的模數, 計算整數模式使用 %, 浮點數模數則用 fmod()
 modf(x) 傳回 x 小數與整數部分組成的 tuple 
 sqrt(x) 傳回 x 的平方根 (x 須為正數)
 pow(x, y) 傳回 x 的 y 次方
 floor(x) 傳回小於或等於 x 的最大整數 (進位函式)
 ceil(x) 傳回大於或等於 x 的最小整數 (捨位函式)
 trunc(x) 傳回 x 的整數部分 (無條件捨棄小數)
 gcd(a, b, c, d, ....) 傳回 a, b, c, d, ... 的最大公因數 
 lcm(a, b, c, d, ....) 傳回 a, b, c, d, ... 的最小公倍數 (Python 3.9 版新增)
 comb(n, k) 傳回從 n 個元素中取出 k 個的組合數=n!/(n-k)!, 3.8 版後
 perm(n, k) 傳回從 n 個元素中取出 k 個的排列數=n!/(k!(n-k)!), 3.8 版後
 prod(x) 傳回 x (串列, 元組, 集合) 所有元素之乘積 (Python 3.8 版新增)
 exp(x) 傳回 x 的自然指數
 expm1(x) 傳回 x 的自然指數減 1 (Python 3.2 新增)
 log(x) 傳回 x 的自然對數 (以 e 為底)
 log2(x) 傳回 x 的以 2 為底的對數 (Python 3.3 新增)
 log10(x) 傳回 x 的自然對數 (以 10 為底)
 log1p(x) 傳回 x + 1 的自然對數 (以 e 為底)
 factorial(x) 傳回 x 的階乘 x!
 sin(x) 傳回 x 的正弦值
 cos(x) 傳回 x 的餘弦值
 tan(x) 傳回 x 的正切值
 asin(x) 傳回 x 的反正弦值
 acos(x) 傳回 x 的反餘弦值
 atan(x) 傳回 x 的反正切值
 atan2(y,x) 傳回 y/x 的反正切值
 radians(x) 傳回角度 x 之弧度 (角度轉弧度)
 degrees(x) 傳回弧度 x 之角度 (弧度轉角度)
 sinh(x) 傳回 x 的雙曲正弦值
 cosh(x) 傳回 x 的雙曲餘弦值
 tanh(x) 傳回 x 的雙曲正切值
 isinf(x) 判斷 x 是否為 inf (傳回 True/False)
 isnan(x) 判斷 x 是否為 NaN (傳回 True/False)

fabs() 會傳回絕對值 (正數), 例如 : 

>>> math.fabs(-1.234)       # 絕對值
1.234 

內建函式 sum() 可以傳入數列 (多參數), 串列, 或元組, 但 fsum() 不能傳入數列, 因為它只能傳入一個可迭代的容器型態參數, 例如串列, 元組, 或集合等, fsum() 會傳回可迭代物件所有元素之和, 例如 : 

>>> math.fsum([1, 2, 3])    # 計算串列元素和     
6.0
>>> math.fsum((1, 2, 3))    # 計算元組元素和
6.0
>>> math.fsum({1, 2, 3})    # 計算集合元素和
6.0 

remainder(x, y) 函式顧名思義是傳回 x/y 之餘數, 但其計算方式為 x-n*y, 其中 n 是最接近 x/y 商之整數, 例如 : 

>>> math.remainder(13, 3)       # 13/3 餘數是 1=13-4*3
1.0
>>> math.remainder(23.5, 5)    # 傳回 -1,5=23.5-5*5
-1.5

此處餘數並非 0, 因 23.5/5 商為 4.7, 最接近之整數為 5, 故傳回 23.5-5*5=-1.5. 

fmod(x, y) 會傳回 x/y 之模數, 計算整數模數會用 % 運算子; 計算浮點數模數則用 fmod() :

>>> 23 % 5   
3
>>> math.fmod(23.5, 5)   
3.5 
>>> math.fmod(5, -2)    
1.0
>>> 5 % -2   
-1

modf(x) 則會把浮點數的小數與整數部分拆開, 組成 (小數, 整數) 之元組傳回 (帶符號) :

>>> math.modf(3.14159)   
(0.14158999999999988, 3.0)
>>> math.modf(-3.14159)    
(-0.14158999999999988, -3.0)  
>>> math.modf(-3.14159e2)     
(-0.15899999999999181, -314.0) 

開平方函式 sqrt() 與次方函式 pow() 都屬於冪函數, sqrt(x) 其實等於 pow(x, 0.5) :

>>> math.sqrt(2)               # 開平方
1.4142135623730951
>>> math.pow(2, 4)           # 2 的 4 次方
16.0
>>> math.pow(2, 0.5)        # 開平方就是 0.5 次方
1.4142135623730951
>>> math.pow(8, 1/3)        # 開立方就是 1/3 次方
2.0

floor(x), ceil(x), 與 trunc(x) 是近似函式 (math 模組沒有四捨五入或五捨六入函式), 它們會傳回浮點數的近似值, floor(x) 傳回 x 的地板整數; ceil(x) 傳回 x 的天花板整數; 而 trunc() 則是將小數部分完全捨去只傳回整數部分, 例如 : 

>>> math.floor(3.14)          # 小於或等於 3.14 的最大整數=3
3
>>> math.floor(-3.14)         # 小於或等於 -3.14 的最大整數=-4
-4
>>> math.ceil(3.14)             # 大於或等於 3.14 的最小整數=3
4
>>> math.ceil(-3.14)            # 大於或等於 -3.14 的最小整數=-3
-3
>>> math.trunc(3.14)          # 捨去小數部分
3
>>> math.trunc(-3.14)         # 捨去小數部分
-3

gcd() 會傳回傳入參數數列 (正整數) 的最大公因數, 而 lcm() 則會傳回其最小公倍數 (Python 3.9 新增功能) :

>>> math.gcd(6, 21)            # 最大公因數
3
>>> math.lcm(12, 8, 3, 4)    # 最小公倍數
24   

perm(n, k) 是從 n 個元素中取出 k 個不重複的排列方式, 等於 n!/(n-k)!, 此函式須在 Python 3.8 以上版本才有支援, 例如 : 

>>> math.perm(6, 2)     
30  
>>> math.factorial(6)/(math.factorial(6-2))     
30.0

exp(x) 會傳回 x 的自然指數值; log(x) 則會傳回 x 以 e 為底的自然對數值, 兩者互為反函數 :

>>> math.exp(1)                  # 自然指數
2.718281828459045   
>>> math.log(1)                   # 自然對數
0.0
>>> math.log(math.e)         # ln(e)=1
1.0

在 Python 3.2 版新增了 expm1(x) 函式, 其值為 exp(x)-1, 對於曉得浮點數 x 而言, exp(x)-1 可能會有誤差, expm1(x) 則能保持同樣運算的浮點數精度, 例如 :

>>> math.exp(1e-5)-1   
1.0000050000069649e-05
>>> math.expm1(1e-5)      
1.0000050000166667e-05

當 x 很小時, 用 exp(x)-1 來運算的話, 小數部分精確度會不足, 應改用 expm1(). 

任何基底的 1 的對數結果都是 0, 當 0 < x < 1 時對數都是負值, 越接近 0 越負, 0 的對數是負無限大, 故為了讓很小的數取對數仍是正值, 會將其加 1 後再取對數, 即 log1p() 之用途, 例如 : 

>>> math.log(1e-5)  
-11.512925464970229
>>> math.log(1e-10)   
-23.025850929940457
>>> math.log(1e-100)    
-230.25850929940458
>>> math.log1p(1e-5)    
9.99995000033333e-06
>>> math.log1p(1e-10)   
9.999999999500001e-11
>>> math.log1p(1e-100)   
1e-100

可見當 x 越小時, log1p() 的值越接近 x. Python 3.3 版新增了以 2 為底的 log2(x) : 

>>> math.log2(2)   
1.0
>>> math.log2(4)   
2.0
>>> math.log2(64)   
6.0

常用對數則是以 10 為底, 例如 : 

>>> math.log10(10)            # 常用對數
1.0
>>> math.log10(100)           # 常用對數
2.0

factorial(x) 會傳回 x 階乘 :

>>> math.factorial(3)         # 3!=3*2*1=6
6

sin(x), cos(x), tan(x) 為三角函數, 其中 tan(x)=sin(x)/cos(x), 數學上的另外三個三角函數 csc(), sec(), 與 cot(x) 分別為其倒數, 故 math 並未提供. 注意, 傳入參數均為弧度, 角度須先用 radians() 轉成弧度, 例如 :

>>> math.radians(30)         # 30 度=math.pi/6
0.5235987755982988
>>> math.radians(45)         # 45 度=math.pi/4
0.7853981633974483
>>> math.radians(60)         # 60 度=math.pi/3
1.0471975511965976
>>> math.radians(90)         # 90 度=math.pi/2
1.5707963267948966
>>> math.radians(180)       # 180 度=math.pi
3.141592653589793
>>> math.degrees(math.pi/6)      # math.pi/6=30 度
29.999999999999996
>>> math.degrees(math.pi/4)      # math.pi/4=45 度
45.0
>>> math.degrees(math.pi/3)      # math.pi/3=60 度
59.99999999999999
>>> math.degrees(math.pi/2)      # math.pi/2=90 度
90.0
>>> math.degrees(math.pi)         # 180 度=math.pi/2
180.0
>>> math.sin(math.radians(30))       # sin(30)=0.5
0.49999999999999994
>>> math.sin(math.radians(45))       # sin(45)=math.sqrt(2)/2
0.7071067811865476
>>> math.sqrt(2)/2     
0.7071067811865476
>>> math.sin(math.radians(60))       # sin(60)=math.sqrt(3)/2
0.8660254037844386
>>> math.sqrt(3)/2     
0.8660254037844386
>>> math.sin(math.radians(90))       # sin(90)=1
1.0
>>> math.cos(math.radians(30))       # cos(30)=math.sqrt(3)/2
0.8660254037844387
>>> math.cos(math.radians(45))       # cos(45)=math.sqrt(2)/2
0.7071067811865476
>>> math.cos(math.radians(60))      # cos(30)=0.5
0.5000000000000001
>>> math.cos(math.radians(90))      # cos(90)=0
6.123233995736766e-17

可見 cos(90) 趨近於 0, 使得 tan(90) 趨近於無限大, 例如 : 

>>> math.tan(math.radians(30))         
0.5773502691896257
>>> math.sin(math.radians(30))/math.cos(math.radians(30))    
0.5773502691896256
>>> math.tan(math.radians(45))    
0.9999999999999999
>>> math.sin(math.radians(45))/math.cos(math.radians(45))    
1.0
>>> math.tan(math.radians(60))    
1.7320508075688767
>>> math.sin(math.radians(60))/math.cos(math.radians(60))    
1.7320508075688767
>>> math.tan(math.radians(90))    
1.633123935319537e+16
>>> math.sin(math.radians(90))/math.cos(math.radians(90))      
1.633123935319537e+16

asin(x), acos(x), 與 atan(x) 分別為 sin(x), cos(x), 與 tan(x) 的反函數, 其傳回值同樣是弧度, 須利用 degrees() 轉成角度, 例如 :

>>> math.asin(0.5)      
0.5235987755982989                          # 這是弧度
>>> math.degrees(math.asin(0.5))   
30.000000000000004
>>> math.degrees(math.asin(0.7071))   
44.99945053347443
>>> math.degrees(math.asin(0.866))   
59.997089068811974
>>> math.degrees(math.asin(1))   
90.0
>>> math.degrees(math.acos(0.866))   
30.002910931188026
>>> math.degrees(math.acos(0.7071))        
45.00054946652557
>>> math.degrees(math.acos(0.5))    
60.00000000000001
>>> math.degrees(math.acos(0))      
90.0
>>> math.degrees(math.atan(0.5/0.866))   
30.000727780827372
>>> math.degrees(math.atan(0.701/0.701))   
45.0
>>> math.degrees(math.atan(0.866/0.5))   
59.999272219172624
>>> math.degrees(math.atan(math.inf))       # 1/0 為無限大
90.0
>>> math.degrees(math.atan2(0.5, 0.866))        # 第一參數 sin 值, 第二參數 cos 值
30.000727780827372
>>> math.degrees(math.atan2(0.701, 0.701))   
45.0
>>> math.degrees(math.atan2(0.866, 0.5))   
59.999272219172624
>>> math.degrees(math.atan2(1, 0))   
90.0

math 模組提供的雙曲函式有三個 : sinh(x), cosh(h), 與 tanh(x), 與三角函數類似, 其餘的三個雙曲函數 csch, cosh, 以及 coth 分別是其倒數, 即 csch=1/sinh, sech=cosh, coth=1/tanh. 例如 :

>>> math.sinh(0)    
0.0
>>> math.sinh(1)   
1.1752011936438014
>>> math.sinh(-1)       
-1.1752011936438014
>>> math.cosh(0)   
1.0
>>> math.cosh(1)   
1.5430806348152437
>>> math.cosh(-1)   
1.5430806348152437

沒有留言 :