控切割路徑優(yōu)化:雙層旅行商問(wèn)題與迭代求解策略)
1. 問(wèn)題引入從一塊鋼板到最優(yōu)切割路徑五一數(shù)學(xué)建模競(jìng)賽的A題每年都像一道精心設(shè)計(jì)的“工業(yè)謎題”今年這道“鋼板最優(yōu)切割路徑問(wèn)題”也不例外。乍一看題目描述的是數(shù)控切割機(jī)如何在一塊鋼板上高效地切割出多個(gè)零件目標(biāo)是找到一條讓空程切割頭不進(jìn)行切割的移動(dòng)距離最短的路徑。這聽(tīng)起來(lái)像是一個(gè)經(jīng)典的“旅行商問(wèn)題”TSP變種——切割頭需要“訪問(wèn)”每一個(gè)待切割的圖形輪廓但又不完全一樣因?yàn)榍懈铑^在每個(gè)圖形內(nèi)部還需要沿著輪廓走完一圈。很多初次接觸的同學(xué)可能會(huì)直接套用現(xiàn)成的智能優(yōu)化算法比如遺傳算法或者模擬退火去優(yōu)化圖形的訪問(wèn)順序。但如果你真這么做了很可能第一步就走偏了。這道題的核心難點(diǎn)和魅力恰恰在于它比單純的TSP多了一層圖形內(nèi)部的切割起點(diǎn)選擇。想象一下你手里拿著一支焊槍切割頭面前是一塊大鋼板上面用粉筆畫好了十幾個(gè)不同形狀的零件輪廓。你的任務(wù)是把它們都切下來(lái)。你當(dāng)然可以決定先切圓再切方最后切那個(gè)復(fù)雜的五角星這就是訪問(wèn)順序。但當(dāng)你移動(dòng)到圓旁邊準(zhǔn)備開(kāi)切時(shí)一個(gè)新的問(wèn)題出現(xiàn)了從圓周上的哪一點(diǎn)開(kāi)始下刀你從圓的正上方點(diǎn)開(kāi)始順時(shí)針切和從正下方點(diǎn)開(kāi)始逆時(shí)針切對(duì)于完成這個(gè)圓本身來(lái)說(shuō)沒(méi)有區(qū)別但對(duì)于你切完這個(gè)圓后前往下一個(gè)方形的“空程”距離影響可就大了。這個(gè)起始點(diǎn)我們稱之為“切入點(diǎn)”或“切割起點(diǎn)”。最優(yōu)路徑 最優(yōu)的外部圖形訪問(wèn)序列 × 每個(gè)圖形內(nèi)部的最優(yōu)切割起點(diǎn)。這是一個(gè)典型的雙層優(yōu)化問(wèn)題兩層決策相互耦合這才是題目真正的“骨頭”。我見(jiàn)過(guò)很多解題思路要么只優(yōu)化序列默認(rèn)從圖形上某個(gè)固定點(diǎn)比如離上一個(gè)圖形最近的點(diǎn)開(kāi)始切要么試圖用超級(jí)復(fù)雜的算法同時(shí)優(yōu)化兩者結(jié)果陷入組合爆炸算力撐不住。今天我想分享一套經(jīng)過(guò)實(shí)戰(zhàn)檢驗(yàn)的、清晰且可實(shí)現(xiàn)的思路“分而治之迭代逼近”。我們不追求一步到位的全局最優(yōu)而是通過(guò)合理的建模和高效的局部搜索找到一個(gè)質(zhì)量非常高、邏輯清晰的可行解。下面我就把這套方法的每一個(gè)環(huán)節(jié)掰開(kāi)揉碎包括其背后的原理、具體的實(shí)現(xiàn)步驟、我踩過(guò)的坑以及能直接運(yùn)行的Python參考代碼。2. 模型構(gòu)建如何將鋼板切割抽象為數(shù)學(xué)模型面對(duì)一個(gè)實(shí)際問(wèn)題首要任務(wù)是進(jìn)行合理的抽象和簡(jiǎn)化建立數(shù)學(xué)模型。我們不能一頭扎進(jìn)代碼里必須先想清楚用什么數(shù)據(jù)結(jié)構(gòu)來(lái)表示問(wèn)題以及要優(yōu)化的是什么。2.1 關(guān)鍵概念定義與假設(shè)首先我們明確幾個(gè)關(guān)鍵概念和必要的假設(shè)這能讓模型更清晰也便于后續(xù)編程切割頭視為一個(gè)沒(méi)有大小的點(diǎn)。這在路徑規(guī)劃中是常見(jiàn)假設(shè)簡(jiǎn)化了碰撞檢測(cè)本題未強(qiáng)調(diào)碰撞故更可忽略。空程切割頭在非切割狀態(tài)下的移動(dòng)距離。核心優(yōu)化目標(biāo)就是最小化總空程。切割路徑本身的長(zhǎng)度是固定的即所有零件輪廓周長(zhǎng)之和無(wú)法優(yōu)化因此我們只關(guān)心空程。零件圖形題目中通常是多邊形矩形、三角形等和圓形。我們需要用數(shù)學(xué)方式描述它們。多邊形用一組有序的頂點(diǎn)坐標(biāo)表示例如矩形[(x1,y1), (x2,y2), (x3,y3), (x4,y4)]。切割路徑就是依次連接這些頂點(diǎn)并回到起點(diǎn)。圓形用圓心(cx, cy)和半徑r表示。切割路徑是一個(gè)圓。為了統(tǒng)一處理我們需要將圓形離散化即用正多邊形來(lái)近似。例如用360個(gè)點(diǎn)來(lái)近似一個(gè)圓足以保證精度。切入點(diǎn)在每個(gè)圖形輪廓上開(kāi)始切割的點(diǎn)。對(duì)于多邊形可以是任意頂點(diǎn)對(duì)于圓形是離散化后多邊形上的任意點(diǎn)。切割方向?qū)τ诙噙呅瓮ǔ<僭O(shè)固定為頂點(diǎn)給定的順序如順時(shí)針。改變順序可能意味著“翻轉(zhuǎn)”零件在實(shí)際切割中可能不被允許因此我們默認(rèn)固定。對(duì)于圓形方向順時(shí)針/逆時(shí)針不影響空程。基于以上我們可以做出一個(gè)至關(guān)重要的建模決策將每個(gè)零件的完整閉合輪廓的切割轉(zhuǎn)化為對(duì)一個(gè)特殊的“城市”的訪問(wèn)。這個(gè)“城市”不是一個(gè)點(diǎn)而是一條閉合的環(huán)。訪問(wèn)這個(gè)“城市”意味著1. 移動(dòng)切割頭到該環(huán)上的某個(gè)點(diǎn)切入點(diǎn)2. 完整遍歷這個(gè)環(huán)執(zhí)行切割3. 從環(huán)上的終點(diǎn)也就是切入點(diǎn)因?yàn)殚]合離開(kāi)前往下一個(gè)“城市”。2.2 數(shù)學(xué)模型的形式化表述設(shè)共有N個(gè)零件。對(duì)于零件i其輪廓離散化或表示為一系列有序點(diǎn)P_i [p_i1, p_i2, ..., p_iM_i]其中p_i1和p_iM_i首尾相連。M_i是描述該輪廓的點(diǎn)數(shù)。定義d_cut(i)為切割零件i所需的固定路徑長(zhǎng)度即其輪廓周長(zhǎng)。定義enter_point(i, k)為選擇零件i的第k個(gè)點(diǎn) (p_ik) 作為切入點(diǎn)。我們需要決策一個(gè)零件的排列順序π [π1, π2, ..., πN]表示切割的先后次序。對(duì)于每個(gè)零件πj選擇一個(gè)切入點(diǎn)索引k_j。目標(biāo)函數(shù)最小化總空程。 總路徑 初始空程(從原點(diǎn)或初始點(diǎn)到第一個(gè)零件切入點(diǎn)) Σ(零件間空程) Σ(零件切割固定長(zhǎng)度)。 由于 Σ(零件切割固定長(zhǎng)度) 是常數(shù)因此優(yōu)化目標(biāo)等價(jià)于Minimize: D_start(enter(π1)) Σ D(exit(πj), enter(π(j1)))其中D_start(p)是從切割頭初始位置到切入點(diǎn)p的距離。exit(πj)是零件πj切割結(jié)束的點(diǎn)由于切割是閉合的它等于該零件的切入點(diǎn)enter(πj)。D(a, b)是點(diǎn)a到點(diǎn)b的歐幾里得距離。至此我們成功地將一個(gè)復(fù)雜的物理切割問(wèn)題轉(zhuǎn)化為了一個(gè)清晰的組合優(yōu)化問(wèn)題為一個(gè)特殊的“旅行商”規(guī)劃路線每個(gè)“城市”允許你選擇其邊界上的任何一個(gè)點(diǎn)作為“訪問(wèn)站”。3. 核心求解策略分治與迭代逼近框架直接求解上述雙層優(yōu)化問(wèn)題非常困難。我們的策略是將其分解并迭代改進(jìn)。3.1 策略一固定切入點(diǎn)優(yōu)化訪問(wèn)序列這是簡(jiǎn)化問(wèn)題的第一步。我們暫時(shí)“凍結(jié)”內(nèi)部?jī)?yōu)化為每個(gè)零件i預(yù)先選定一個(gè)切入點(diǎn)。一個(gè)直觀簡(jiǎn)單的策略是選擇每個(gè)零件輪廓上距離鋼板中心最近的點(diǎn)或者距離上一個(gè)零件“可能位置”最近的點(diǎn)。但在迭代初期我們沒(méi)有序列信息一個(gè)穩(wěn)健的初始選擇是選擇每個(gè)零件輪廓上距離所有其他零件輪廓最近點(diǎn)的平均位置最近的那個(gè)點(diǎn)。簡(jiǎn)單起見(jiàn)初始階段我們可以選擇每個(gè)零件的一個(gè)特征點(diǎn)如多邊形重心、圓心作為其“代表點(diǎn)”但注意空程計(jì)算時(shí)移動(dòng)是從一個(gè)零件的切入點(diǎn)到另一個(gè)零件的切入點(diǎn)而不是重心到重心。假設(shè)我們已經(jīng)為每個(gè)零件i固定了一個(gè)切入點(diǎn)e_i。那么問(wèn)題退化為一個(gè)標(biāo)準(zhǔn)的非對(duì)稱旅行商問(wèn)題ATSP嗎不因?yàn)榫嚯xD(e_i, e_j)通常等于D(e_j, e_i)歐氏距離對(duì)稱所以是對(duì)稱TSP。我們的目標(biāo)是找到訪問(wèn){e_1, e_2, ..., e_N}這組點(diǎn)的一條最短哈密頓路徑如果起點(diǎn)固定則是回路。如何求解這個(gè)TSP對(duì)于N不太大比如50的情況我們可以使用模擬退火算法SA或遺傳算法GA來(lái)獲得一個(gè)優(yōu)質(zhì)解。這里我更推薦模擬退火因?yàn)樗鼘?shí)現(xiàn)簡(jiǎn)單調(diào)整參數(shù)較少適合作為核心優(yōu)化器。模擬退火算法設(shè)計(jì)要點(diǎn)狀態(tài)一個(gè)零件的排列序列π。鄰域操作采用2-opt交換兩段路徑或隨機(jī)交換兩個(gè)零件的位置。2-opt在TSP中非常有效。能量函數(shù)即目標(biāo)函數(shù)F(π) D_start(e_{π1}) Σ D(e_{πj}, e_{π(j1)})。降溫計(jì)劃初始溫度T0設(shè)置得足夠高使得初始的壞解也有較大概率被接受例如T0 1000 * F(initial)。降溫系數(shù)alpha通常取0.95到0.99。迭代次數(shù)每個(gè)溫度下的馬爾可夫鏈長(zhǎng)度設(shè)為L(zhǎng) 100 * N。終止條件溫度低于某個(gè)閾值T_min如1e-6或連續(xù)若干個(gè)溫度下最優(yōu)解未更新。注意這里存在一個(gè)常見(jiàn)的“坑”。我們計(jì)算的是點(diǎn)e_i到e_j的距離。但如果e_i和e_j不是簡(jiǎn)單的點(diǎn)而是需要從輪廓點(diǎn)集合中動(dòng)態(tài)選擇呢這就是我們下一步要解凍的。在第一步我們強(qiáng)行固定了e_i所以計(jì)算是直接的。這個(gè)固定策略為我們提供了一個(gè)基準(zhǔn)序列。3.2 策略二固定訪問(wèn)序列優(yōu)化每個(gè)零件的切入點(diǎn)現(xiàn)在假設(shè)我們通過(guò)上一步得到了一個(gè)零件訪問(wèn)序列π。序列固定了但每個(gè)零件的切入點(diǎn)e_{πj}還沒(méi)定。這時(shí)優(yōu)化問(wèn)題變成了一個(gè)動(dòng)態(tài)規(guī)劃DP問(wèn)題或者可以通過(guò)貪婪局部搜索高效求解。問(wèn)題描述給定序列π1 - π2 - ... - πN。對(duì)于零件πj其切入點(diǎn)可以從其輪廓點(diǎn)集P_{πj}中任選一點(diǎn)p。我們需要為每個(gè)零件選擇切入點(diǎn)使得序列總空程最小。 總空程S D_start(p1) D(p1, p2) D(p2, p3) ... D(p_{N-1}, p_N)其中pj ∈ P_{πj}。這實(shí)際上是一個(gè)多階段決策問(wèn)題非常適合用動(dòng)態(tài)規(guī)劃求解。動(dòng)態(tài)規(guī)劃狀態(tài)定義dp[j][k]表示切割完前j個(gè)零件即π1, ..., πj并且第j個(gè)零件πj選擇其第k個(gè)點(diǎn)作為切入點(diǎn)時(shí)所累積的最小空程。這里j從1到Nk是零件πj輪廓點(diǎn)的索引0 到M_{πj}-1。狀態(tài)轉(zhuǎn)移方程dp[j][k] min_{t} { dp[j-1][t] D( p_{π(j-1), t}, p_{πj, k} ) }其中p_{π(j-1), t}表示零件π(j-1)的第t個(gè)輪廓點(diǎn)。 對(duì)于j1第一個(gè)零件dp[1][k] D_start( p_{π1, k} )最終答案min_{k} dp[N][k]通過(guò)DP我們可以在O(N * M^2)的時(shí)間內(nèi)找到給定序列下的最優(yōu)切入點(diǎn)組合M是平均輪廓點(diǎn)數(shù)。如果M很大如圓形離散化為360點(diǎn)這個(gè)復(fù)雜度會(huì)很高。此時(shí)可以采用近似DP或貪婪法對(duì)于零件πj在固定π(j-1)切入點(diǎn)的情況下選擇πj輪廓上距離該點(diǎn)最近的點(diǎn)作為切入點(diǎn)。這種貪婪法一次遍歷即可復(fù)雜度O(N * M)雖然不能保證全局最優(yōu)但效果通常很好且可以作為DP的初始解或快速迭代工具。3.3 策略三迭代反饋與整體優(yōu)化框架現(xiàn)在我們把策略一和策略二結(jié)合起來(lái)形成一個(gè)迭代優(yōu)化框架初始化為每個(gè)零件i隨機(jī)選擇一個(gè)初始切入點(diǎn)e_i例如輪廓上的第一個(gè)點(diǎn)。或者使用一個(gè)簡(jiǎn)單啟發(fā)式選擇距離所有零件幾何中心最近的點(diǎn)。外層循環(huán)迭代若干次例如10-20次或直到目標(biāo)函數(shù)收斂。 a.階段A優(yōu)化序列。固定當(dāng)前所有零件的切入點(diǎn){e_i}將其視為TSP的城市使用模擬退火算法求解最優(yōu)訪問(wèn)序列π。 b.階段B優(yōu)化切入點(diǎn)。固定上一步得到的最優(yōu)序列π使用動(dòng)態(tài)規(guī)劃或貪婪法為序列中的每個(gè)零件重新計(jì)算最優(yōu)切入點(diǎn){e_i_new}。 c.更新與評(píng)估用新切入點(diǎn){e_i_new}替換舊的{e_i}。計(jì)算新配置下的總空程。如果優(yōu)于歷史最優(yōu)則更新歷史最優(yōu)解。 d.降溫或擾動(dòng)模擬退火的外層也可以引入“溫度”概念以一定概率接受變差的切入點(diǎn)更新避免陷入局部最優(yōu)。或者簡(jiǎn)單地在切入點(diǎn)更新后對(duì)序列進(jìn)行小幅擾動(dòng)重新進(jìn)入階段A。這個(gè)框架將復(fù)雜的雙層問(wèn)題分解為兩個(gè)相對(duì)簡(jiǎn)單的子問(wèn)題并讓它們相互指導(dǎo)、迭代改進(jìn)。在實(shí)際編程中它非常有效通常能在短時(shí)間內(nèi)收斂到一個(gè)滿意的解。4. 代碼實(shí)現(xiàn)詳解與關(guān)鍵技巧理論說(shuō)完我們來(lái)點(diǎn)實(shí)在的。以下是用Python實(shí)現(xiàn)上述核心框架的關(guān)鍵部分。我會(huì)用注釋解釋每一步并分享一些調(diào)試和優(yōu)化技巧。4.1 數(shù)據(jù)結(jié)構(gòu)定義首先定義零件Part類用于存儲(chǔ)輪廓信息和管理切入點(diǎn)。import numpy as np import math import random import itertools class Part: def __init__(self, part_id, contour_points): 初始化一個(gè)零件。 :param part_id: 零件ID :param contour_points: 輪廓點(diǎn)列表形狀為 (n, 2) 的numpy數(shù)組表示n個(gè)點(diǎn)的(x, y)坐標(biāo)。 注意點(diǎn)應(yīng)該是閉合的即 contour_points[0] 和 contour_points[-1] 應(yīng)相同或非常接近。 self.id part_id self.contour np.array(contour_points) # 確保輪廓是閉合的 if not np.allclose(self.contour[0], self.contour[-1]): self.contour np.vstack([self.contour, self.contour[0:1]]) self.num_points len(self.contour) # 當(dāng)前選擇的切入點(diǎn)索引 self.entry_idx 0 # 預(yù)計(jì)算輪廓周長(zhǎng)切割固定長(zhǎng)度 self.perimeter self._calculate_perimeter() def _calculate_perimeter(self): 計(jì)算輪廓周長(zhǎng)。 perimeter 0.0 for i in range(self.num_points - 1): perimeter np.linalg.norm(self.contour[i1] - self.contour[i]) return perimeter def get_entry_point(self): 返回當(dāng)前切入點(diǎn)的坐標(biāo)。 return self.contour[self.entry_idx] def set_entry_by_point(self, point): 給定一個(gè)坐標(biāo)點(diǎn)選擇輪廓上離該點(diǎn)最近的點(diǎn)作為切入點(diǎn)。返回該點(diǎn)索引。 distances np.linalg.norm(self.contour - point, axis1) self.entry_idx np.argmin(distances) return self.entry_idx def get_closest_point_idx(self, point): 返回輪廓上離給定點(diǎn)最近的點(diǎn)的索引。 distances np.linalg.norm(self.contour - point, axis1) return np.argmin(distances)4.2 距離計(jì)算與目標(biāo)函數(shù)目標(biāo)函數(shù)是空程我們需要高效計(jì)算。def euclidean_distance(p1, p2): 計(jì)算兩點(diǎn)間歐氏距離。 return np.linalg.norm(p1 - p2) def calculate_total_idle_distance(parts, sequence, start_pointnp.array([0.0, 0.0])): 計(jì)算給定零件列表、訪問(wèn)序列和切入點(diǎn)選擇下的總空程。 :param parts: Part對(duì)象列表 :param sequence: 零件索引的列表表示訪問(wèn)順序 :param start_point: 切割頭起始位置 :return: 總空程 total_distance 0.0 current_pos start_point for part_idx in sequence: part parts[part_idx] entry_point part.get_entry_point() total_distance euclidean_distance(current_pos, entry_point) current_pos entry_point # 切割后切割頭仍在該點(diǎn) return total_distance4.3 模擬退火求解TSP固定切入點(diǎn)這是策略一的核心。def simulated_annealing_tsp(parts, initial_sequence, start_point, max_iter5000, t0100.0, alpha0.95): 使用模擬退火求解TSP零件訪問(wèn)序列。 :param parts: Part對(duì)象列表切入點(diǎn)已固定。 :param initial_sequence: 初始序列 :param start_point: 起始點(diǎn) :param max_iter: 最大迭代次數(shù) :param t0: 初始溫度 :param alpha: 降溫系數(shù) :return: (best_sequence, best_distance) current_seq initial_sequence[:] best_seq current_seq[:] current_dist calculate_total_idle_distance(parts, current_seq, start_point) best_dist current_dist t t0 n len(parts) # 每個(gè)溫度下的迭代次數(shù)與問(wèn)題規(guī)模相關(guān) lk n * 10 for iter in range(max_iter): for _ in range(lk): # 鄰域操作隨機(jī)交換兩個(gè)位置另一種常用是2-opt這里用簡(jiǎn)單交換 i, j random.sample(range(n), 2) new_seq current_seq[:] new_seq[i], new_seq[j] new_seq[j], new_seq[i] new_dist calculate_total_idle_distance(parts, new_seq, start_point) delta new_dist - current_dist # 接受更差解的概率 if delta 0 or random.random() math.exp(-delta / t): current_seq, current_dist new_seq, new_dist if current_dist best_dist: best_seq, best_dist current_seq[:], current_dist # 降溫 t * alpha if t 1e-6: break return best_seq, best_dist4.4 動(dòng)態(tài)規(guī)劃優(yōu)化切入點(diǎn)固定序列這是策略二的核心。這里實(shí)現(xiàn)貪婪法最近點(diǎn)作為示例因?yàn)樗?jiǎn)單高效。DP版本更精確但代碼稍長(zhǎng)。def greedy_optimize_entry_points(parts, sequence, start_point): 貪婪法優(yōu)化切入點(diǎn)固定序列每個(gè)零件選擇離上一個(gè)點(diǎn)最近的輪廓點(diǎn)作為切入點(diǎn)。 :param parts: Part對(duì)象列表 :param sequence: 固定好的零件訪問(wèn)序列 :param start_point: 起始點(diǎn) :return: 更新了切入點(diǎn)的parts列表以及新的總空程 current_pos start_point total_idle 0.0 for part_idx in sequence: part parts[part_idx] # 找到離current_pos最近的輪廓點(diǎn)并設(shè)置為切入點(diǎn) closest_idx part.get_closest_point_idx(current_pos) part.entry_idx closest_idx entry_point part.get_entry_point() total_idle euclidean_distance(current_pos, entry_point) current_pos entry_point return parts, total_idle4.5 主迭代框架將以上模塊組合起來(lái)。def solve_cutting_path(parts, start_pointnp.array([0.0, 0.0]), max_outer_iter20): 主求解函數(shù)迭代優(yōu)化序列和切入點(diǎn)。 :param parts: Part對(duì)象列表 :param start_point: 起始點(diǎn) :param max_outer_iter: 外層最大迭代次數(shù) :return: (best_sequence, best_parts, best_total_idle) n len(parts) # 初始化隨機(jī)序列每個(gè)零件隨機(jī)切入點(diǎn) initial_sequence list(range(n)) random.shuffle(initial_sequence) for part in parts: part.entry_idx random.randint(0, part.num_points - 1) best_sequence initial_sequence[:] best_parts [part for part in parts] # 注意這里需要深拷貝簡(jiǎn)單起見(jiàn)用重新賦值切入點(diǎn)的方式 best_idle calculate_total_idle_distance(parts, best_sequence, start_point) current_sequence initial_sequence[:] current_parts parts for outer_iter in range(max_outer_iter): print(fIteration {outer_iter1}: Current best idle {best_idle:.2f}) # 階段A: 固定切入點(diǎn)優(yōu)化序列 # 注意simulated_annealing_tsp內(nèi)部計(jì)算距離時(shí)使用的是parts當(dāng)前的切入點(diǎn) new_sequence, seq_idle simulated_annealing_tsp( current_parts, current_sequence, start_point, max_iter1000, t050.0 ) # 階段B: 固定新序列貪婪優(yōu)化切入點(diǎn) # 注意greedy_optimize_entry_points會(huì)修改parts對(duì)象的entry_idx updated_parts, new_idle greedy_optimize_entry_points( current_parts, new_sequence, start_point ) # 更新當(dāng)前狀態(tài) current_sequence new_sequence current_parts updated_parts # 對(duì)象已更新 # 更新全局最優(yōu) if new_idle best_idle: best_idle new_idle best_sequence new_sequence[:] # 保存最優(yōu)狀態(tài)下的切入點(diǎn)選擇 for i, part in enumerate(current_parts): best_parts[i].entry_idx part.entry_idx # 簡(jiǎn)單收斂判斷如果連續(xù)幾次迭代沒(méi)有改進(jìn)可以提前終止 # 這里省略為了演示運(yùn)行完整迭代 # 計(jì)算最終的總路徑空程 固定切割長(zhǎng)度 total_cut_length sum(part.perimeter for part in best_parts) total_path_length best_idle total_cut_length print(f\nOptimization Finished.) print(fBest sequence: {best_sequence}) print(fBest idle distance: {best_idle:.2f}) print(fTotal cut length (fixed): {total_cut_length:.2f}) print(fTotal path length: {total_path_length:.2f}) return best_sequence, best_parts, best_idle4.6 關(guān)鍵技巧與避坑指南圓形離散化如果零件包含圓形務(wù)必將其離散化為足夠多的點(diǎn)如360。點(diǎn)數(shù)太少會(huì)導(dǎo)致“最近點(diǎn)搜索”誤差大可能錯(cuò)過(guò)真正最優(yōu)的切入點(diǎn)。在Part初始化時(shí)完成此操作。起始點(diǎn)處理切割頭通常從一個(gè)“原點(diǎn)”或“換刀點(diǎn)”開(kāi)始。我們的模型將D_start納入目標(biāo)函數(shù)是正確的。確保start_point參數(shù)設(shè)置正確。模擬退火參數(shù)調(diào)優(yōu)t0初始溫度和alpha降溫系數(shù)需要根據(jù)問(wèn)題規(guī)模調(diào)整。如果接受壞解的概率一開(kāi)始就太低算法容易陷入局部最優(yōu)如果降溫太快搜索可能不充分。一個(gè)實(shí)用的技巧是讓t0與初始解的目標(biāo)函數(shù)值相關(guān)聯(lián)如t0 10 * initial_distance并觀察前幾百次迭代中壞解的接受比例將其調(diào)整到30%-50%左右為宜。貪婪法與DP的選擇貪婪法最近點(diǎn)速度極快O(N*M)在迭代框架中作為默認(rèn)選擇。如果追求更高精度可以在迭代的最后幾輪或?qū)ψ罱K序列使用動(dòng)態(tài)規(guī)劃DP進(jìn)行精細(xì)優(yōu)化。DP的O(N*M^2)復(fù)雜度在M較大時(shí)是負(fù)擔(dān)但可以嘗試減少M(fèi)如對(duì)圓形只用72個(gè)點(diǎn)做DP優(yōu)化。局部最優(yōu)陷阱我們的迭代框架本質(zhì)上是交替優(yōu)化容易陷入局部最優(yōu)。為了跳出可以在外層循環(huán)中加入擾動(dòng)機(jī)制每隔幾次迭代隨機(jī)改變幾個(gè)零件的切入點(diǎn)或者對(duì)序列進(jìn)行一個(gè)較大的擾動(dòng)如隨機(jī)反轉(zhuǎn)一段序列然后重新開(kāi)始優(yōu)化。結(jié)果驗(yàn)證與可視化一定要將最終路徑畫出來(lái)使用Matplotlib將鋼板、零件輪廓、空程虛線和切割路徑實(shí)線可視化。肉眼觀察路徑是否交叉、是否明顯繞遠(yuǎn)這是發(fā)現(xiàn)模型或代碼錯(cuò)誤的最快方式。5. 從模型到論文解題思路的呈現(xiàn)與擴(kuò)展參加數(shù)學(xué)建模競(jìng)賽光有代碼和結(jié)果不夠還需要將你的思路清晰、邏輯嚴(yán)謹(jǐn)?shù)爻尸F(xiàn)在論文中。針對(duì)這道題論文的建模部分可以圍繞以下幾點(diǎn)展開(kāi)問(wèn)題重述與分析強(qiáng)調(diào)問(wèn)題的雙層決策特性序列與切入點(diǎn)指出將其直接視為TSP的不足引出分解與迭代的思想。模型假設(shè)明確列出切割頭為點(diǎn)、空程定義、圖形離散化等假設(shè)使模型邊界清晰。符號(hào)說(shuō)明規(guī)范地定義文中使用的所有變量、符號(hào)提升論文專業(yè)性。模型建立整體模型給出目標(biāo)函數(shù)總空程的數(shù)學(xué)表達(dá)式。子模型一序列優(yōu)化闡述在固定切入點(diǎn)下問(wèn)題如何轉(zhuǎn)化為TSP并說(shuō)明采用模擬退火算法的理由適用于組合優(yōu)化、能處理中等規(guī)模問(wèn)題、易實(shí)現(xiàn)。子模型二切入點(diǎn)優(yōu)化闡述在固定序列下問(wèn)題如何轉(zhuǎn)化為動(dòng)態(tài)規(guī)劃或最近點(diǎn)選擇問(wèn)題給出狀態(tài)轉(zhuǎn)移方程或貪婪策略。迭代優(yōu)化框架用流程圖展示“初始化 - 序列優(yōu)化 - 切入點(diǎn)優(yōu)化 - 更新與判斷”的完整流程體現(xiàn)分治與迭代的思想。算法步驟用偽代碼或清晰的步驟描述模擬退火、貪婪法/動(dòng)態(tài)規(guī)劃以及主循環(huán)的實(shí)現(xiàn)過(guò)程。仿真結(jié)果與分析測(cè)試數(shù)據(jù)自己構(gòu)造幾組不同數(shù)量、不同形狀的零件數(shù)據(jù)包括簡(jiǎn)單和復(fù)雜案例。結(jié)果展示提供優(yōu)化前后的空程對(duì)比數(shù)據(jù)表格。務(wù)必附上路徑可視化圖這是最直觀的證據(jù)。靈敏度分析探討關(guān)鍵參數(shù)如模擬退火的初始溫度、降溫系數(shù)圓形離散化點(diǎn)數(shù)對(duì)結(jié)果的影響。可以設(shè)計(jì)控制變量實(shí)驗(yàn)用圖表展示結(jié)果變化體現(xiàn)研究的深度。算法對(duì)比可以簡(jiǎn)單對(duì)比純貪婪算法最近鄰法、只優(yōu)化序列不優(yōu)化切入點(diǎn)等方法突出你模型的優(yōu)越性。模型評(píng)價(jià)與推廣優(yōu)點(diǎn)分解思想降低復(fù)雜度迭代框架保證解的質(zhì)量模型通用性強(qiáng)可處理任意多邊形和圓形。缺點(diǎn)迭代法不能保證全局最優(yōu)對(duì)于零件數(shù)量極大500的情況計(jì)算時(shí)間可能較長(zhǎng)。推廣可擴(kuò)展到考慮切割頭加速度、不同切割速度、多切割頭協(xié)同等實(shí)際場(chǎng)景。記住數(shù)學(xué)建模論文看重的是解決問(wèn)題的思路過(guò)程而不僅僅是最終答案。你的模型是否合理、算法是否有效、分析是否全面這些才是評(píng)委關(guān)注的重點(diǎn)。本文提供的框架和代碼為你搭建了一個(gè)堅(jiān)實(shí)的起點(diǎn)你需要做的是理解它、運(yùn)用它并根據(jù)題目具體數(shù)據(jù)和要求進(jìn)行調(diào)整與深化。