① python可以弄動態的三維圖形嗎
可以的。
可以採用圖形庫,例如:matplotlib等,按照說明或是參照樣常式序修改並配置好參數即可。
② 使用Matplotlib模擬Python中的三維太陽系
編程的一個用途是通過模擬來幫助我們理解真實世界。這一技術被應用於科學、金融和許多其他定量領域。只要控制現實世界屬性的「規則」是已知的,你就可以編寫一個計算機程序來 探索 你遵循這些規則所得到的結果。在本文中,您將 用Python模擬三維太陽系 使用流行的可視化庫Matplotlib
在這篇文章,你將能夠用Python創建你自己的3D太陽系,你可以用你想要的多少太陽和行星。下面是一個簡單的太陽系的一個例子,它有一個太陽和兩個行星:
你還可以打開動畫地板上的二維投影,更好地展示太陽系的三維本質。下面是同樣的太陽系模擬,包括2D投影:
下面是這篇文章的概要,以便您知道接下來會發生什麼:
在本文中,您將使用面向對象的編程和Matplotlib。如果您希望閱讀更多關於任何一個主題的內容,您可以閱讀:
讓我們從使用Matplotlib在Python中模擬一個3D太陽系開始。
太陽系中的太陽、行星和其他天體都是運動中的天體,它們相互吸引。引力在任何兩個物體之間施加。
如果這兩個對象有大量M_1和M_2是距離r然後,你可以用以下公式計算它們之間的引力:
常數G是一個引力常數。您將看到如何在模擬的版本中忽略這個常量,在本文中,您將使用任意單位的質量和距離,而不是kg和m。
一旦你知道了兩個物體之間的引力,你就可以計算出加速度。a每個物體都是由於這種引力而經歷的,使用以下公式:
使用這個加速度,你可以調整運動物體的速度。當速度發生變化時,速度和方向都會發生變化。
當用Python模擬一個三維太陽系時,你需要用三維空間來表示太陽系。因此,這個3D空間中的每個點都可以用三個數字來表示, x -, y -和 z -坐標。例如,如果你想把太陽放在太陽系的中心,你可以將太陽的位置表示為 (0, 0, 0) .
您還需要在3D空間中表示向量。矢量具有大小和方向。你需要像速度、加速度和力這樣的量的矢量,因為這些量都有一個方向和一個震級。
在本文中,我將不詳細討論向量代數。相反,我將陳述您需要的任何結果。你可以讀到更多關於向量與向量代數如果你願意的話。
為了在代碼中更容易地處理向量,您可以創建一個類來處理它們。編寫這個類將作為對類和面向對象編程的快速刷新。你可以讀到用Python進行面向對象的編程如果你覺得你需要一個更徹底的解釋。雖然您也可以創建一個類來處理3D空間中的點,但這並不是必要的,在本文中我也不會創建一個類。
如果您熟悉向量和面向對象編程,可以跳過本節,只需在定義 Vector 班級。
創建一個名為 vectors.py 中,您將定義 Vector 班級。您將使用此腳本定義類並對其進行測試。然後,可以刪除最後的測試代碼,只需在這個腳本中保留類定義:
這個 __init__() 方法的 Vector 類有三個參數,表示每個軸上的值。每個參數的默認值為 0 表示該軸的原點。雖然我們不喜歡在Python中使用單個字母名稱, x , y ,和 z 是恰當的,因為它們代表了數學中常用的笛卡爾坐標系的術語。
您還定義了兩個Dunder方法來將對象表示為一個字元串:
在代碼段的末尾,您可以更多地了解這兩種類型的字元串表示之間的差異。Python編碼書第9章 .
測試代碼塊的輸出如下:
在Python項目中的這個3D太陽系中,如果 Vector 類是可索引的,以便您可以使用 [] 帶有索引以提取其中一個值的符號。使用當前形式的類,如果添加 print(test[0]) 在您的腳本中,您將得到一個 TypeError 說 Vector 對象不可訂閱。您可以通過向類定義中添加另一個Dud方法來修復這個問題:
通過定義 __getitem__() ,你做了 Vector 可索引的類。向量中的第一項是 x 的價值。 y 的價值。 z 。任何其他索引都會引發錯誤。測試代碼塊的輸出如下:
test[0] 返迴向量中的第一個項, x .
可以定義類的對象的加法和減法。 __add__() 和 __sub__() DunderMethod.這些方法將使您能夠使用 + 和 - 執行這些操作的符號。如果沒有這些Dud方法,則使用 + 和 - 提出 TypeError .
若要添加或減去兩個向量,可以分別添加或減去向量的每個元素:
雙管齊下 __add__() 和 __sub__() 返回另一個 Vector 對象,每個元素等於兩個原始向量中相應元素的加減。輸出如下:
對於乘法和除法,您也可以這樣做,盡管在處理向量時,這些操作需要更多的注意。
在處理向量時,不能僅僅引用「乘法」,因為有不同類型的「乘法」。在這個項目中,你只需要標量乘法。標量乘法是指向量與標量相乘(標量有一個數量級,但沒有方向)。但是,在本小節中,您還將定義點積兩個向量。你想用 * 運算符,既適用於標量乘法,也適用於點積。因此,可以定義 __mul__() DunderMethod:
使用 * 運算符將取決於第二個操作數,即 * 符號,是標量或向量。如果由參數表示的第二個操作數 other ,是類型的 Vector ,計算了點積。但是,如果 other 是類型的 int 或 float ,返回的結果是一個新的 Vector ,按比例調整。
以上代碼的輸出如下:
如果您想要標量乘法,則需要標量乘法。 後 這個 * 象徵。如果您試圖運行該語句 3*Vector(3, 5, 9) 相反, TypeError 將被提高,因為 Vector 類不是用於使用的有效操作數。 * 帶有類型的對象 int .
兩個向量是分不開的。但是,可以將向量除以標量。您可以使用 / 運算符 Vector 如果定義 __truep__() DunderMethod:
產出如下:
如果你有一個向量(x,y,z),您可以找到它的震級使用表達式(x^2+y^2+z^2)。你也可以規范化向量。歸一化給出一個方向相同但大小為 1 。您可以通過將向量的每個元素除以矢量的大小來計算歸一化向量。
可以定義兩個新方法來完成 Vector 班級:
測試代碼提供了以下輸出:
第三個輸出給出了歸一化向量的大小,表明它的大小是 1 .
根據使用的IDE或其他工具,在分割時可能會收到警告 self.x , self.y ,和 self.z ,如在 __truep__() 和 normalize() 。您不需要擔心這個問題,但是如果您想要修復它,可以通過更改 __init__() 簽署下列任何一項:
或
這兩個選項都讓IDE知道參數應該是浮動的。在第二個選項中,您使用類型暗示來實現。
您現在可以刪除此腳本末尾的測試代碼,以便您在 vectors.py 是類的定義。
現在,你可以開始研究Python中的3D太陽系了。您將創建兩個主要類:
你將使用Matplotlib來創建和可視化太陽系。您可以在終端中使用以下內容來安裝Matplotlib:
或
這個 Axes3D Matplotlib中的物體將「託管」太陽系。如果您使用過Matplotlib,並且主要使用了2D繪圖,那麼您將使用(有意或不知情的) Axes 對象。 Axes3D 的3D等效 Axes ,顧名思義!
現在是開始編寫和測試這些類的時候了。您可以創建兩個新文件:
接下來,您將開始處理 SolarSystem 班級。
您將在整個項目中使用任意單元。這意味著,與其用米作為距離,而用公斤作為質量,你將使用沒有單位的數量。參數 size 用於定義包含太陽系的立方體的大小:
定義 SolarSystem 類的 __init__() 方法,其中包含參數。 size 。您還定義了 bodies 屬性。這個屬性是一個空列表,當你稍後創建它們時,它將包含太陽系內的所有天體。這個 add_body() 方法可以用來將軌道天體添加到太陽系中。
下一步是介紹Matplotlib。屬性創建圖形和一組軸。 subplots() 在 matplotlib.pyplot :
你打電話 plt.subplots() ,它返回一個圖形和一組軸。返回的值分配給屬性。 fig 和 ax 。你打電話 plt.subplots() 有以下論點:
您還可以調用該方法。 tight_layout() 。這是 Figure 類在Matplotlib中。此方法減少了圖形邊緣的邊距。
到目前為止,您可以在控制台/REPL中嘗試代碼:
這給出了一組空的三維軸的圖形:
您將使用 size 參數設置此多維數據集的大小。你會回到 SolarSystem 稍後上課。目前,您可以將您的注意力轉向定義 SolarSystemBody 班級。
您可以開始創建 SolarSystemBody 類及其 __init__() 方法。我正在截斷 SolarSystem 下面代碼中的類定義用於顯示。在此代碼塊和以後的代碼塊中,包含 # ... 指出您之前編寫的未顯示的代碼:
中的參數。 __init__() 方法是:
你也叫 add_body() 方法中定義的 SolarSystem 類將這個天體添加到太陽系中。稍後,您將向 __init__() 方法。
中定義另一個方法。 SolarSystemBody 用其當前的位置和速度移動物體:
這個 move() 方法重新定義 position 屬性的 velocity 屬性。我們已經討論過你是如何用任意單位來計算距離和質量的。你也在使用任意的時間單位。每個『時間單位』將是循環的一個迭代,您將使用它來運行模擬。因此, move() 將身體按一次迭代所需的數量移動,這是一個時間單位。
你們已經創建了Matplotlib結構,它將容納太陽系及其所有天體。現在,您可以添加一個 draw() 方法 SolarSystemBody 若要在Matplotlib圖上顯示主體,請執行以下操作。您可以通過繪制一個標記來完成這一任務。
在這樣做之前,您需要在 SolarSystemBody 若要控制將繪制的標記的顏色和大小以表示身體,請執行以下操作:
類屬性 min_display_size 和 display_log_base 設置參數,以確定您將在3D圖上顯示的標記的大小。您設置了一個最小的大小,以便您顯示的標記不太小,即使對於小的身體也是如此。您將使用對數標度將質量轉換為標記大小,並將此對數的基值設置為另一個類屬性。
這個 display_size 屬性中的實例屬性。 __init__() 方法在計算的標記大小和所設置的最小標記大小之間進行選擇。為了在這個項目中確定身體的顯示大小,你要使用它的質量。
您還可以添加 colour 屬性 __init__() ,暫時默認為黑色。
要測試這些新添加的內容,可以在控制台/REPL中嘗試以下內容:
第一次呼叫 body.draw() 在原點繪制物體,因為你使用的是太陽系天體的默認位置。打電話給 body.move() 用一個「時間單位」所需的數量移動身體。因為身體的速度是 (1, 1, 1) ,身體將沿著三個軸中的每一個移動一個單位。第二次呼叫 body.draw() 在第二個位置畫太陽系天體。請注意,當您這樣做時,軸將自動重新排列。您很快就會在主代碼中處理這個問題。
您可以返回到 SolarSystem 通過給太陽系及其天體添加兩種新的方法,將其分類和連接起來: update_all() 和 draw_all() :
這個 update_all() 方法穿過太陽系中的每一個物體,移動並畫出每一個物體。這個 draw_all() 方法使用太陽系的大小設置三軸的限制,並通過 pause() 功能。此方法還清除軸,為下一個繪圖做好准備。
您可以開始構建一個簡單的太陽系,並通過創建一個名為 simple_solar_system.py :
運行此腳本時,您將看到一個黑體從情節的中心移動:
您可以更改三維圖形的透視圖,這樣您就可以直接沿著其中一個軸查看3D軸。可以通過將視圖的方位和仰角設置為 0 在……裡面 SolarSystem.__init__() :
跑動 simple_solar_system.py 現在給出以下觀點:
這個 x -軸現在垂直於你的屏幕。因為你在2D顯示器上顯示一個3D視圖,所以你總是有一個方向與你用來顯示圖形的2D平面垂直。這一限制使得很難區分物體何時沿該軸運動。你可以通過改變身體的速度 simple_solar_system.py 到 (1, 0, 0) 並再次運行腳本。身體似乎是靜止的,因為它只是沿著軸移動,從你的屏幕出來!
您可以通過根據它的不同更改標記的大小來改進三維可視化。 x -協調。靠近您的對象看起來更大,而更遠的對象看起來更小。您可以對 draw() 方法中的 SolarSystemBody 班級:
self.position[0] 表示身體的位置。 x -軸,即垂直於屏幕的軸。因子 30 除以是一個任意因素,您可以使用它來控制您希望這種效果有多強。
在本教程的後面,您還將添加另一個功能,將有助於可視化的三維運動的恆星和行星。
你有一個太陽系,裡面有可以移動的物體。到目前為止,如果您只有一個身體,那麼代碼可以正常工作。但那不是一個非常有趣的太陽系!如果你有兩個或兩個以上的物體,它們就會通過相互的引力相互作用。
在這篇文章的開頭,我簡要回顧了你需要處理兩個物體之間的引力的物理。由於在這個項目中使用的是任意單位,所以可以忽略引力常數 G 簡單地計算出由於兩個物體之間的重力而產生的力,如:
一旦你知道了兩個物體之間的力,因為F=ma,您可以計算出每個對象必須使用的加速度:
一旦你知道加速度,你就可以改變物體的速度。
您可以添加兩個新方法,一個在 SolarSystemBody 另一個在 SolarSystem ,計算出任何兩個物體之間的力和加速度,並穿過太陽系中的所有物體,並計算它們之間的相互作用。
第一種方法計算出兩個物體之間的引力,計算每個物體的加速度,並改變兩個物體的速度。如果您願意,可以將這些任務分為三種方法,但在本例中,我將將這些任務放在 SolarSystemBody :
accelerate_e_to_gravity() 對類型的對象調用。 SolarSystemBody 需要另一個 SolarSystemBody 身體作為一種爭論。參數 self 和 other 代表兩個身體相互作用。此方法的步驟如下:
現在你可以計算出任何兩個天體之間的相互作用,你可以計算出太陽系中所有天體之間的相互作用。你可以把你的注意力轉移到 SolarSystem 類的類:
這個 calculate_all_body_interactions() 方法貫穿太陽系的所有天體。每個天體與太陽系中的其他天體相互作用:
現在,您已經准備好創建一個簡單的太陽系,並測試您到目前為止編寫的代碼。
在這個項目中,您將關注創建兩種類型的天體之一:太陽和行星。您可以為這些機構創建兩個類。新類繼承自 SolarSystemBody :
這個 Sun 類的默認質量為10,000個單位,並將顏色設置為黃色。使用字元串 'yellow' ,這是Matplotlib中的有效顏色。
在 Planet 類創建一個 itertools.cycle 對象有三種顏色。在這種情況下,這三種顏色是紅色、綠色和藍色。你可以使用你想要的任何RGB顏色,也可以使用任意數量的顏色。在這個類中,使用帶有RGB值的元組來定義顏色,而不是使用顏色名稱的字元串。這也是在Matplotlib中定義顏色的有效方法。使用 next() 每當你創建一個新的行星時。
您還將默認質量設置為10個單元。
現在,你可以創建一個太陽系,其中一個太陽和兩個行星在 simple_solar_system.py :
在這個腳本中,您創建了一個太陽和兩個行星。你把太陽和行星分配給變數 sun 和 planets ,但這並不是嚴格要求的,因為 Sun 和 Planet 對象被創建,它們被添加到 solar_system 你不需要直接引用它們。
你用一個 while 循環來運行模擬。循環在每次迭代中執行三個操作。運行此腳本時,將獲得以下動畫:
它起作用了,算是吧。你可以看到太陽錨定在這個太陽系的中心,行星受到太陽引力的影響。除了行星在包含你電腦屏幕的平面上的運動(這些是 y -和 z --軸),你也可以看到行星越來越大,因為它們也在 x -軸,垂直於屏幕。
然而,你可能已經注意到行星的一些奇怪的行為。當它們被安排在太陽後面時,行星仍然被展示在太陽的前面。這不是數學上的問題--如果你跟蹤行星的位置,你會發現 x -坐標顯示,它們實際上是在太陽後面,正如你所預料的那樣。
這個問題來自Matplotlib在繪圖中繪制對象的方式。Matplotlib按繪制對象的順序將對象按層繪制。因為你在行星之前創造了太陽, Sun 對象放在第一位 solar_system.bodies 並作為底層繪制。您可以通過在行星之後創建太陽來驗證這一事實,在這種情況下,您將看到行星總是出現在太陽後面。
你會希望Matplotlib按照正確的順序繪制太陽系的天體,從最前的那些天體開始。要實現這一點,您可以對 SolarSystem.bodies 的值為基礎的列表。 x -協調每次刷新3D圖形的時間。下面是如何在 update_all() 方法 SolarSystem :
使用List方法 sort 帶著 key 參數來定義要用於排序列表的規則。這個 lambda 函數設置此規則。在本例中,您使用的值是 position[0] 表示 x -協調。因此,每次你打電話 update_all() 在模擬中 while 循環中,根據其沿 x -軸心。
運行 simple_solar_system.py 現在的腳本如下:
現在,你可以想像行星的軌道,就像它們圍繞太陽運行一樣。不斷變化的大小顯示了它們的 x -位置,當行星在太陽後面時,它們被隱藏在視線之外!
最後,你也可以移除軸線和網格,這樣你在模擬中看到的就是太陽和行星。可以通過添加對Matplotlib的調用來做到這一點。 axis() 方法 SolarSystem.draw_all() :
模擬現在看起來是這樣的:
使用Matplotlib對Python中的一個三維太陽系進行的模擬現在已經完成。在下一節中,您將添加一個功能,允許您查看 XY -模擬底部的飛機。這有助於可視化太陽系中物體的三維動力學。
在Python的三維太陽系模擬中,為了幫助可視化身體的運動,您可以在動畫的「地板」上添加一個2D投影。這個2D投影將顯示物體在 XY -飛機。要實現這一點,您需要將另一個繪圖添加到顯示動畫的相同軸上,並且只需在 x -和 y -坐標。你可以錨定 z -與圖形底部協調,使2D投影顯示在動畫的地板上。
您可以首先將一個新參數添加到 __init__() 方法的 SolarSystem 班級:
新參數 projection_2d ,默認為 False ,將允許您在兩個可視化選項之間切換。如果 projection_2d 是 False 動畫將只顯示身體在3D中移動,沒有軸和網格,就像你最後看到的結果一樣。
讓我們開始做一些改變 projection_2d 是 True :
您所做的更改如下:
您還需要在 simple_solar_system.py 打開2D投影:
模擬現在看起來如下:
的二維投影 XY -平面使它更容易跟隨軌道物體的路徑。
我們將用Python完成另一個三維太陽系的模擬。您將使用已經定義的類來模擬雙星系統。創建一個名為 binary_star_system.py 創造兩個太陽和兩個行星:
③ python 繪制三維圖形、三維數據散點圖
1. 繪制3D曲面圖
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig=plt.figure()
ax=Axes3D(fig)
x=np.arange(-4,4,0.25)
y=np.arange(-4,4,0.25)
x,y=np.meshgrid(x,y)
r=np.sqrt(x**2, y**2)
z=np.sin(r)
//繪面函數
ax.plot_surface(x,y,z,rstride=1,cstride=1,cmap=「rainbow」
plt.show()
2.繪制三維的散點圖(表述一些數據點分布)
4a.mat數據地址:http blog.csdn.net/eddy_zhang/article/details/50496164
from matplotlib import pyplot as plt
import scipy.io as sio
from mpl_toolkits.mplot3d import Axes3D
matl=『4a.mat』
data=sio.loadmat(matl)
m=data[『data』]
x,y,z=m[0],m[1],m[2]
//創建一個繪圖工程
ax=plt.subplot(111,project=『3D』)
//將數據點分成三部分畫,在顏色上有區分度
ax.scatter(x[:1000], y[:1000], z[:1000],c=『y』 )//繪制數據點
ax.scatter(x[1000:4000], y[1000:4000], z[1000:4000],c=『r』 )//繪制數據點
ax.scatter(x[4000:], y[4000:], z[4000:],c=『g』 )//繪制數據點
ax.set_zlable(『z』)//坐標軸
ax.set_ylable(『y』)//坐標軸
ax.set_xlable(『x』)
plt.show()
④ c4d怎麼用python畫三維圖
要以Python生成器為媒介。
用以下代碼可以簡單行程一個三維圖,在這個基礎上根據您的需要改寫代碼就可以了。
在生成器內的python代碼會生成一個object。默認下,生成了一個立方體,並返回:
import c4d
def main():
return c4d.BaseObject(c4d.Ocube)
UserData輸入
當然也可以返回別的物體,或者用userdata調整物體參數。注意op可以快速引用生成器對象。
importc4d
defmain():
cone =c4d.BaseObject(c4d.Ocone)
cone[c4d.PRIM_CONE_TRAD] = op[c4d.ID_USERDATA,1]
return cone
具體創建三維圖步驟如下
1首先要【創建】-【造型】-【python生成器】,默認生成一個立方體即python編輯器
2-選中對象,右下角【打開python編輯器】
3-代碼表示定義一個函數並返回C4D基本物體
4-這里簡單改一下,把原代碼中的Ocube改成Osphere,點一下執行,會生成一個三維球體。對於基本造型對象,這里的對象名稱通用語法為大寫字母O加對象的英文。
5-這里還可以用定義變數返回值的表達方法,比如這里定義變數cone(圓錐),c4d的屬性就是大寫字母O加上圓錐的英文即Ocone,返回這個變數值,執行就得到了一個圓錐。
6-除了生成基本三維圖形,python編輯器可以做很多事情,這里如果有一定的python編碼基礎,會更容易一些。可以在網上找幾個實例試一下,比如這種,生成數字的。可以在搜索引擎輸入關鍵字Cinema 4D - Python scripts來檢索別人寫好的腳本。
⑤ Python如何運用matplotlib庫繪制3D圖形
3D圖形在數據分析、數據建模、圖形和圖像處理等領域中都有著廣泛的應用,下面將給大家介紹一下如何在Python中使用 matplotlib進行3D圖形的繪制,包括3D散點、3D表面、3D輪廓、3D直線(曲線)以及3D文字等的繪制。
准備工作:
python中繪制3D圖形,依舊使用常用的繪圖模塊matplotlib,但需要安裝mpl_toolkits工具包,安裝方法如下:windows命令行進入到python安裝目錄下的Scripts文件夾下,執行: pip install --upgrade matplotlib即可;Linux環境下直接執行該命令。
安裝好這個模塊後,即可調用mpl_tookits下的mplot3d類進行3D圖形的繪制。
下面以實例進行說明。
1、3D表面形狀的繪制
這段代碼是繪制一個3D的橢球表面,結果如下:
2、3D直線(曲線)的繪制
這段代碼用於繪制一個螺旋狀3D曲線,結果如下:
3、繪制3D輪廓
繪制結果如下:
相關推薦:《Python視頻教程》
4、繪制3D直方圖
繪制結果如下:
5、繪制3D網狀線
繪制結果如下:
6、繪制3D三角面片圖
繪制結果如下:
7、繪制3D散點圖
繪制結果如下:
⑥ 萬字教你如何用 Python 實現線性規劃
想像一下,您有一個線性方程組和不等式系統。這樣的系統通常有許多可能的解決方案。線性規劃是一組數學和計算工具,可讓您找到該系統的特定解,該解對應於某些其他線性函數的最大值或最小值。
混合整數線性規劃是 線性規劃 的擴展。它處理至少一個變數採用離散整數而不是連續值的問題。盡管乍一看混合整數問題與連續變數問題相似,但它們在靈活性和精度方面具有顯著優勢。
整數變數對於正確表示自然用整數表示的數量很重要,例如生產的飛機數量或服務的客戶數量。
一種特別重要的整數變數是 二進制變數 。它只能取 零 或 一 的值,在做出是或否的決定時很有用,例如是否應該建造工廠或者是否應該打開或關閉機器。您還可以使用它們來模擬邏輯約束。
線性規劃是一種基本的優化技術,已在科學和數學密集型領域使用了數十年。它精確、相對快速,適用於一系列實際應用。
混合整數線性規劃允許您克服線性規劃的許多限制。您可以使用分段線性函數近似非線性函數、使用半連續變數、模型邏輯約束等。它是一種計算密集型工具,但計算機硬體和軟體的進步使其每天都更加適用。
通常,當人們試圖制定和解決優化問題時,第一個問題是他們是否可以應用線性規劃或混合整數線性規劃。
以下文章說明了線性規劃和混合整數線性規劃的一些用例:
隨著計算機能力的增強、演算法的改進以及更多用戶友好的軟體解決方案的出現,線性規劃,尤其是混合整數線性規劃的重要性隨著時間的推移而增加。
解決線性規劃問題的基本方法稱為,它有多種變體。另一種流行的方法是。
混合整數線性規劃問題可以通過更復雜且計算量更大的方法來解決,例如,它在幕後使用線性規劃。這種方法的一些變體是,它涉及使用 切割平面 ,以及。
有幾種適用於線性規劃和混合整數線性規劃的合適且眾所周知的 Python 工具。其中一些是開源的,而另一些是專有的。您是否需要免費或付費工具取決於問題的規模和復雜性,以及對速度和靈活性的需求。
值得一提的是,幾乎所有廣泛使用的線性規劃和混合整數線性規劃庫都是以 Fortran 或 C 或 C++ 原生和編寫的。這是因為線性規劃需要對(通常很大)矩陣進行計算密集型工作。此類庫稱為求解器。Python 工具只是求解器的包裝器。
Python 適合圍繞本機庫構建包裝器,因為它可以很好地與 C/C++ 配合使用。對於本教程,您不需要任何 C/C++(或 Fortran),但如果您想了解有關此酷功能的更多信息,請查看以下資源:
基本上,當您定義和求解模型時,您使用 Python 函數或方法調用低級庫,該庫執行實際優化工作並將解決方案返回給您的 Python 對象。
幾個免費的 Python 庫專門用於與線性或混合整數線性規劃求解器交互:
在本教程中,您將使用SciPy和PuLP來定義和解決線性規劃問題。
在本節中,您將看到線性規劃問題的兩個示例:
您將在下一節中使用 Python 來解決這兩個問題。
考慮以下線性規劃問題:
你需要找到X和Ÿ使得紅色,藍色和黃色的不平等,以及不平等X 0和ÿ 0,是滿意的。同時,您的解決方案必須對應於z的最大可能值。
您需要找到的自變數(在本例中為 x 和 y )稱為 決策變數 。要最大化或最小化的決策變數的函數(在本例中為 z) 稱為 目標函數 、 成本函數 或僅稱為 目標 。您需要滿足的 不等式 稱為 不等式約束 。您還可以在稱為 等式約束 的約束中使用方程。
這是您如何可視化問題的方法:
紅線代表的功能2 X + Ý = 20,和它上面的紅色區域示出了紅色不等式不滿足。同樣,藍線是函數 4 x + 5 y = 10,藍色區域被禁止,因為它違反了藍色不等式。黃線是 x + 2 y = 2,其下方的黃色區域是黃色不等式無效的地方。
如果您忽略紅色、藍色和黃色區域,則僅保留灰色區域。灰色區域的每個點都滿足所有約束,是問題的潛在解決方案。該區域稱為 可行域 ,其點為 可行解 。在這種情況下,有無數可行的解決方案。
您想最大化z。對應於最大z的可行解是 最優解 。如果您嘗試最小化目標函數,那麼最佳解決方案將對應於其可行的最小值。
請注意,z是線性的。你可以把它想像成一個三維空間中的平面。這就是為什麼最優解必須在可行區域的 頂點 或角上的原因。在這種情況下,最佳解決方案是紅線和藍線相交的點,稍後您將看到。
有時,可行區域的整個邊緣,甚至整個區域,都可以對應相同的z值。在這種情況下,您有許多最佳解決方案。
您現在已准備好使用綠色顯示的附加等式約束來擴展問題:
方程式 x + 5 y = 15,以綠色書寫,是新的。這是一個等式約束。您可以通過向上一張圖像添加相應的綠線來將其可視化:
現在的解決方案必須滿足綠色等式,因此可行區域不再是整個灰色區域。它是綠線從與藍線的交點到與紅線的交點穿過灰色區域的部分。後一點是解決方案。
如果插入x的所有值都必須是整數的要求,那麼就會得到一個混合整數線性規劃問題,可行解的集合又會發生變化:
您不再有綠線,只有沿線的x值為整數的點。可行解是灰色背景上的綠點,此時最優解離紅線最近。
這三個例子說明了 可行的線性規劃問題 ,因為它們具有有界可行區域和有限解。
如果沒有解,線性規劃問題是 不可行的 。當沒有解決方案可以同時滿足所有約束時,通常會發生這種情況。
例如,考慮如果添加約束x + y 1會發生什麼。那麼至少有一個決策變數(x或y)必須是負數。這與給定的約束x 0 和y 0相沖突。這樣的系統沒有可行的解決方案,因此稱為不可行的。
另一個示例是添加與綠線平行的第二個等式約束。這兩行沒有共同點,因此不會有滿足這兩個約束的解決方案。
一個線性規劃問題是 無界的 ,如果它的可行區域是無界,將溶液不是有限。這意味著您的變數中至少有一個不受約束,可以達到正無窮大或負無窮大,從而使目標也無限大。
例如,假設您採用上面的初始問題並刪除紅色和黃色約束。從問題中刪除約束稱為 放鬆 問題。在這種情況下,x和y不會在正側有界。您可以將它們增加到正無窮大,從而產生無限大的z值。
在前面的部分中,您研究了一個與任何實際應用程序無關的抽象線性規劃問題。在本小節中,您將找到與製造業資源分配相關的更具體和實用的優化問題。
假設一家工廠生產四種不同的產品,第一種產品的日產量為x ₁,第二種產品的產量為x 2,依此類推。目標是確定每種產品的利潤最大化日產量,同時牢記以下條件:
數學模型可以這樣定義:
目標函數(利潤)在條件 1 中定義。人力約束遵循條件 2。對原材料 A 和 B 的約束可以從條件 3 和條件 4 中通過對每種產品的原材料需求求和得出。
最後,產品數量不能為負,因此所有決策變數必須大於或等於零。
與前面的示例不同,您無法方便地將其可視化,因為它有四個決策變數。但是,無論問題的維度如何,原理都是相同的。
在本教程中,您將使用兩個Python 包來解決上述線性規劃問題:
SciPy 設置起來很簡單。安裝後,您將擁有開始所需的一切。它的子包 scipy.optimize 可用於線性和非線性優化。
PuLP 允許您選擇求解器並以更自然的方式表述問題。PuLP 使用的默認求解器是COIN-OR Branch and Cut Solver (CBC)。它連接到用於線性鬆弛的COIN-OR 線性規劃求解器 (CLP)和用於切割生成的COIN-OR 切割生成器庫 (CGL)。
另一個偉大的開源求解器是GNU 線性規劃工具包 (GLPK)。一些著名且非常強大的商業和專有解決方案是Gurobi、CPLEX和XPRESS。
除了在定義問題時提供靈活性和運行各種求解器的能力外,PuLP 使用起來不如 Pyomo 或 CVXOPT 等替代方案復雜,後者需要更多的時間和精力來掌握。
要學習本教程,您需要安裝 SciPy 和 PuLP。下面的示例使用 SciPy 1.4.1 版和 PuLP 2.1 版。
您可以使用pip以下方法安裝兩者:
您可能需要運行pulptest或sudo pulptest啟用 PuLP 的默認求解器,尤其是在您使用 Linux 或 Mac 時:
或者,您可以下載、安裝和使用 GLPK。它是免費和開源的,適用於 Windows、MacOS 和 Linux。在本教程的後面部分,您將看到如何將 GLPK(除了 CBC)與 PuLP 一起使用。
在 Windows 上,您可以下載檔案並運行安裝文件。
在 MacOS 上,您可以使用 Homebrew:
在 Debian 和 Ubuntu 上,使用apt來安裝glpk和glpk-utils:
在Fedora,使用dnf具有glpk-utils:
您可能還會發現conda對安裝 GLPK 很有用:
安裝完成後,可以查看GLPK的版本:
有關詳細信息,請參閱 GLPK 關於使用Windows 可執行文件和Linux 軟體包進行安裝的教程。
在本節中,您將學習如何使用 SciPy優化和求根庫進行線性規劃。
要使用 SciPy 定義和解決優化問題,您需要導入scipy.optimize.linprog():
現在您已經linprog()導入,您可以開始優化。
讓我們首先解決上面的線性規劃問題:
linprog()僅解決最小化(而非最大化)問題,並且不允許具有大於或等於符號 ( ) 的不等式約束。要解決這些問題,您需要在開始優化之前修改您的問題:
引入這些更改後,您將獲得一個新系統:
該系統與原始系統等效,並且將具有相同的解決方案。應用這些更改的唯一原因是克服 SciPy 與問題表述相關的局限性。
下一步是定義輸入值:
您將上述系統中的值放入適當的列表、元組或NumPy 數組中:
注意:請注意行和列的順序!
約束左側和右側的行順序必須相同。每一行代表一個約束。
來自目標函數和約束左側的系數的順序必須匹配。每列對應一個決策變數。
下一步是以與系數相同的順序定義每個變數的界限。在這種情況下,它們都在零和正無窮大之間:
此語句是多餘的,因為linprog()默認情況下採用這些邊界(零到正無窮大)。
註:相反的float("inf"),你可以使用math.inf,numpy.inf或scipy.inf。
最後,是時候優化和解決您感興趣的問題了。你可以這樣做linprog():
參數c是指來自目標函數的系數。A_ub和b_ub分別與不等式約束左邊和右邊的系數有關。同樣,A_eq並b_eq參考等式約束。您可以使用bounds提供決策變數的下限和上限。
您可以使用該參數method來定義要使用的線性規劃方法。有以下三種選擇:
linprog() 返回具有以下屬性的數據結構:
您可以分別訪問這些值:
這就是您獲得優化結果的方式。您還可以以圖形方式顯示它們:
如前所述,線性規劃問題的最優解位於可行區域的頂點。在這種情況下,可行區域只是藍線和紅線之間的綠線部分。最優解是代表綠線和紅線交點的綠色方塊。
如果要排除相等(綠色)約束,只需刪除參數A_eq並b_eq從linprog()調用中刪除:
解決方案與前一種情況不同。你可以在圖表上看到:
在這個例子中,最優解是紅色和藍色約束相交的可行(灰色)區域的紫色頂點。其他頂點,如黃色頂點,具有更高的目標函數值。
您可以使用 SciPy 來解決前面部分所述的資源分配問題:
和前面的例子一樣,你需要從上面的問題中提取必要的向量和矩陣,將它們作為參數傳遞給.linprog(),然後得到結果:
結果告訴您最大利潤是1900並且對應於x ₁ = 5 和x ₃ = 45。在給定條件下生產第二和第四個產品是沒有利潤的。您可以在這里得出幾個有趣的結論:
opt.statusis0和opt.successis True,說明優化問題成功求解,最優可行解。
SciPy 的線性規劃功能主要用於較小的問題。對於更大和更復雜的問題,您可能會發現其他庫更適合,原因如下:
幸運的是,Python 生態系統為線性編程提供了幾種替代解決方案,這些解決方案對於更大的問題非常有用。其中之一是 PuLP,您將在下一節中看到它的實際應用。
PuLP 具有比 SciPy 更方便的線性編程 API。您不必在數學上修改您的問題或使用向量和矩陣。一切都更干凈,更不容易出錯。
像往常一樣,您首先導入您需要的內容:
現在您已經導入了 PuLP,您可以解決您的問題。
您現在將使用 PuLP 解決此系統:
第一步是初始化一個實例LpProblem來表示你的模型:
您可以使用該sense參數來選擇是執行最小化(LpMinimize或1,這是默認值)還是最大化(LpMaximize或-1)。這個選擇會影響你的問題的結果。
一旦有了模型,就可以將決策變數定義為LpVariable類的實例:
您需要提供下限,lowBound=0因為默認值為負無窮大。該參數upBound定義了上限,但您可以在此處省略它,因為它默認為正無窮大。
可選參數cat定義決策變數的類別。如果您使用的是連續變數,則可以使用默認值"Continuous"。
您可以使用變數x和y創建表示線性表達式和約束的其他 PuLP 對象:
當您將決策變數與標量相乘或構建多個決策變數的線性組合時,您會得到一個pulp.LpAffineExpression代表線性表達式的實例。
注意:您可以增加或減少變數或表達式,你可以乘他們常數,因為紙漿類實現一些Python的特殊方法,即模擬數字類型一樣__add__(),__sub__()和__mul__()。這些方法用於像定製運營商的行為+,-和*。
類似地,您可以將線性表達式、變數和標量與運算符 ==、=以獲取表示模型線性約束的紙漿.LpConstraint實例。
註:也有可能與豐富的比較方法來構建的約束.__eq__(),.__le__()以及.__ge__()定義了運營商的行為==,=。
考慮到這一點,下一步是創建約束和目標函數並將它們分配給您的模型。您不需要創建列表或矩陣。只需編寫 Python 表達式並使用+=運算符將它們附加到模型中:
在上面的代碼中,您定義了包含約束及其名稱的元組。LpProblem允許您通過將約束指定為元組來向模型添加約束。第一個元素是一個LpConstraint實例。第二個元素是該約束的可讀名稱。
設置目標函數非常相似:
或者,您可以使用更短的符號:
現在您已經添加了目標函數並定義了模型。
注意:您可以使用運算符將 約束或目標附加到模型中,+=因為它的類LpProblem實現了特殊方法.__iadd__(),該方法用於指定 的行為+=。
對於較大的問題,lpSum()與列表或其他序列一起使用通常比重復+運算符更方便。例如,您可以使用以下語句將目標函數添加到模型中:
它產生與前一條語句相同的結果。
您現在可以看到此模型的完整定義:
模型的字元串表示包含所有相關數據:變數、約束、目標及其名稱。
注意:字元串表示是通過定義特殊方法構建的.__repr__()。有關 的更多詳細信息.__repr__(),請查看Pythonic OOP 字元串轉換:__repr__vs__str__ .
最後,您已准備好解決問題。你可以通過調用.solve()你的模型對象來做到這一點。如果要使用默認求解器 (CBC),則不需要傳遞任何參數:
.solve()調用底層求解器,修改model對象,並返回解決方案的整數狀態,1如果找到了最優解。有關其餘狀態代碼,請參閱LpStatus[]。
你可以得到優化結果作為 的屬性model。該函數value()和相應的方法.value()返回屬性的實際值:
model.objective持有目標函數model.constraints的值,包含鬆弛變數的值,以及對象x和y具有決策變數的最優值。model.variables()返回一個包含決策變數的列表:
如您所見,此列表包含使用 的構造函數創建的確切對象LpVariable。
結果與您使用 SciPy 獲得的結果大致相同。
注意:注意這個方法.solve()——它會改變對象的狀態,x並且y!
您可以通過調用查看使用了哪個求解器.solver:
輸出通知您求解器是 CBC。您沒有指定求解器,因此 PuLP 調用了默認求解器。
如果要運行不同的求解器,則可以將其指定為 的參數.solve()。例如,如果您想使用 GLPK 並且已經安裝了它,那麼您可以solver=GLPK(msg=False)在最後一行使用。請記住,您還需要導入它:
現在你已經導入了 GLPK,你可以在裡面使用它.solve():
該msg參數用於顯示來自求解器的信息。msg=False禁用顯示此信息。如果要包含信息,則只需省略msg或設置msg=True。
您的模型已定義並求解,因此您可以按照與前一種情況相同的方式檢查結果:
使用 GLPK 得到的結果與使用 SciPy 和 CBC 得到的結果幾乎相同。
一起來看看這次用的是哪個求解器:
正如您在上面用突出顯示的語句定義的那樣model.solve(solver=GLPK(msg=False)),求解器是 GLPK。
您還可以使用 PuLP 來解決混合整數線性規劃問題。要定義整數或二進制變數,只需傳遞cat="Integer"或cat="Binary"到LpVariable。其他一切都保持不變:
在本例中,您有一個整數變數並獲得與之前不同的結果:
Nowx是一個整數,如模型中所指定。(從技術上講,它保存一個小數點後為零的浮點值。)這一事實改變了整個解決方案。讓我們在圖表上展示這一點:
如您所見,最佳解決方案是灰色背景上最右邊的綠點。這是兩者的最大價值的可行的解決方案x和y,給它的最大目標函數值。
GLPK 也能夠解決此類問題。
現在你可以使用 PuLP 來解決上面的資源分配問題:
定義和解決問題的方法與前面的示例相同:
在這種情況下,您使用字典 x來存儲所有決策變數。這種方法很方便,因為字典可以將決策變數的名稱或索引存儲為鍵,將相應的LpVariable對象存儲為值。列表或元組的LpVariable實例可以是有用的。
上面的代碼產生以下結果:
如您所見,該解決方案與使用 SciPy 獲得的解決方案一致。最有利可圖的解決方案是每天生產5.0第一件產品和45.0第三件產品。
讓我們把這個問題變得更復雜和有趣。假設由於機器問題,工廠無法同時生產第一種和第三種產品。在這種情況下,最有利可圖的解決方案是什麼?
現在您有另一個邏輯約束:如果x ₁ 為正數,則x ₃ 必須為零,反之亦然。這是二元決策變數非常有用的地方。您將使用兩個二元決策變數y ₁ 和y ₃,它們將表示是否生成了第一個或第三個產品:
除了突出顯示的行之外,代碼與前面的示例非常相似。以下是差異:
這是解決方案:
事實證明,最佳方法是排除第一種產品而只生產第三種產品。
就像有許多資源可以幫助您學習線性規劃和混合整數線性規劃一樣,還有許多具有 Python 包裝器的求解器可用。這是部分列表:
其中一些庫,如 Gurobi,包括他們自己的 Python 包裝器。其他人使用外部包裝器。例如,您看到可以使用 PuLP 訪問 CBC 和 GLPK。
您現在知道什麼是線性規劃以及如何使用 Python 解決線性規劃問題。您還了解到 Python 線性編程庫只是本機求解器的包裝器。當求解器完成其工作時,包裝器返回解決方案狀態、決策變數值、鬆弛變數、目標函數等。