境自檢到建模求解的完整避坑指南)
簡介Lingo8.0.zip 面向數(shù)學(xué)建模競賽選手、運(yùn)籌優(yōu)化學(xué)習(xí)者及高校相關(guān)課程師生提供一套完整的線性與非線性優(yōu)化求解環(huán)境。LINGO 全稱 Linear INteractive and General Optimizer可快速構(gòu)建并求解線性、非線性及整數(shù)最優(yōu)化模型內(nèi)置建模語言、問題編輯環(huán)境、Excel 與數(shù)據(jù)庫讀寫接口以及多種求解程序適合賽前熟悉建模流程與驗(yàn)證算法思路。壓縮包共 415 個文件約 9.28MB以 lg4 模型文件為主輔以 exe 可執(zhí)行程序、dll 動態(tài)庫、h 頭文件、xls 數(shù)據(jù)表及 cpp、java、class 等源碼與編譯產(chǎn)物另含 lng、ltf、ldt 等 LINGO 專用格式目錄結(jié)構(gòu)完整便于按模塊查閱與二次開發(fā)。目前已有 190 人學(xué)習(xí)下載可作為數(shù)學(xué)建模備賽中快速上手優(yōu)化求解的實(shí)用工具包。1. 拿到 Lingo8.0.zip 之后一個求解器壓縮包到底該怎么用很多人第一次看到 Lingo8.0.zip 這個文件名第一反應(yīng)是“解壓完點(diǎn) exe 就能用”結(jié)果雙擊運(yùn)行報錯、模型跑不出結(jié)果、許可證彈窗反復(fù)出現(xiàn)。Lingo 是一套面向線性、非線性、整數(shù)規(guī)劃的建模與求解環(huán)境它的壓縮包通常包含主程序、求解器動態(tài)庫、示例模型和授權(quán)文件幾個部分。你拿到的這個包大概率是某個課程或項(xiàng)目里流轉(zhuǎn)出來的完整安裝目錄而不是一個綠色免安裝的單文件。這意味著它能不能跑起來取決于三件事目錄結(jié)構(gòu)是否完整、授權(quán)文件是否放對位置、以及你的模型寫法是否符合它的語法約束。這篇筆記面向的是已經(jīng)拿到包、準(zhǔn)備在本地把優(yōu)化模型跑通的人從解壓到建模到排錯把每一步講清楚。2. 解壓與目錄結(jié)構(gòu)先搞清楚每個文件夾在干什么2.1 解壓后必須確認(rèn)的四類文件把 Lingo8.0.zip 解壓到一個純英文、無空格的路徑下比如D:\work\lingo80。中文路徑和空格是這類老牌求解器的經(jīng)典翻車點(diǎn)程序內(nèi)部拼接路徑時不會做轉(zhuǎn)義輕則找不到授權(quán)重則直接閃退。解壓完成后先別急著運(yùn)行按下面這張表核對目錄內(nèi)容。文件/目錄作用缺失后果主程序可執(zhí)行文件啟動建模界面無法打開求解器動態(tài)庫.dll實(shí)際執(zhí)行求解算法能建模但求解報錯授權(quán)文件.lic 或類似校驗(yàn)使用權(quán)限彈窗提示未授權(quán)示例模型目錄官方樣例用于驗(yàn)證環(huán)境不影響運(yùn)行但少了對拍基準(zhǔn)常見做法是先把示例模型目錄里的一個標(biāo)準(zhǔn)線性規(guī)劃例子跑一遍能出結(jié)果就說明環(huán)境和授權(quán)都沒問題。如果示例都跑不動后面自己寫的模型更不可能跑通先解決環(huán)境再談建模。2.2 授權(quán)文件的放置位置與命名授權(quán)問題是 Lingo8.0.zip 這類包最高頻的坑。授權(quán)文件必須和主程序在同一目錄或者放在程序約定的固定子目錄下具體取決于包內(nèi)說明。命名不能隨意改很多授權(quán)校驗(yàn)是拿文件名做匹配的。如果你把xxx.lic改成license.lic程序就認(rèn)不出來。一個穩(wěn)妥的驗(yàn)證流程是先原樣解壓不改任何文件名直接運(yùn)行主程序。如果彈出授權(quán)相關(guān)提示再去看包內(nèi)是否有說明文件指明授權(quán)路徑。不要憑感覺把授權(quán)文件到處復(fù)制復(fù)制到多個位置反而可能觸發(fā)校驗(yàn)沖突。# 以 Windows 為例解壓后進(jìn)入目錄列出關(guān)鍵文件確認(rèn)結(jié)構(gòu) cd /d D:\work\lingo80 dir /b # 預(yù)期能看到主程序、若干 .dll、授權(quán)文件、示例模型目錄 # 如果 .dll 數(shù)量明顯偏少說明壓縮包可能不完整這段命令的作用是快速核對目錄內(nèi)容。dir /b只列文件名輸出干凈方便你數(shù)動態(tài)庫的數(shù)量。參數(shù)上沒什么可調(diào)的重點(diǎn)是看.dll是否成組出現(xiàn)——求解器通常依賴多個動態(tài)庫缺一個就會在求解階段報“找不到入口點(diǎn)”之類的錯誤而不是在啟動時報錯這種延遲報錯最容易讓人誤以為是模型問題。2.3 用示例模型做一次環(huán)境自檢環(huán)境自檢不要自己寫模型直接用包內(nèi)示例。找一個目標(biāo)函數(shù)加若干約束的線性規(guī)劃樣例點(diǎn)求解看是否返回最優(yōu)值和求解狀態(tài)。如果返回“infeasible”或“unbounded”先別懷疑模型換一個明顯有解的樣例再試。示例都異?;究梢耘卸ㄊ黔h(huán)境或授權(quán)問題。提示環(huán)境自檢通過之前不要開始調(diào)自己的模型。把環(huán)境問題和建模問題混在一起排查是最浪費(fèi)時間的做法。3. 從零寫一個可求解的模型語法、參數(shù)與求解狀態(tài)3.1 建模語言的基本骨架Lingo 的建模語言是聲明式的核心結(jié)構(gòu)是集合段、數(shù)據(jù)段、目標(biāo)函數(shù)和約束。下面是一個最小可運(yùn)行的生產(chǎn)計(jì)劃模型兩個產(chǎn)品、兩種資源約束目標(biāo)是利潤最大化。MODEL: SETS: PRODUCT /A, B/: profit, x; RESOURCE /R1, R2/: capacity; USAGE(PRODUCT, RESOURCE): consume; ENDSETS DATA: profit 30 40; capacity 100 80; consume 2 1 1 3; ENDDATA MAX SUM(PRODUCT(i): profit(i) * x(i)); FOR(RESOURCE(j): SUM(PRODUCT(i): consume(i, j) * x(i)) capacity(j) ); FOR(PRODUCT(i): x(i) 0); END這段代碼的邏輯分四塊。SETS段定義了兩個集合產(chǎn)品集合和資源集合以及一個二維的消耗系數(shù)集合。DATA段給參數(shù)賦值consume的寫法是按行對應(yīng)產(chǎn)品、按列對應(yīng)資源。目標(biāo)函數(shù)用SUM對每個產(chǎn)品的利潤乘產(chǎn)量求和。約束部分用FOR遍歷每種資源保證消耗不超過產(chǎn)能。最后的非負(fù)約束是線性規(guī)劃的標(biāo)準(zhǔn)要求。參數(shù)說明上profit和capacity是一維數(shù)組賦值順序必須和集合成員的聲明順序一致寫反了不會報錯但結(jié)果會完全錯掉這是最隱蔽的坑之一。consume是二維數(shù)組賦值時按行書寫換行只是可讀性不影響解析。3.2 求解狀態(tài)怎么看別只看目標(biāo)值點(diǎn)求解之后界面會返回一個求解狀態(tài)。很多人只看目標(biāo)函數(shù)值忽略了狀態(tài)字段。常見狀態(tài)有全局最優(yōu)、局部最優(yōu)、不可行、無界、迭代超限。非線性模型默認(rèn)可能只給局部最優(yōu)這時候目標(biāo)值再好看也不能直接用。狀態(tài)含義處理方式Global Optimum全局最優(yōu)可直接采用Local Optimum局部最優(yōu)換初值或啟用全局求解器Infeasible約束沖突檢查約束是否互相矛盾Unbounded目標(biāo)無界檢查是否漏了上界約束Iteration Limit迭代超限放寬迭代上限或簡化模型不可行是最常見的狀態(tài)原因通常是約束寫得太緊或者符號寫反。排查方法是把約束逐條注釋掉看哪一條去掉之后變可行逐步定位沖突約束。3.3 整數(shù)變量與求解器選擇當(dāng)模型里出現(xiàn)整數(shù)或 0-1 變量時要用GIN或BIN聲明。整數(shù)規(guī)劃的求解時間對變量規(guī)模非常敏感幾十個整數(shù)變量可能秒解上百個就可能跑很久。! 在約束段之后追加整數(shù)聲明; FOR(PRODUCT(i): GIN(x(i)));GIN表示一般整數(shù)BIN表示 0-1 變量。聲明位置放在約束之后、END之前即可。參數(shù)上整數(shù)規(guī)劃可以設(shè)置求解時間上限和相對間隙容忍度間隙設(shè)得越松求解越快但解的質(zhì)量越差。實(shí)際項(xiàng)目里我一般先把間隙設(shè)到百分之一到百分之五拿到可行解之后再收緊做精調(diào)。4. 避坑與排查五條血淚經(jīng)驗(yàn)4.1 中文路徑導(dǎo)致授權(quán)校驗(yàn)失敗現(xiàn)象是程序啟動正常但一求解就提示未授權(quán)或直接退出。原因是授權(quán)校驗(yàn)?zāi)K對路徑做了字符串處理中文字符導(dǎo)致匹配失敗。解決辦法是把整個目錄移到純英文路徑下重新解壓一次不要只改文件夾名因?yàn)椴糠峙渲美飳懰懒嗽悸窂健?.2 動態(tài)庫缺失引發(fā)的延遲報錯現(xiàn)象是能打開界面、能寫模型但點(diǎn)求解時報“找不到指定模塊”。原因是求解器依賴的動態(tài)庫沒解壓全或者被殺毒軟件隔離了。解決辦法是核對目錄內(nèi).dll數(shù)量必要時關(guān)閉實(shí)時防護(hù)重新解壓再把目錄加入白名單。4.3 集合成員順序與數(shù)據(jù)賦值錯位現(xiàn)象是模型能求解但結(jié)果明顯不合理。原因是DATA段里參數(shù)的賦值順序和SETS段成員聲明順序不一致。解決辦法是養(yǎng)成習(xí)慣聲明集合時按字母或業(yè)務(wù)順序排列賦值時逐項(xiàng)對照不要憑記憶。4.4 非線性模型誤用局部求解器現(xiàn)象是每次求解結(jié)果不一樣或者換臺機(jī)器結(jié)果就變了。原因是模型含非線性項(xiàng)默認(rèn)求解器只保證局部最優(yōu)。解決辦法是確認(rèn)模型是否真的非線性如果是啟用全局求解選項(xiàng)或者提供多個初值對比結(jié)果。4.5 迭代上限過低導(dǎo)致假不可行現(xiàn)象是復(fù)雜模型報不可行但簡化后又能求解。原因是迭代次數(shù)達(dá)到上限求解器提前終止并返回了不可行狀態(tài)。解決辦法是適當(dāng)放寬迭代上限和求解時間再看真實(shí)狀態(tài)。5. 進(jìn)階技巧用腳本批量跑參數(shù)掃描單次求解只能得到一個場景的結(jié)果實(shí)際做方案對比時往往要掃描多個參數(shù)組合。手動改數(shù)據(jù)再點(diǎn)求解效率太低可以用 Lingo 的腳本接口做批量調(diào)用。下面是一個思路示例用命令行方式驅(qū)動求解并收集結(jié)果。# 假設(shè)主程序支持命令行傳入模型文件 # 循環(huán)修改數(shù)據(jù)文件中的參數(shù)逐個求解并輸出結(jié)果 for /l %i in (1,1,10) do ( echo 正在求解第 %i 組參數(shù) lingo_run.exe model.lng result_%i.txt )這段腳本的核心是把模型文件和數(shù)據(jù)分離每次只改數(shù)據(jù)文件然后調(diào)用求解器。參數(shù)上循環(huán)次數(shù)對應(yīng)你要掃描的場景數(shù)輸出重定向到獨(dú)立文件方便后續(xù)匯總。實(shí)際使用時主程序是否支持命令行、參數(shù)如何傳入要以包內(nèi)說明為準(zhǔn)不同版本差異較大。更穩(wěn)妥的做法是把參數(shù)掃描邏輯寫在模型內(nèi)部用ODBC或文件讀寫函數(shù)在求解過程中動態(tài)改數(shù)據(jù)這樣不用反復(fù)啟動程序速度更快。但這對語法要求更高建議先把單次求解跑穩(wěn)再上批量。我自己的習(xí)慣是任何優(yōu)化模型先用手算能驗(yàn)證的小規(guī)模樣例跑通確認(rèn)目標(biāo)值和約束都符合預(yù)期再放大到真實(shí)規(guī)模。小樣例對不上大模型的結(jié)果就沒有可信度。這個習(xí)慣幫我省掉了無數(shù)次“結(jié)果看起來合理但其實(shí)是錯的”的后悔藥。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取