- 非線性規划是指目標函數或約束條件中包含非線性函數的規划問題,實際就是非線性最優化問題。
- 從線性規划到非線性規划,不僅是數學方法的差異,更是解決問題的思想方法的轉變。
- 非線性規划問題沒有統一的通用方法,我們在這里學習的當然不是數學方法,而是如何建模、如何編程求解。
- 『Python小白的數學建模課 @ Youcans』帶你從數模小白成為國賽達人。
1. 從線性規划到非線性規划
本系列的開篇我們介紹了線性規划 (Linear Programming) 並延伸到整數規划、0-1規划,以及相對復雜的固定費用問題、選址問題。這些問題的共同特點是,目標函數與約束條件都是線性函數。如果目標函數或約束條件中包含非線性函數,則是非線性規划。
通常,非線性問題都比線性問題復雜得多,困難得多,非線性規划也是這樣。非線性規划沒有統一的通用方法、算法來解決,各種方法都有特定的應用范圍和適用條件。另一方面,很多非線性規划問題在實踐中不能獲得全局最優解,只能得到局部最優解或近似最優解。
這意味着什么?對於數學研究來說,這也許意味着存在新的課題和挑戰,可以研究更有效的算法。確實如此,即便線性規划問題的研究也在不斷前進,非線性規划問題的研究更是豐富多彩。但熱鬧是他們的,我什么也沒有。
我所想到的,是數學建模學習/課程/競賽的根本目的是什么?是掌握各種算法的推演,努力編程以實現,還是練習分析問題建立模型的能力,使用軟件和工具求解問題的能力?顯然是后者。可是,為什么培訓課上老師講的都是算法呢?到了例題例程,不是一帶而過,就是跳步驟講。聽課時津津有味,下課了題目還是不會做,程序還是調不通。於是,...
不過,到了非線性規划這一課,我們發現老師也不再不厭其煩地講算法了,不知道是講不下去還是講不過來了: 20世紀50年代,H.W.Kuhn 和 A.W.Tucker 提出了非線性規划的基本定理,為非線性規划奠定了理論基礎 ;50、60 年代出現了許多解非線性規划問題的有效算法;80年代后,隨着計算機技術的快速發展,非線性規划方法取得了長足進步,在信賴域法、稀疏擬牛頓法、並行計算、內點法和有限存儲法等領域取得了豐碩的成果。
所以,沒關系的,都一樣——參見章北海文集。
這意味着什么呢?這意味着對於學習數學建模的小白,學會把問題簡化為非線性規划的標准方程,學會按照本文的方法使用求解工具包的函數,才能求解非線性規划問題,才能完賽。
2. Scipy 庫求解非線性規划問題
2.1 非線性規划問題的描述
首先,我們回顧線性規划問題的標准形式:
類似地,可以寫出非線性規划的一般形式:
其中:\(x=[x_1,...,x_n]^T\) 為決策變量,\(f(x)\) 為目標函數,\(h_j(x)\) 和 \(g_i(x)\) 為約束條件。
由此可見,非線性規划問題,實際上就是帶有約束條件的非線性函數優化問題。
按照我們的學習模式,非線性規划問題的建模和求解與線性規划問題是類似的,按照以下步驟進行:
- 問題定義,確定決策變量、目標函數和約束條件;
- 模型構建,由問題描述建立數學方程,並轉化為標准形式的數學模型;
- 模型求解,用標准模型的優化算法對模型求解,得到優化結果。
2.2 Scipy 求解非線性規划問題的函數
Scipy 是 Python 算法庫和數學工具包,包括最優化、線性代數、積分、插值、特殊函數、傅里葉變換、信號和圖像處理、常微分方程求解等模塊。
本文推薦和講解使用 Scipy 工具包中的 optimize 模塊求解常見的非線性規划問題。
scipy.optimize 模塊中提供了多個用於非線性規划問題的方法,適用於不同類型的問題。
-
brent():單變量無約束優化問題,混合使用牛頓法/二分法。
-
fmin():多變量無約束優化問題,使用單純性法,只需要利用函數值,不需要函數的導數或二階導數。
-
leatsq():非線性最小二乘問題,用於求解非線性最小二乘擬合問題。
-
minimize():約束優化問題,使用拉格朗日乘子法將約束優化轉化為無約束優化問題。
2.3 scipy.optimize.brent() 求解單變量無約束優化問題
非線性規划最簡單的形式是一維搜索,一維搜索的常用方法是函數逼近法和區間收縮法。
brent() 函數是 SciPy.optimize 模塊中求解單變量無約束優化問題最小值的首選方法。這是牛頓法和二分法的混合方法,既能保證穩定性又能快速收斂。
scipy.optimize.brent(func, args=(), brack=None, tol=1.48e-08, full_output=0, maxiter=500)
optimize.brent() 的主要參數:
- func: callable f(x,*args) 目標函數 \(f(x)\),以函數形式表示,可以通過 *args 傳遞參數
- args: tuple 可選項,以 f(x,*args) 的形式將可變參數 p 傳遞給目標函數 \(f(x,p)\) 。
- brack: tuple 可選項,搜索算法的開始區間(不是指 x 的上下限)
optimize.brent() 的主要返回值:
- **xmin: ** 返回函數達到最小值時的 x(注意是局部最優,不一定是全局最優)。
- **fval: ** 返回函數的最優值(默認不返回,僅當 full_output 為 1 時返回)。
optimize.brent() 的使用例程:
from scipy.optimize import brent, fmin_ncg, minimize
import numpy as np
# 1. Demo1:單變量無約束優化問題(Scipy.optimize.brent)
def objf(x): # 目標函數
fx = x**2 - 8*np.sin(2*x+np.pi)
return fx
xIni = -5.0
xOpt= brent(objf, brack=(xIni,2))
print("xIni={:.4f}\tfxIni={:.4f}".format(xIni,objf(xIni))
print("xOpt={:.4f}\tfxOpt={:.4f}".format(xOpt,objf(xOpt)))
例程運行結果:
xIni=-5.0000 fxIni=29.3522
xOpt=-0.7391 fxOpt=-7.4195

2.4 scipy.optimize.fmin() 求解多變量無約束優化問題
多變量無約束優化問題的算法很多,分類方式也很多。從使用者的角度來說可以分為:只使用目標函數值、使用導數(梯度下降法)、使用二階導數。大體來說,使用導數的算法收斂較快,使用二階導數收斂更快,但是收斂快也容易陷入局部最優。
fmin() 函數是 SciPy.optimize 模塊中求解多變量無約束優化問題(最小值)的首選方法,采用下山單純性方法。下山單純性方法又稱 Nelder-Mead 法,只使用目標函數值,不需要導數或二階導數值,是最重要的多維無約束優化問題數值方法之一。
scipy.optimize.fmin(func, x0, args=(), xtol=0.0001, ftol=0.0001, maxiter=None, maxfun=None, full_output=0, disp=1, retall=0, callback=None, initial_simplex=None)
optimize.fmin() 的主要參數:
- func: callable f(x,*args) 目標函數 \(f(x)\),以函數形式表示,可以通過 *args 傳遞參數。
- x0: nadarray 搜索算法的初值。
- args: tuple 可選項,以 f(x,*args) 的形式將可變參數 p 傳遞給目標函數 \(f(x,p)\) 。
optimize.fmin() 的主要返回值:
- **xopt: ** 返回最小值時的 x 值。
- **fopt: ** 返回最小值時的目標函數值,fopt=func(xopt)。
optimize.fmin() 的使用例程:
from scipy.optimize import brent, fmin, minimize
import numpy as np
# 2. Demo2:多變量無約束優化問題(Scipy.optimize.brent)
# Rosenbrock 測試函數
def objf2(x): # Rosenbrock benchmark function
fx = sum(100.0 * (x[1:] - x[:-1] ** 2.0) ** 2.0 + (1 - x[:-1]) ** 2.0)
return fx
xIni = np.array([-2, -2])
xOpt = fmin(objf2, xIni)
print("xIni={:.4f},{:.4f}\tfxIni={:.4f}".format(xIni[0],xIni[1],objf2(xIni)))
print("xOpt={:.4f},{:.4f}\tfxOpt={:.4f}".format(xOpt[0],xOpt[1],objf2(xOpt)))
例程運行結果:
xIni=-2.0000,-2.0000 fxIni=3609.0000
xOpt=1.0000,1.0000 fxOpt=0.0000

3. scipy.optimize.minimize() 求解非線性規划問題
3.1 scipy.optimize.minimize() 函數說明
minimize() 函數是 SciPy.optimize 模塊中求解多變量優化問題的通用方法,可以調用多種算法,支持約束優化和無約束優化。
scipy.optimize.minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)
optimize.minimize() 的主要參數:
- fun: callable f(x,*args) 目標函數 \(f(x)\),以函數形式表示,可以通過 *args 傳遞參數。
- x0: nadarray, shape(n,) 搜索算法的初值,n 是決策變量個數。
- args: tuple 可選項,將可變參數傳遞給目標函數 fun、導數函數 jac 和二階導數函數 hess。
- method: str 可選項,選擇優化算法。默認算法為 BFGS, L-BFGS-B, SLSQP(取決於問題有沒有邊界條件和約束條件)
- **jac: ** 可選項,梯度計算方法。可以以函數形式表示,或選擇 '2-point', '3-point', 'cs'。該選項只能用於 CG, BFGS, Newton-CG, L-BFGS-B, TNC, SLSQP, dogleg, trust-ncg, trust-krylov, trust-exact 和 trust-constr 算法。
- **hess: ** 可選項,Hessian 矩陣計算方法。可以以函數形式表示,或選擇 '2-point', '3-point', 'cs'。該選項只能用於 Newton-CG, dogleg, trust-ncg, trust-krylov, trust-exact 和 trust-constr 算法。
- **bounds: ** 可選項,變量的邊界條件(上下限,lb<=x<=ub)。該選項只能用於 Nelder-Mead, L-BFGS-B, TNC, SLSQP, Powell 和 trust-constr 算法。
- **constraints: ** 可選項,定義約束條件 f(x)>=0。該選項只能用於 COBYLA, SLSQP 和 trust-constr 算法,注意不同算法中對於約束條件的定義是不同的。
optimize.minimize() 的主要返回值:
- **res: ** 返回優化結果,以對象方式表示,主要包括優化是否成功、決策變量的優化值 xOpt。
optimize.minimize() 的優化算法選項:
optimize.minimize() 的默認算法為 BFGS, L-BFGS-B, SLSQP(取決於問題有沒有邊界條件和約束條件),可以通過 "method=None" 選項調用多種算法:
無約束問題優化算法
-
**method=‘CG’ **: 非線性共軛梯度算法,只能處理無約束優化問題,需要使用一階導數函數。
-
**method=‘BFGS’ **: BFGS 擬牛頓法,只能處理無約束優化問題,需要使用一階導數函數。BFGS 算法性能良好,是無約束優化問題的默認算法。
-
**method=‘Newton-CG’ **: 截斷牛頓法,只能處理無約束優化問題,需要使用一階導數函數,適合處理大規模問題。
-
**method=‘dogleg’ **: dog-leg 信賴域算法,需要使用梯度和 Hessian(必須正定),只能處理無約束優化問題,
-
**method=‘trust-ncg’ **: 采用牛頓共軛梯度信賴域算法,需要使用梯度和 Hessian(必須正定),只能處理無約束優化問題,適合大規模問題。
-
method=‘trust-exact’: 求解無約束極小化問題的信賴域方法,需要梯度和Hessian(不需要正定)。
-
method=‘trust-krylov’: 使用Newton-GLTR 信賴域算法度,需要使用梯度和 Hessian(必須正定),只能處理無約束優化問題,適合中大規模問題。
邊界約束條件問題優化算法
-
method=‘Nelder-Mead’: 下山單純性法,可以處理邊界約束條件(決策變量的上下限),只使用目標函數,不使用導數函數、二階導數,魯棒性強。
-
**method=‘L-BFGS-B’ **: 改進的 BFGS 擬牛頓法,L- 指有限內存,-B 指邊界約束,可以處理邊界約束條件,需要使用一階導數函數。L-BFGS_B 算法性能良好,消耗內存量很小,適合處理大規模問題,是邊界約束優化問題的默認算法。
-
method=‘Powell’: 改進的共軛方向法,可以處理邊界約束條件(決策變量的上下限)。
-
**method=‘TNC’ **: 截斷牛頓法,可以處理邊界約束條件
帶有約束條件問題優化算法
-
**method=‘COBYLA’ **: 線性近似約束優化方法,通過對目標函數和約束條件的線性逼近處理非線性問題。只使用目標函數,不需要導數或二階導數值,可以處理約束條件。
-
**method=‘SLSQP’ **: 序貫最小二乘規划算法,可以處理邊界約束、等式約束和不等式約束條件。SLSQP 算法性能良好,是帶有約束條件優化問題的默認算法。
-
**method=‘trust-constr’ **: 信賴域算法,通用的約束最優化方法,適合處理大規模問題。
由於 optimize.minimize() 實際是多種算法的集成接口,各種算法對於問題、約束條件和參數的定義並不完全相同,對於各種算法的研究和應用已超出本文的內容,有興趣的讀者可以閱讀官方文檔: scipy.optimize.minimize — SciPy v1.7.0 Manual
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize
我們還是針對數學建模的常用需求和小白的特點,結合實際案例來學習基本應用。
3.2 scipy.optimize.minimize() 函數使用例程
編程步驟說明:
- 導入 scipy、numpy 包;
- 定義目標函數 objf3(x),輸入變量 x 表示向量,返回值 fx 是目標函數的計算結果 。
- 定義邊界約束,即優化變量的上下限:
- minimize() 默認無邊界約束條件,即各自變量的取值范圍沒有限制;
- 如果設置邊界約束,要對每個自變量(決策變量)定義其上下限,注意定義邊界約束的格式;
- 如果某個自變量沒有上限(下限),則表示為 None 。
- 定義 x 的初值。
- 求解最小化問題 resRosen,其中目標函數 objf3 和搜索的初值點 xIni 是必需的,指定優化方法和邊界條件是可選項。如果優化問題是求最大值 maxFx,可以通過 minFx = - maxFx 的變換來實現。
- 通過調用最小化問題的返回值 resRosen.x 得到最優點 xOpt。
Python 例程:
from scipy.optimize import brent, fmin, minimize
import numpy as np
# 3. Demo3:多變量邊界約束優化問題(Scipy.optimize.minimize)
# 定義目標函數
def objf3(x): # Rosenbrock 測試函數
fx = sum(100.0 * (x[1:] - x[:-1] ** 2.0) ** 2.0 + (1 - x[:-1]) ** 2.0)
return fx
# 定義邊界約束(優化變量的上下限)
b0 = (0.0, None) # 0.0 <= x[0] <= Inf
b1 = (0.0, 10.0) # 0.0 <= x[1] <= 10.0
b2 = (-5.0, 100.) # -5.0 <= x[2] <= 100.0
bnds = (b0, b1, b2) # 邊界約束
# 優化計算
xIni = np.array([1., 2., 3.])
resRosen = minimize(objf3, xIni, method='SLSQP', bounds=bnds)
xOpt = resRosen.x
print("xOpt = {:.4f}, {:.4f}, {:.4f}".format(xOpt[0],xOpt[1],xOpt[2]))
print("min f(x) = {:.4f}".format(objf3(xOpt)))
例程運行結果:
xOpt = 1.0000, 1.0000, 1.0000
min f(x) = 0.0000
4. 約束非線性規划問題實例
4.1 非線性規划問題的數學模型:
由於 minimize() 函數中對約束條件的形式定義為 f(x)>=0,因此要將問題的數學模型轉換為標准形式:
4.2 Python 例程 1:
程序說明:
- 在本例程中,目標函數中的參數 a, b, c, d 在子程序中直接賦值,這種實現方式最簡單;
- 定義邊界約束,即優化變量的上下限,與 3.2 中的例程相同,用 minimize() 函數中的選項 bounds=bnds 進行定義。
- 定義約束條件:
- 本案例有 4個約束條件,2個等式約束、2個不等式約束,上節中已寫成標准形式;
- 本例程將每個約束條件作為一個子函數定義,
- minimize() 函數對約束條件按照字典格式: {'type': 'ineq', 'fun': functionname} 進行定義。'type' 的鍵值可選 'eq' 和 'ineq',分別表示的是約束和不等式約束;functionname是定義約束條件的函數名。
- 求解最小化問題 res,其中目標函數 objF4 和搜索的初值點 x0 是必需的,指定優化方法和邊界條件、約束條件是可選項。
- 通過調用最小化問題的返回值可以得到優化是否成功的說明(res.message)、自變量的優化值(res.x)和目標函數的優化值(res.fun)。
Python 例程:
from scipy.optimize import brent, fmin, minimize
import numpy as np
# 4. Demo4:約束非線性規划問題(Scipy.optimize.minimize)
def objF4(x): # 定義目標函數
a, b, c, d = 1, 2, 3, 8
fx = a*x[0]**2 + b*x[1]**2 + c*x[2]**2 + d
return fx
# 定義約束條件函數
def constraint1(x): # 不等式約束 f(x)>=0
return x[0]** 2 - x[1] + x[2]**2
def constraint2(x): # 不等式約束 轉換為標准形式
return -(x[0] + x[1]**2 + x[2]**3 - 20)
def constraint3(x): # 等式約束
return -x[0] - x[1]**2 + 2
def constraint4(x): # 等式約束
return x[1] + 2*x[2]**2 -3
# 定義邊界約束
b = (0.0, None)
bnds = (b, b, b)
# 定義約束條件
con1 = {'type': 'ineq', 'fun': constraint1}
con2 = {'type': 'ineq', 'fun': constraint2}
con3 = {'type': 'eq', 'fun': constraint3}
con4 = {'type': 'eq', 'fun': constraint4}
cons = ([con1, con2, con3,con4]) # 3個約束條件
# 求解優化問題
x0 = np.array([1., 2., 3.]) # 定義搜索的初值
res = minimize(objF4, x0, method='SLSQP', bounds=bnds, constraints=cons)
print("Optimization problem (res):\t{}".format(res.message)) # 優化是否成功
print("xOpt = {}".format(res.x)) # 自變量的優化值
print("min f(x) = {:.4f}".format(res.fun)) # 目標函數的優化值
例程 1 運行結果:
Optimization problem (res): Optimization terminated successfully
xOpt = [0.6743061 1.15138781 0.96140839]
min f(x) = 13.8790
4.3 Python 例程 2:
程序說明:
-
本例程的問題與 4.2 中的例程 1 是相同的,結果也相同,但編程實現的方法進行了改進;
-
本例程中目標函數中的參數 a, b, c, d 在主程序中賦值,通過 args 把參數傳遞到子程序,這種實現方式使參數賦值更為靈活,特別是適用於可變參數的問題;注意目標函數的定義不是 def objF5(x,args),而是 def objF5(args),要特別注意目標函數的定義和實現方法。
-
定義約束條件:
- 本案例有 4 個約束條件,2個等式約束、2個不等式約束,上節中已寫成標准形式;
- 本例程將 4 個約束條件放在一個子函數中定義,是程序更加簡潔。
- 注意每個約束條件仍然按照字典格式 {'type': 'ineq', 'fun': functionname} 進行定義,但 functionname 並不是函數名,而是一個 lambda 匿名函數。
-
通過調用最小化問題的返回值可以得到優化是否成功的說明(res.message)、自變量的優化值(res.x)和目標函數的優化值(res.fun)。
Python 例程 2:
from scipy.optimize import brent, fmin, minimize
import numpy as np
# 5. Demo5:約束非線性規划問題(Scipy.optimize.minimize)
def objF5(args): # 定義目標函數
a,b,c,d = args
fx = lambda x: a*x[0]**2 + b*x[1]**2 + c*x[2]**2 + d
return fx
def constraint1(): # 定義約束條件函數
cons = ({'type': 'ineq', 'fun': lambda x: (x[0]**2 - x[1] + x[2]**2)}, # 不等式約束 f(x)>=0
{'type': 'ineq', 'fun': lambda x: -(x[0] + x[1]**2 + x[2]**3 - 20)}, # 不等式約束 轉換為標准形式
{'type': 'eq', 'fun': lambda x: (-x[0] - x[1]**2 + 2)}, # 等式約束
{'type': 'eq', 'fun': lambda x: (x[1] + 2*x[2]**2 - 3)}) # 等式約束
return cons
# 定義邊界約束
b = (0.0, None)
bnds = (b, b, b)
# 定義約束條件
cons = constraint1()
args1 = (1,2,3,8) # 定義目標函數中的參數
# 求解優化問題
x0 = np.array([1., 2., 3.]) # 定義搜索的初值
res1 = minimize(objF5(args1), x0, method='SLSQP', bounds=bnds, constraints=cons)
print("Optimization problem (res1):\t{}".format(res1.message)) # 優化是否成功
print("xOpt = {}".format(res1.x)) # 自變量的優化值
print("min f(x) = {:.4f}".format(res1.fun)) # 目標函數的優化值
例程 2 運行結果:
Optimization problem (res1): Optimization terminated successfully
xOpt = [0.6743061 1.15138781 0.96140839]
min f(x) = 13.8790
4.4 Python 例程 3:
程序說明:
- 本例程的問題與 4.3 中的例程 2 是相同的,結果也相同,但編程實現的方法進行了改進;
- 本例程中約束條件中的參數在主程序中賦值,通過 args 把參數傳遞到約束條件定義的子程序,這種實現方式使參數賦值更為靈活,特別是適用於可變參數的問題。
- 本例程中將邊界約束條件即自變量的取值范圍作為不等式約束條件處理,不另作邊界條件設置。
- 通過調用最小化問題的返回值可以得到優化是否成功的說明(res.message)、自變量的優化值(res.x)和目標函數的優化值(res.fun)。
Python 例程 3:
from scipy.optimize import brent, fmin, minimize
import numpy as np
# 6. Demo6:約束非線性規划問題(Scipy.optimize.minimize)
def objF6(args): # 定義目標函數
a,b,c,d = args
fx = lambda x: a*x[0]**2 + b*x[1]**2 + c*x[2]**2 + d
return fx
def constraint2(args):
xmin0, xmin1, xmin2 = args
cons = ({'type': 'ineq', 'fun': lambda x: (x[0]**2 - x[1] + x[2]**2)}, # 不等式約束 f(x)>=0
{'type': 'ineq', 'fun': lambda x: -(x[0] + x[1]**2 + x[2]**3 - 20)}, # 不等式約束 轉換為標准形式
{'type': 'eq', 'fun': lambda x: (-x[0] - x[1]**2 + 2)}, # 等式約束
{'type': 'eq', 'fun': lambda x: (x[1] + 2*x[2]**2 - 3)}, # 等式約束
{'type': 'ineq', 'fun': lambda x: (x[0] - xmin0)}, # x0 >= xmin0
{'type': 'ineq', 'fun': lambda x: (x[1] - xmin1)}, # x1 >= xmin1
{'type': 'ineq', 'fun': lambda x: (x[2] - xmin2)}) # x2 >= xmin2
return cons
# 求解優化問題
args1 = (1,2,3,8) # 定義目標函數中的參數
args2 = (0.0, 0.0, 0.0) # xmin0, xmin1, xmin2
cons2 = constraint2(args2)
x0 = np.array([1., 2., 3.]) # 定義搜索的初值
res2 = minimize(objF6(args1), x0, method='SLSQP', constraints=cons2)
print("Optimization problem (res2):\t{}".format(res2.message)) # 優化是否成功
print("xOpt = {}".format(res2.x)) # 自變量的優化值
print("min f(x) = {:.4f}".format(res2.fun)) # 目標函數的優化值
例程 3 運行結果:
Optimization problem (res2): Optimization terminated successfully
xOpt = [0.6743061 1.15138781 0.96140839]
min f(x) = 13.8790
5. 小結
Scipy 工具包中的 minimize() 函數集成了多種求解線性規划問題的算法,可以處理邊界條件和等式、不等式約束,對於常見的非線性規划問題都能獲得較好的解。
minimize() 函數對於等式約束、不等式約束條件的編程定義了標准形式和輸入格式,通過對比 4.2~4.4 的 3個例程可以幫助讀者理解有關的格式要求。
【本節完】
版權說明:
歡迎關注『Python小白的數學建模課 @ Youcans』 原創作品
CSDN 原創作品,轉載必須標注原文鏈接:(https://www.cnblogs.com/youcans/p/15064686.html)。
Copyright 2021 Youcans, XUPT
Crated:2021-06-30
歡迎關注 『Python小白的數學建模課 @ Youcans』,每周更新數模筆記
Python小白的數學建模課-01.新手必讀
Python小白的數學建模課-02.數據導入
Python小白的數學建模課-03.線性規划
Python小白的數學建模課-04.整數規划
Python小白的數學建模課-05.0-1規划
Python小白的數學建模課-06.固定費用問題
Python小白的數學建模課-07.選址問題
Python小白的數學建模課-09.微分方程模型
Python小白的數學建模課-10.微分方程邊值問題
Python小白的數學建模課-B2.新冠疫情 SI模型
Python小白的數學建模課-B3.新冠疫情 SIS模型
Python小白的數學建模課-B4.新冠疫情 SIR模型
Python小白的數學建模課-B5.新冠疫情 SEIR模型
Python小白的數學建模課-B6.改進 SEIR疫情模型
