【项目总结】2018年全国大学生数学建模大赛B题简要分析(附代码)( q8 `2 ]% \$ r T
# o; J" _* t- l2 p. ]" P1 p J+ y
今天早上跟学姐室友去复旦把论文答辩做掉了,虽然整个项目基本上是我承担了主要的思路与代码部分,但是今天答辩我跟室友竟然连一句有用的话都没说出来,全场都靠学姐开场流畅地引入,临场随机应变,从而得以与答辩教授欢聚喜散。主要原因是教授竟然准确地问到了我代码里一个细节却相当致命的问题(是一个随机初始化问题,我下面代码部分会详细提到),正好学姐室友都不是特别熟悉我的随机初始化方法,我又不能当场跟他们两个解释这个随机初始化的问题。我差点当场就要以“这样随机初始化能够减少代码量”这种蹩脚的理由跟教授争辩了。好在姜还是老的辣,辩论队队长出身的学姐一顿 Speech Art 操作成功忽悠掉了两位教授,最终两位答辩教授还是认可了我们的模拟仿真方法[捂脸]。事后细想以后我成功也好,失败也罢,恐怕也是成也言语,败也言语。也许我确实能够成为一个有能力的人,但是说话艺术确实是一门很大的学问。不过看我运气一直这么差,大概率还是凡人一个落入俗套吧[摊手]。 6 `4 W5 f) O+ H8 Z3 p- t2 t7 ? c: K) t! H
言归正传,本文主要介绍我们小组解决2018年全国大学生B题的思路分析,不代表标准答案。当然我还是有自知之明,本身水平不是很高,再加上三天时间限制,自己做出来的模型以及算法肯定是比较差的。这里仅仅从我个人的思考角度出发写一些参考思路作为分享讨论,希望各位读者朋友轻喷。 - q/ K# W% O, j+ @9 W5 w2 S/ C: [1 }
问题分析 8 c. e& x4 M1 f' p 3 O% h1 y/ o- n: a G6 ?( b( w今年的B题确实与往年有很大的不同。往年的数学建模问题往往具有比较好的开放性,问题解决存在较大的建模空间。今年的B题的题干本身就几乎是一个明确的模型(8台CNC+1台RGV+CNC定址),加上第二道任务要求我们根据给定三组数据完成八小时内的RGV详细调度方案,并写入四张Excel表格,给人的感觉就是要求我们去完成一道填空题,然后附带写一篇论文[尴尬]。/ I( I2 b+ b. e. P$ B. r
/ G' v% q! g& K+ q
为了方便各位读者对赛题的阅读,这里给出链接:https://download.csdn.net/download/cy19980216/10708725 $ O. K; j* }1 G% ]0 j% P. ?& d# U7 L) D
问题一共有四种不同的情况:一道工序无故障,一道工序有故障,两道工序无故障,两道工序有故障。! a; g2 ~# A+ m( D8 w& w- U2 |' f
# A4 M7 H" M L' Z+ H# k
一道工序无故障0 k' i$ P R2 W5 O/ z
+ D, r$ u/ c# ^8 B2 h# L
第一种情况是最简单的,直观上直接不停地1234567812345678……按顺序上料差不多就是最优了。但严谨地来说,虽然题目中给的三组数据确实都是用这种最幼稚的策略能够达到最优,但是如果对于一般的情况而言,比如最极端的情况下,RGV移动时间无穷大,那RGV显然就只会不停地在121212121212……这样原地上下料了。 * C3 g8 b. K% S/ D5 C& J& V5 y( `. \' R" n
然而我们发现无论参数怎么变化,最终RGV给CNC上下料的过程始终是一个周期性过程。当然这个似乎很“显然”的事实却是相当难以通过数学严格证明的(参数已知的情况下一般比较容易证明,但是所有的参数都是未知的情况下是很难严格说明的)。我赛后也仔细的思考过,但是也没有得出很漂亮的证明。我最终仅仅是针对给定的三组数据使用了遗传算法对RGV前17次上下料(17次是考虑从初始状态开始循环两圈的最短路径)的最优路径进行了搜索,并且利用穷举证明了这是前17步最优的上下料次序。之后基本上就是不断地循环。4 @4 X3 J+ j( v& Q N
; u& y, v/ I+ d1 ^' \' A; P6 z
这里的模拟退火遗传算法比较鸡肋,所以我不详细说明,在第三种情况我会详细说明模拟退火遗传算法的原理。 ; G/ J# |& |4 F" `2 a9 }% S1 x p- l, b4 B% b
以下给出第一种情况的模拟退火遗传算法算法以及对应的穷举最优证明 ↓↓↓6 N; I# m( h2 a* I1 f
# -*- coding:UTF-8 -*-8 n( w3 O9 Q, O, ~
""" ) k, }3 F6 G' |4 {$ y 作者:囚生CY $ |7 G. g6 b3 J7 P' G$ ~% I# N+ j# a 平台:CSDN3 Z% ^) T6 |) P! P7 i
时间:2018/10/09 + B; K/ @, H5 ] 转载请注明原作者; P3 t- Y0 ?5 ]/ |8 E7 I9 Q
创作不易,仅供分享5 s& |8 _( D" m# u- b1 f
"""1 `, |% e: M$ a. t' f0 H
! Z. @: ^3 o+ G+ a% b1 Vimport math 9 X6 {* H6 L+ `9 {2 \7 pimport random) J) q$ K0 N9 ]
import itertools : \ t0 A! ~9 X8 ~1 `2 o! z # c% j6 p: ?# Z" \/ M" n9 G4 q""" 选取一组数据 """; u) R6 Q1 G/ `/ G- P. k, S6 C
T = 580 $ P a( z4 h- J. md1 = 23" g- X: u/ ~: h5 e
d2 = 413 W! c W! D8 g) x9 m
d3 = 59 0 R) n/ Q N, b0 _% Q9 rTe = 35 * u4 I+ j1 v# ^" t$ T/ k$ q% P6 xTo = 30 , |" l. W3 {, U9 i% V! vTc = 30& { ~' l5 z- K5 p
; }+ H, D* {0 Z4 `5 a2 d
CNCT = [To,Te,To,Te,To,Te,To,Te] # CNC上下料时间 # ~9 d9 a7 G e! {/ q 5 g$ ~3 |6 n- h. b) R: Q0 M6 s/ [( }N = 50 - p. Y# P' _) s# h" [L = 17/ f: @& h0 ?, U7 o9 L
- _1 f9 S4 k- }* b( P8 @: U) N4 E
varP = 0.1 : v# a+ c8 h! v9 a1 RcroP = 0.6$ o7 @& L% d, N* S! Y i n
8 F' r* F/ L) ]+ [( G
croL = 4 ; n/ P) t3 I9 r; ?6 {e = 0.991 ^! v x, Z. P t; _: p6 k+ B5 {$ f
1 N, a- J. @1 ?2 L! y+ E
tm = [ % A: m9 V7 T8 {" w! Y. K [0,0,d1,d1,d2,d2,d3,d3],. S, R) U% p# y6 m
[0,0,d1,d1,d2,d2,d3,d3],9 i- E" d7 B$ N
[d1,d1,0,0,d1,d1,d2,d2], 4 T! @* J8 w: C) I& ]) } [d1,d1,0,0,d1,d1,d2,d2], ' `, x- _+ ^) v4 u4 y [d2,d2,d1,d1,0,0,d1,d1], ! Z* I* l, x ` G [d2,d2,d1,d1,0,0,d1,d1],' Z2 Q# ^2 F; d: x9 }) R
[d3,d3,d2,d2,d1,d1,0,0], ; z' v. B, W; Z( V+ d [d3,d3,d2,d2,d1,d1,0,0],2 J! Z+ N' H. S& j v
]0 y9 R0 ]4 y7 v+ }/ Q; F
, m( ]$ T" P; b9 \3 [2 U
def update_state(state,t): + _9 W! Z+ x* \ length = len(state)3 v3 \5 ]& y$ h, X; h" h# n. r
for i in range(length): ' r, u7 G- D) N; F8 [& {6 m! x if state < t: ! b/ ^" o/ x. L state = 0 # @7 A7 n7 g" D8 t else:) N: [$ g/ ^+ t" Y
state -= t ! I7 B, L! k9 v* C/ G$ i- f; ~ return state 2 f4 J4 ~" o* l* J. j6 e( M6 d4 G" `: {. r0 J+ ]) h# l
def time_calc(seq):5 w* `3 U% X6 {6 j5 E8 C6 B! h
state = [0 for i in range(8)] # 记录CNC状态 6 N: E3 X0 O* W. r$ F isEmpty = [1 for i in range(8)] # CNC是否为空? o3 ?3 `; o/ U4 Y9 B$ k7 q
currP = 0 % I2 o: C# E9 T$ o) ? total = 04 q0 |& @$ ?. B+ w; u% v
length = len(seq)( f7 U3 l: K5 `5 ]7 T: K
for No in seq:0 J$ N0 B9 d: |" y6 R
nextP = No$ x' g! t5 R1 U# m1 V
t = tm[currP][nextP]4 \5 g# }: `2 H" a
total += t # rgv移动# A$ x( P4 j+ j8 y1 o" e- r
state = update_state(state,t) # 更新state* \2 X* \7 ?+ H' w+ P+ ~
if state[No]==0: # 表明CNC等待/ m. ]$ a! A/ ~1 T
if isEmpty[No]: # 当前CNC空& [) b# V! X9 x/ ?8 F4 z
t = CNCT[No]" N5 x/ H3 ]% W% D* B# N" s
isEmpty[No] = 00 ]. ?7 t/ F) `8 V5 X- J
else:9 y' M0 n1 X9 ] k
t = CNCT[No]+Tc , K7 t3 p" R! X. Y& @ total += t 0 R% ]" Y+ ~( M state = update_state(state,t)" B7 s: H6 E, A: Z! \; t2 `: ]9 j
state[No] = T' f# _6 {4 x/ g8 z Q% p) I
else: # 当前CNC忙 # P: d$ q9 ]' S1 I3 b$ [ total += state[No] # 先等当前CNC结束8 W4 |2 J0 {6 H. T( Z8 i5 |" a
state = update_state(state,state[No]) & n. I4 R2 s, U t = CNCT[No]+Tc & G! L) d+ y$ `/ A total += t 5 ]+ ~3 ]% A, G1 M+ Y( s. {6 m" f state = update_state(state,t)# `0 q6 \8 j+ B- {' i: f
state[No] = T / Z4 E1 |8 i0 O# t. O7 f6 X currP = No/ ~1 X, k3 Q8 ?8 S
total += tm[currP][0]* B7 P* w* p) j) o; A5 D
return total " ^( X2 m: p% q' V 9 s% m% P, w! @" { [6 o4 H7 _def init_prob(sample):1 c3 Z* L8 u8 L- `, O& x& A
prob = []+ {, J7 G4 M. y; ~6 G# A( F* q
for seq in sample:' y5 C* M2 O S$ V1 \
prob.append(time_calc(seq))5 C$ @$ z6 l/ P# S7 U7 ^
maxi = max(prob)9 c; R3 \/ ?* T0 y
prob = [maxi-prob+1 for i in range(N)]# ^3 u& N; {. O; Y6 @6 x0 ^
temp = 0 ( s% Q% A" o2 l$ F7 Z% y0 [ for p in prob: 9 q* v& t q4 @! G3 j) w w8 |% E temp += p 1 B) d. M: v( v9 E7 F prob = [prob/temp for i in range(N)]8 ^1 o' ~2 M5 S4 a# c9 G# O
for i in range(1,len(prob)): $ n; i0 Y4 A6 O$ R3 R3 J& G prob += prob[i-1]6 {, v# S5 T" c" a2 S
prob[-1] = 1 # 精度有时候很出问题 " B4 q+ L6 T, A return prob . G. o$ g* U! w' ~- o U6 w& ]# H& H) |. t; o7 adef minT_calc(sample): 1 k6 h$ w/ A' X4 h5 d. Y7 Q( \ minT = time_calc(sample[0])0 a* D' U+ g# o9 u, ?
index = 0 0 }6 M) P+ u1 d( D; i for i in range(1,len(sample)): 5 l1 ?9 |( |6 e P L t = time_calc(sample)/ A9 v9 O2 _! U! i( F* v6 Z( K
if t < minT:1 p: z, |4 ^( L( R/ I8 \) R
index = i3 U: s/ {9 Q6 D+ O6 }0 Y( |
minT = t# \/ w8 M4 h+ g r5 ^& `
return minT,index$ n O8 ?! m( I* B% n
# d4 f7 L0 L8 g [8 L
def init(): + u- B) S1 }4 N& F# |+ E sample = [] 3 I$ v) J- y" @ for i in range(N): 3 J8 `" r" g' u+ N7 [4 I: s sample.append([])! u5 @0 N X; Y+ N: ~7 A" M! ~0 U
for j in range(L):' r8 ^* T1 ~) ^4 J5 y/ u4 o
sample[-1].append(random.randint(0,7)). x3 p8 { n" m1 v c8 J0 k/ a' ?
return sample( l, E5 t" d( L$ V
' p8 W& T# }/ P1 E- ldef select(sample,prob): # 选择 / h/ j! z$ @6 I sampleEX = []: K7 }- j$ `, g
for i in range(N): # 取出N个样本 + ^9 I' ?9 ^7 W rand = random.random() % Q$ A3 |0 L g5 E* C for j in range(len(prob)): ) A! k; n, M8 J8 Z R if rand<=prob[j]: ; ^! ?0 s+ J) `2 s8 V2 P! y5 @: E sampleEX.append(sample[j]) $ `- \6 Q7 X- ? break " `) S+ f4 w& g9 E. V& x9 [% U' S return sampleEX5 q1 U0 t) N6 ^
1 W! @; |; `: g
def cross(sample,i): # 交叉. G Y! c$ R% Q: F9 E9 s }
for i in range(len(sample)-1): 0 |8 [5 y8 i. q0 o" `1 S for j in range(i,len(sample)): % f, a4 ] C, `# n) x- b- } rand = random.random()! O$ C# Z% i4 f- A; h! u. z
if rand<=croP*(e**i): # 执行交叉$ w8 w; P/ a9 P7 _7 v- f
loc = random.randint(0,L-croL-1) 3 M+ H x, X, O% O2 N( A& m' `; S) ~ temp1 = sample[loc:loc+croL] 7 F& ~8 v. f" ^. W; C/ \" }9 [( A! | temp2 = sample[j][loc:loc+croL] ; e! L `/ j* |- F+ Q+ g for k in range(loc,loc+croL):9 \" [8 Z1 Z9 y, t: \% C/ [6 f, A
sample[k] = temp2[k-loc]5 }+ A* T! L4 H4 w5 b8 k
sample[j][k] = temp1[k-loc]4 c6 L5 ?3 O1 p* s
return sample 3 {- `, n( y+ f7 s& E- ? Z* d/ `7 l7 J1 b' I+ Idef variance(sample,i): # 变异算子 5 t* z$ m. W- v+ d2 u, o
for i in range(len(sample)):7 B! \4 n+ V- c1 Y" B2 A4 H' c7 W
rand = random.random()$ h: a: Q+ g+ i+ i; F
if rand<varP*(e**i):8 G" i2 i, V3 q& W% R1 l3 C
rand1 = random.randint(0,L-1) * z% p, q1 [5 T9 n4 C& @" U+ Q rand2 = random.randint(0,L-1)) S. |& K j% v( X! a3 f( r
temp = sample[rand1] % }1 @- d5 u; G/ A# v9 r sample[rand1] = sample[rand2], O1 h. i9 K) R
sample[rand2] = temp$ G8 ]6 c% N" X! {( J' v) f! W, d( u
return sample ; o% H9 A( t. W: b& ]4 ?' | + j1 j# R9 d6 r+ P* ^7 \
def main(): + c) h0 S+ t. x sample = init() ) U/ k* R9 J% B& a h mini,index = minT_calc(sample) " d9 X3 q, ^* J best = sample[index][:] 5 H6 }, t* w M5 y9 _ print(best) . T# t, a% l$ { for i in range(10000):7 l G- y; u" g. e, b' p
print(i,'\t',minT_calc(sample),end="\t") # D0 Z) x; B# ^& L prob = init_prob(sample)4 J9 _7 g0 p3 `1 W3 O
sample = select(sample,prob)" V( m# d3 i7 R$ f2 i
sample = cross(sample,i) 9 }1 J2 k; K" x+ l- D# B# P- ?0 P3 b sample = variance(sample,i) ! {! y7 F' a1 _" P# P mi,index = minT_calc(sample) F8 ? @' s, ]/ H: \- d* W
if mi>mini and random.random()<e**i: # 精英保留策略 0 B' K3 O, v0 `8 B+ @2 ^ rand = random.randint(0,N-1) ( Q/ M6 o" c7 Y. _ k- Z sample[rand] = best[:]- h% W6 _! U% O. @' ]) U/ E
mini,index = minT_calc(sample)! ^& c/ f* ^+ h- L/ b/ r7 O9 W+ j
best = sample[index][:], ~- g" P- W8 ^9 [' v) I/ Q" s
print(best) 7 E1 T8 c" n9 b: M- s$ ?% T3 C print(sample) 8 G1 D% x- b7 S# j- F) ^# n- I! e( ]' q4 I
if __name__ == "__main__":' O6 p0 I+ ~" F& {% S
main1()0 [; ?7 q9 z9 i
""" 穷举搜索验证 """ * s( C; R G( ?# C, ^ ?; R a = list(itertools.permutations([1,2,3,4,5,6,7],7))7 x, w0 Z7 `0 v# g
ts = [] 3 w! c& _4 b1 n6 ?! J first = [0,1,2,3,4,5,6,7,0]. V9 R2 k+ G) n7 W2 @
for i in a: / {: r! S& b1 v2 i% b5 U temp = first+list(i) 2 K3 p; f7 }6 D) E temp.append(0). Q' |; j7 K& C; a0 O1 ~
t = time_calc(temp) 3 G8 o0 s0 E3 i# z7 B) d' I ts.append(t)' i: V# q) [; s) |( |
print(min(ts)) ; v$ y; @* E6 }( E7 [' G; X
print(time_calc([0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0]))" ?8 L; l* D! ^. _* \, v
- ~/ V6 u- Y; o A9 r" E$ Y8 M7 S) C. X6 @
5 I1 Q0 l! Y# U7 }: l7 {
一道工序有故障 2 ]' F' M* l5 \" B 4 U4 A0 n$ y/ o这部分是学姐做的,学姐用了偏数学的思考方式,仍然从循环的角度去考虑,主要考虑故障发生是否会影响当前循环,是否需要建立新的循环。因此就没有写代码处理问题了。具体的思路我确实不是很能讲清楚。但是这里面有一个非常大的问题,就是如果出现多台CNC同时发生故障怎么办。关于多台机器同时发生故障的概率,我们通过估算认为以给定的三组数据8小时内会出现这种特殊情况的可能性大约为30%。这个问题是我无法很好严格处理的(当然如果用贪心算法也就没这么多事了)。 2 t3 B+ P& ^4 v7 [ + F; t9 L [# w }* \- K两道工序无故障 & 两道工序有故障 2 w3 x. p5 z2 a& J Q s% k( _8 X& L
这两个部分都是我来处理的,因为使用的方法大致相同,就并在一起说了。 {3 s2 y2 H' S/ x; v$ e; m. p7 }; W Y8 r
两道工序与一道工序最大的区别在于三点:- w, }; q- E7 h2 i) A; {, p
9 X: _1 T$ O$ J$ Q4 B f
1、开始要处理CNC任务分配:分配给第一道工序几台CNC,分配给第二道工序几台CNC?具体怎么布局? & c w7 i% T6 Z3 a' C3 `- O # p6 S. F# j% {# c @- x2、加工过程可能仍然是一个循环,但是这个循环将可能会非常的庞大以至于不可能直观的看出来。 8 |/ `! _7 R. ?( f; C1 s. L. J$ i- `2 [1 F4 a
3、两道工序的分配已经是一个严格的NP难问题了(即理论上无法在多项式时间内求得最优解)。& a3 w& a$ b6 V5 ^
, s! B3 f# _& J. M/ Z( q* J( G5 |
第一点我的想法很单纯——穷举,没错,就是穷举,除了显然不合适的分配方案外,其他方案都试一遍(虽然真的很蠢,但是我真的想不出到底能怎么办了)2 k% o1 Y5 P5 Y% m
0 O/ I4 Y+ k+ z( u9 k3 M, H( u第二点因为不存在循环则使用遗传算法需要设定一个相当长的染色体长度(我们设定的染色体是RGV为各台CNC上下料的次序,如果要考虑全过程的模拟退火遗传算法,则染色体长度大约在300~400左右)。事实上我也尝试了这个方法,结果从我写完这个算法我开始跑,一直跑到比赛结束算法依旧没有收敛[捂脸]。这里给出代码仅供参考(各位朋友要是有好意见也可以提出) ↓↓↓ 9 Q! T- `/ d9 c }2 l* p. C2 j" s. D+ R, n. F; Q. A1 f' s# }' \3 g# k
# -*- coding:UTF-8 -*- }, p9 [6 F) o6 s4 k2 Q( q
""" 7 J- K0 [% v! i' V1 T$ G: ` 作者:囚生CY - X0 { N# d6 ?2 ^+ w 平台:CSDN 5 v/ F( z$ `% g2 n* S9 n 时间:2018/10/09 0 [) H( {! X4 c& w A# q3 x# o& i, f' G 转载请注明原作者 7 @" c J) y. b8 R3 g 创作不易,仅供分享 ( D4 [. U, I5 u+ S"""! R) G) \* _3 x8 f
import random/ I+ \2 ]( `! g. A* s) `
$ k" H" V+ E, W b$ C% H! z R' m# 第1组 % a7 i0 i! }8 Y; x- \8 l"""8 E U) Y. H7 P- Z4 R/ z5 v5 L
d1 = 20 G/ J- J- o1 I \d2 = 335 l& u; H! \+ w! M/ b( O4 u
d3 = 46 7 _5 \( R+ X, k4 o1 T8 [8 ^T1 = 400& L# q5 @2 q* ?* R8 H+ |+ E
T2 = 378 / ^2 G2 n2 R2 g2 uTo = 28 - {7 Q* g4 {# d! V1 z8 dTe = 31. N3 F, O$ {0 T2 `" E6 ]( E
Tc = 250 f9 B/ L1 T( N
""" & M& I' V8 O" s1 P& i ' y7 h" n: z$ j% ^# 第2组9 _. s3 X3 b8 W. N; E) d3 ~/ {
"""6 t+ m: ~- d, } S& A N6 v
d1 = 232 [2 }, {! X( r) R
d2 = 41 + j0 }- p1 X% e# Fd3 = 59 ' x0 N' H9 w% }" Y0 GT1 = 2809 s) b6 k; P5 I1 o! N& j. ^% @
T2 = 500$ C; x& m5 g4 n$ p: ?8 k
To = 30* g; l% m, n& S* N4 h8 s
Te = 352 \( [. Q8 a1 g, y+ F4 w
Tc = 309 i V$ d/ C5 f2 \. M# j& Y0 j' `
""") q6 p1 q1 u- k+ L3 O% V' n3 L
# L% j3 A& Q7 g1 w6 U e# 第3组 & V- c% a% S* u8 ~/ t0 Bd1 = 18& r+ S- H# C! t9 ?: V R
d2 = 32 8 z5 C$ D# @- O+ v8 m% Pd3 = 46" A" `: |/ a6 t! H
T1 = 455. d2 [$ ?) @; I- s9 ~2 _8 S6 a! ^
T2 = 182% Q* W- E& L2 g* p
To = 27 3 s6 g4 T' i5 k9 E. a6 Y) DTe = 32) ?" ^ n5 r7 |3 w/ R
Tc = 258 P+ @3 |# T" j
$ k$ \3 p% J; h, S
cncT = [To,Te,To,Te,To,Te,To,Te]# p! f7 x7 p! o; r7 A+ z6 [
tm = [ 9 B: v- x7 B- U, C4 t9 [5 B [0,0,d1,d1,d2,d2,d3,d3], 1 ~2 A9 J4 O/ W+ H7 d2 [; s$ N [0,0,d1,d1,d2,d2,d3,d3],0 y' {, L0 Z2 j& d" P* u8 C* s
[d1,d1,0,0,d1,d1,d2,d2], : V/ @, W% V& R5 Q5 u3 g [d1,d1,0,0,d1,d1,d2,d2], 6 J. U4 ~/ ]) I# M [d2,d2,d1,d1,0,0,d1,d1],8 Y1 D2 P8 t5 s+ I1 N
[d2,d2,d1,d1,0,0,d1,d1],7 c" f( ?" \- ?: \% O
[d3,d3,d2,d2,d1,d1,0,0],4 G/ C( E2 e* c8 `% t
[d3,d3,d2,d2,d1,d1,0,0], 9 u: ?3 n7 @6 h' B0 r2 R2 ~] . C( a% ]5 j9 aType = [1,0,1,0,1,0,0,0] # CNC刀具分类& q; J, H, H1 Y4 y$ h9 w( Q _
+ f) o0 g/ Y% Y6 o" X. {
N = 64 : g, F* [8 o! n2 ]* h7 j: s- lL = 100 - z' i6 C+ e8 T3 _2 O. V! n7 @varP = 0.1) b$ y6 J7 m2 U$ @- t Z/ P" k$ o
croP = 0.6 ' E% I8 {' i3 u; o& JcroL = 2 2 W) ]9 j2 t& _1 s1 Z7 Ye = 0.99 4 U( {2 {7 b* k% ? % L4 U- H: i; `7 O! v! Ldef init_first_round(): # 第一圈初始化(默认把所有第一道CNC按顺序加满再回到当前位置全部加满) ; e% Z% ~4 D- Z1 g* B1 z) Z state = [0 for i in range(8)] # 记录CNC状态(还剩多少秒结束,0表示空闲)% U3 X% l" Q( T' K
isEmpty = [1 for i in range(8)] # CNC是否为空+ `/ A$ Z1 c: _+ ~& S; U
rgv = 0 # rgv状态(0表示空车,1表示载着半成品)6 h8 j4 L) \5 ?# N- ^3 q
currP = 06 r! u4 F' G7 z1 x4 l0 ^0 @
total = 0 j% k8 c# q8 ~4 l9 c% m. `3 | seq = []& V; p- ~& g! b( _; k, h+ V+ ?
flag = False 4 I( P8 a4 L! V6 P" f7 @) q for i in range(len(Type)):+ r) V/ J% Y S" D6 a# C8 Y
if Type==0: 8 N1 C% J7 C5 v! D" i, V( O seq.append(i) 5 ~" G9 F0 G8 ?6 J0 ] S flag = True 3 u. a* s: h, e. R, Y currP = seq[0] - Y3 t* A7 ?" ~ \7 T& ~& j9 I seq.append(currP); n% f' [ Y+ U3 v, x* h
rgv,currP,total = time_calc(seq,state,isEmpty,rgv,currP,total)0 z2 ?5 c2 O2 r, L- l
return state,isEmpty,rgv,currP,total,seq0 h0 f1 `; W- L
2 L, ]" V$ p, z! c" V$ @& ~def update(state,t): % Y4 G% v: a# |: J# f; R; X# I for i in range(len(state)):$ u% _$ p- F1 [
if state < t:# V) s1 \* D6 \7 o. u+ T2 A
state = 0% D0 U% U7 y5 l" d
else:) x# ?6 F0 r9 g; a# i. S
state -= t" w) ~6 ?% D: [% Q7 Z
! B. Y% x3 i! Q h% q0 J6 J' h+ Wdef time_calc(seq,state,isEmpty,rgv,currP,total): # 事实上sequence可能是无效的,所以可能需要9 P! I' A: J) P* L
index = 01 \' h6 R# R5 I* n) i" {
temp = 0 4 J, a" g6 }7 ]1 { while index<len(seq):* [: L, W* r0 s$ h+ g# q0 M, _
""" 先移动到下一个位置 """ 9 W7 U* k* l: h% p& V$ p! L4 C- q nextP = seq[index]. ^9 c: ^& ~4 v c
t = tm[currP][nextP] ' D+ }- n. k. w total += t 2 E: x0 h$ X$ n3 N update(state,t) / t! u3 I1 x( F8 a, Y, T: z8 g2 J if Type[nextP]==0: # 如果下一个位置是第一道工作点 9 Y+ t+ A. t6 U8 R) U% ?0 @0 d if rgv==1: # 然而载着半成品 3 F9 U( e( M$ ] seq.pop(index) # 去掉这个元素并中止当次循环进入下一个循环 ' ]( a4 x x! R* t- q continue 9 f5 j% b, |- G5 `" f% { if isEmpty[nextP]: # 如果下一个位置是空的& R, P0 W! h- M) S. `) t0 v
t = cncT[nextP] * p6 d7 ?) G4 R3 t total += t r- {0 v; C! e6 \& O update(state,t)7 z! i" |+ _# F
state[nextP] = T1 # 更新当前的CNC状态# y, |8 Q& q' t* n6 i2 |0 S/ ]9 r. U
isEmpty[nextP] = 0 # 就不空闲了2 {, d5 L) M( ?* M/ M% I. e
else: # 如果没有空闲 1 I$ e0 ]( }, B2 |1 _ if state[nextP] > 0: # 如果还在工作就等待结束0 }; a" S, }7 \" \3 X
t = state[nextP]5 E/ J1 i2 w9 H8 P) q4 S
total += t - E. N9 ?: \5 c9 X& ^6 x update(state,t) : P0 R5 i) N6 v- t t = cncT[nextP] # 完成一次上下料 9 [9 ~& M" ?" N1 d total += t + {# z+ }' \+ i$ n, H# n update(state,t) . x4 R1 |0 F! b2 U2 O* N state[nextP] = T19 x9 V; A) X# Q& Z
rgv = 1* n/ D) x. N% G5 O2 R' o
else: # 如果下一个位置是第二道工作点 ! i) [/ t9 q/ f+ J* m# I if rgv==0: # 如果是个空车 / D$ a8 ?- e4 }6 | seq.pop(index) # 删除当前节点# ?# E6 U% F5 N5 w; ~
continue4 _# k& R0 g6 U! w# _
if isEmpty[nextP]: # 如果下一个位置是空的 7 \( g9 o, i- Y- C7 m2 U t = cncT[nextP]* F% e# O4 `9 Q# H8 }1 l! ?% V
total += t 6 j3 X5 ~) B: W. A7 W update(state,t) : X4 Y$ G6 L! C* G+ V2 ^# R state[nextP] = T28 `! I/ L' K' k
isEmpty[nextP] = 0 : A3 x! ~4 g1 w! A) n+ T/ W
else: # 如果没有空闲 : i2 c4 K4 S: x' _6 L if state[nextP] > 0: # 如果还在工作就等待结束0 g* d2 B Y5 O6 I2 [( R1 Q
t = state[nextP]. T! O! X/ y4 \7 N8 H4 q
total += t2 I6 x: z+ `6 b# M+ \
update(state,t)2 r; V2 e7 f I5 Y. ^
t = cncT[nextP]+Tc 7 E: B3 U' l8 k1 R3 M0 @" u/ m total += t 9 E7 I& p1 v$ m. I update(state,t)0 m6 F3 Z# M7 H1 c* N5 N
state[nextP] = T2 # C; w$ D8 ~1 Q5 d4 B rgv = 0: C* o0 C2 [* s
currP = nextP2 V' `% e0 H6 m' G8 m- ?
temp = total $ \& r8 k3 P! ^$ k; n
index += 1 2 v7 q/ I. r! ?- v total += tm[currP][Type.index(0)] # 最后归零9 b; M( |' }& W3 g# G
return rgv,currP,total 9 P, @! ?! B {7 |; R, O % z, m5 }8 Q5 H: gdef init_prob(sample,state,isEmpty,rgv,currP,total): # 计算所有sample的 0 w7 N) @& ]( X+ _" x+ ~ prob = []5 D, _( E9 [" _: P1 l+ `
for seq in sample:5 o1 P, {+ x* ~% y, ^% Z" X& i" ]# X
t = time_calc(seq,state[:],isEmpty[:],rgv,currP,total)[-1] ; N, F; p$ s# H- P prob.append(t)8 h) H9 r9 d% M3 O* a8 P& O
maxi = max(prob)+ _) Z+ I+ @( [
prob = [maxi-prob+1 for i in range(N)] ; D9 ], r, k; |% G/ B temp = 0 * n1 J& y2 T4 U8 f for p in prob:$ U1 J! ^6 y0 A$ @- q s- ]' ?' w; e8 f
temp += p7 D) S. A" t; V. `9 k
prob = [prob/temp for i in range(N)] T2 O' B$ Y. |! J# [' H
for i in range(1,len(prob)): ; U. o i G4 w! E prob += prob[i-1] 5 A' C8 u) y P- A+ A6 N2 e! t prob[-1] = 1 # 精度有时候很出问题, m( h- Z7 T4 Q5 ]: R* |
return prob5 r. l) Z& I$ `
- l# {" h6 G2 u! g) x
def minT_calc(sample,state,isEmpty,rgv,currP,total): * l1 F% R& c. [/ z i minT = time_calc(sample[0],state[:],isEmpty[:],rgv,currP,total)[-1] 1 y* c; G, U# G/ M) m* T8 Z) f/ @! P index = 0/ T" O# E* \0 i5 w& [: D6 Q
for i in range(1,len(sample)):( i) k: ]- X& H! J- W6 L: |7 {# }
t = time_calc(sample,state[:],isEmpty[:],rgv,currP,total)[-1]6 z, G/ H2 Y7 V1 ~7 z
if t < minT:$ `2 ?. k) `, Y( n
index = i4 N, U- B/ W- }# a6 }5 ?
minT = t. j2 m3 l! ]6 G
return minT,index 1 J/ {* E; \1 P% F4 F0 l / F- b/ p* F' e) u& Ndef init(): # 初始化种群(按照第二道工序,第一道工序,第二道工序,第一道工序顺序排列即可) & x. D# K; I3 n$ C* q9 l3 Y sample = [] # J) s! R9 q8 P ~' O refer0 = [] 0 Z6 d u' j" z9 l H- N refer1 = [] * h: X; ]) t6 U& i; o; R- \- T for i in range(8):) X# a6 w ?8 s; M! H# B
if Type==0:$ O) ^0 J# c8 R8 u: W
refer0.append(i)% t1 G9 Q5 T- W2 }4 o) P* J/ Y
else: ' f3 a" v: A3 n1 w0 x/ X' [+ | refer1.append(i) 0 c O) r/ M, U1 e: l# B for i in range(N):+ z- B9 u1 E5 l" l6 h5 l3 T
sample.append([]): R8 ]3 V$ |2 C {2 z# R
for j in range(L): 3 n& f# \0 R. Z Y+ s) C/ O3 ` if j%2==0:, N7 A, R8 x+ n# G# l
sample[-1].append(refer1[random.randint(0,len(refer1)-1)]) 5 [9 x. V$ F# I; r; }) ~ else:% j& [* [0 g- s8 A+ T' L
sample[-1].append(refer0[random.randint(0,len(refer0)-1)])9 x6 e$ T) G( s I' r
return sample3 O( Q% j/ M A2 K3 @: |5 E7 _5 a
, X* z: O/ I: N% j$ udef select(sample,prob): # 选择算子# h6 ]7 Q U$ H! Z0 t
sampleEX = []: z+ N9 j+ { [7 ^; w5 @" g
for i in range(N): # 取出N个样本 ' J1 f: N. B7 F! G6 N; Z rand = random.random() & j h; s, j0 l) w: ~ for j in range(len(prob)):! a' j1 b/ g9 H
if rand<=prob[j]: ; q) Y, f! T, N( v sampleEX.append(sample[j]) 2 p% n$ f z' h( _0 a/ y break 8 X% _+ \5 P/ |) H6 ]. S x, D return sampleEX , B2 H2 |( Z- w! G- z7 @- n4 n% Z9 I1 Y7 k9 J$ k, h7 d' S
def cross(sample,i): # 交叉算子 # B" G$ m q1 q+ ^5 K6 T* } for i in range(len(sample)-1): " i5 x% s! u- X& s, ~6 k2 k for j in range(i,len(sample)): , T% X8 b1 e, L8 H rand = random.random()4 T5 o; l- _8 j, k1 Y& u
if rand<=croP*(e**i): # 执行交叉% A7 s* a- X1 c+ L
loc = random.randint(0,L-croL-1)$ k i) H" Y" k
temp1 = sample[loc:loc+croL]; T% c" c6 k/ O+ b# E
temp2 = sample[j][loc:loc+croL]9 i; z1 U2 k# v( c+ w
for k in range(loc,loc+croL):5 `6 J9 V: d3 A6 C
sample[k] = temp2[k-loc]0 G3 T/ A; w' D1 s
sample[j][k] = temp1[k-loc] u3 t, n$ O% _; [, Z
return sample! X6 c2 v% P% r$ k- `
) f3 g1 o; g0 [def variance(sample,i): # 变异算子 2 U: o: a/ }/ M+ F+ v/ P+ P
for i in range(len(sample)):4 I: ^; I$ ^: p+ ]
rand = random.random()8 f5 ]! Q, C; w0 E+ u
if rand<varP*(e**i): ( |. Y0 q, w. a K rand1 = random.randint(0,L-1) " P3 |2 T6 h p, b randTemp = random.randint(0,int(L/2)-1)9 a e/ n7 t+ X9 t- ~2 {2 i
rand2 = 2*randTemp if rand1%2==0 else 2*randTemp+1 ; d/ C/ K, x$ e5 j. ` temp = sample[rand1] / Y, c6 k3 v4 J. H: r0 } sample[rand1] = sample[rand2] * ] A+ F5 a7 |, A/ q, A% B sample[rand2] = temp8 I1 j# v% t* b& w6 N! D
return sample0 g* `+ N: e$ _8 }' y4 Q( u
6 X. ^) T/ ^. R
if __name__ == "__main__": + B+ m0 {( T$ r) | state,isEmpty,rgv,currP,total,seq = init_first_round()$ D8 e( a8 a, Z2 d/ w
print(state,isEmpty,rgv,currP,total) 0 R; m, [5 U+ `$ J sample = init()9 h ?$ P& v' Y% j
mini,index = minT_calc(sample,state[:],isEmpty[:],rgv,currP,total) ) A7 o5 G; U) H4 A
best = sample[index][:] : D7 ~$ i$ Q8 i6 C" f2 f$ C5 K+ M for i in range(100000):- u* W3 i& g" y/ ^3 q
f = open("GA.txt","a") : q8 Z3 D/ E J tmin = minT_calc(sample,state[:],isEmpty[:],rgv,currP,total)[0]7 i2 X6 s7 S7 T9 [7 J
f.write("{}\t{}\n".format(i,tmin))+ F/ B! S5 L. X/ E/ P2 Y- ^3 `) [
print(i,"\t",tmin,end="\t")5 x8 k$ ~+ X' x6 _3 O
prob = init_prob(sample,state[:],isEmpty[:],rgv,currP,total)) r1 V5 t+ D: a- V
sample = select(sample,prob) ' s$ F$ b! u) \9 s6 y$ F i8 A+ I sample = cross(sample,i)* r# k1 a6 z) |& q- E+ `8 S# J
sample = variance(sample,i) & ~% |6 c: i/ S, @ mi,index = minT_calc(sample,state[:],isEmpty[:],rgv,currP,total) ! W+ ^8 \" N7 `) {# y if mi>mini and random.random()<e**i: # 精英保留策略! Z" B$ q# N' W
rand = random.randint(0,N-1), G: O2 @: S& Q0 K7 M. {, ]2 R
sample[rand] = best[:]8 L% p% t* y- A( M
mini,index = minT_calc(sample,state[:],isEmpty[:],rgv,currP,total)" U: `# y/ C1 D( `) X
best = sample[index][:] ' i, }2 [$ y1 f! v, o4 M1 A. t# T @+ w print(best)$ K' b* U ?, Y& B0 C- Y" f
f.close()8 y9 J7 p4 P; m# S. O: k* _$ g, r+ }
print(sample) ; a: s3 H, ]3 c遗传算法这条路被堵死后我一度陷入俗套,用最直接的贪心搞了一阵子,觉得用贪心算法(即考虑下一步的最优策略)实在是对不起这种比赛。然后我就变得——更贪心一点了。/ S% o% |2 {4 x$ i
* F6 v& R! c# K* y我试图去寻找接下来K步最优的策略,然后走一步。K=1时算法退化为贪心算法,最终我们设置为K=4(当K>=8时算法速度已经相当缓慢,而4~7的结果大致相同,且K=4的速度基本可以做到2秒内得到结果)。* _2 z% Z ?3 u6 u+ b7 X8 x