├── ACO.py ├── README.md └── data.txt /ACO.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import copy 3 | """ 4 | datasets=[] #datasets[i,j,k],i取活动编号,j代表:0——通常模式,k代表:1——R1 5 | # 1——压缩模式 2——R2 6 | # 3——R3 7 | # 4——工期 8 | 9 | eventlist=[] #event[i]代表为第i个事件节点,EventNode对象数组 10 | 11 | activitylist #activitylist[i,j],i取活动编号,j代表:0——该活动的前置事件 12 | 1——该活动的后置事件 13 | 14 | event2activity #event2activity[i,j] i取前置事件id号,j取后置事件id号,其值为对应的活动号 15 | """ 16 | 17 | """ 18 | 事件类:属性 —— its_id :事件的id号 19 | tc:时间约束,说明完成该事件开始时间必须大于该值 20 | predev[i]:紧前事件集合 21 | nextdev[i][j]:j=0第i个紧后事件对应的id号,j=1是执行该事件所对应的活动 22 | """ 23 | class EventNode: 24 | def __init__(self,its_id=0): 25 | self.its_id =its_id 26 | self.tc=0 #时间约束 27 | self.predev =[] 28 | self.nextdev=[] 29 | 30 | """ 31 | 函数名:getData() 32 | 函数功能: 从外界读取项目数据并处理 33 | 输入 无 34 | 输出 1 datasets:datasets[i,j,k],不同模式下各活动的具体参数矩阵,具体说明见数据说明部分 35 | 2 eventlist:事件对象列表,eventlist[event_id]代表event_id事件的对象 36 | 3 activitylist:activitylist[i,j]记录i活动的前后置事件的事件号,具体说明见数据说明部分 37 | 4 activity_num:总活动数 38 | 5 event_num:总事件数 39 | 6 event2activity:event2activity[i,j],通过前(i)后(j)置事件id号索取活动编号矩阵 40 | 7 R:R[i],i资源的总量 41 | 其他说明:无 42 | """ 43 | def getData(): 44 | datafile=open("data.txt") 45 | dataflame=datafile.read() 46 | datafile.close() 47 | 48 | data_temp=dataflame.split("\n") 49 | event_num=int(data_temp[0].split("%*%")[0])+2 50 | activity_num=int(data_temp[0].split("%*%")[1])+2 51 | R=[] 52 | R.append(int(data_temp[0].split("%*%")[2])+1) 53 | R.append(int(data_temp[0].split("%*%")[3])+1) 54 | R.append(int(data_temp[0].split("%*%")[4])+1) 55 | eventlist=construct_graph(event_num) 56 | 57 | datasets=np.zeros((activity_num,2,4),dtype=np.int) 58 | activitylist=np.zeros((activity_num,2),dtype=np.int) 59 | event2activity=np.zeros((activity_num,activity_num),dtype=np.int) 60 | 61 | for temp in data_temp[2:]: 62 | data=temp.split("/") 63 | data1=data[0].split(" ") 64 | 65 | i=int(data1[0]) 66 | datasets[i,0,0]=int(data1[1]) # 工期 67 | datasets[i,0,1]=int(data1[2]) # R1资源 68 | datasets[i,0,2]=int(data1[3]) # R2资源 69 | datasets[i,0,3]=int(data1[4]) # R3资源 70 | datasets[i,1,0]=int(data1[5]) # 工期 71 | datasets[i,1,1]=int(data1[6]) # R1资源 72 | datasets[i,1,2]=int(data1[7]) # R2资源 73 | datasets[i,1,3]=int(data1[8]) # R3资源 74 | 75 | data2=data[1].split("-") 76 | cur_node=int(data2[0]) 77 | next_node=int(data2[1]) 78 | eventlist[cur_node].nextdev.append([next_node,i]) 79 | eventlist[next_node].predev.append(cur_node) 80 | event2activity[cur_node,next_node]=i 81 | activitylist[i,0]=cur_node 82 | activitylist[i,1]=next_node 83 | return datasets,eventlist,activitylist,activity_num,event_num,event2activity,R 84 | 85 | """ 86 | 函数名:get_etatable(activity_num) 87 | 函数功能: 根据总活动数初始化启发项矩阵 88 | 输入 1 activity_num:总活动数 89 | 输出 1 etatable:启发项矩阵,etatable[i,j]代表i活动j模式(j=0-正常模式,j=1-压缩模式)的启发值 90 | 其他说明:无 91 | """ 92 | def get_etatable(activity_num): 93 | etatable=np.zeros([activity_num,2]) 94 | for i in range(activity_num): 95 | etatable[i,0]=(datasets[i,0,1]+datasets[i,0,2]+datasets[i,0,3])/(R[0]+R[1]+R[2]) 96 | etatable[i,1]=(datasets[i,1,1]+datasets[i,1,2]+datasets[i,1,3])/(R[0]+R[1]+R[2]) 97 | return etatable 98 | 99 | 100 | """ 101 | 函数名:init_pheromonetable(activity_num) 102 | 函数功能: 根据总活动数初始化信息素矩阵 103 | 输入 1 activity_num:总活动数 104 | 输出 1 pheromonetable:信息素矩阵,pheromonetable[i,j]代表 105 | i活动j模式(j=0-正常模式,j=1-压缩模式)的信息素含量 106 | 其他说明:无 107 | """ 108 | def init_pheromonetable(activity_num): 109 | pheromonetable=np.ones((activity_num,2))*Q/activity_num 110 | return pheromonetable 111 | 112 | """ 113 | 函数名:construct_graph(event_num) 114 | 函数功能: 根据事件总数生成对象列表 115 | 输入 1 event_num:总事件数 116 | 输出 1 eventlist:事件对象列表,eventlist[event_id]代表event_id事件的对象 117 | 118 | 其他说明:无 119 | """ 120 | def construct_graph(event_num): 121 | eventlist=[] 122 | for i in range(event_num): 123 | eventnode=EventNode(i) 124 | eventlist.append(eventnode) 125 | return eventlist 126 | 127 | 128 | """ 129 | 函数名:check_predev(eventnode) 130 | 函数功能: 检测是否有紧前事件 131 | 输入 1 eventnode:事件对象 132 | 输出 1 True:有紧前事件 133 | False:无紧前事件 134 | 其他说明:无 135 | """ 136 | def check_predev(eventnode): 137 | return True if eventnode.predev else False 138 | 139 | """ 140 | 函数名:check_nextdev(eventnode) 141 | 函数功能: 检测是否有紧后事件 142 | 输入 1 eventnode:事件对象 143 | 输出 1 True:有紧后事件 144 | False:无紧后事件 145 | 其他说明:无 146 | """ 147 | def check_nextdev(eventnode): 148 | return True if eventnode.nextdev else False 149 | 150 | """ 151 | 函数名:check_finish(eventlist_finish) 152 | 函数功能: 检查是不是所有事件(不包括结束虚事件)都完成 153 | 输入 1 eventlist_finish:当前完成的活动列表 154 | 输出 1 True:是 155 | False:否 156 | 其他说明:无 157 | """ 158 | def check_finish(eventlist_finish): 159 | for i in range(event_num-2): 160 | if i not in eventlist_finish: 161 | return False 162 | if event_num-2 in eventlist_finish: 163 | return False 164 | return True 165 | 166 | """ 167 | 函数名:parameters_init() 168 | 函数功能: 蚂蚁开始走的过程中的参数初始化函数 169 | 输入 1 无 170 | 输出 1 无 171 | 其他说明: 对当前事件cur_time=0, 172 | R1当前资源量=R1资源总量 173 | R2当前资源量=R2资源总量 174 | R3当前资源量=R3资源总量 175 | """ 176 | def parameters_init(): 177 | cur_time=0 178 | res_time=[] 179 | 180 | eventlist_finish=[] 181 | eventlist_finish.append(0) 182 | 183 | activity_waited=[] 184 | activity_waited.append(0) 185 | 186 | activitylist_finish=[] 187 | 188 | R1=R[0] 189 | R2=R[1] 190 | R3=R[2] 191 | 192 | return cur_time,res_time,eventlist_finish,\ 193 | activity_waited,activitylist_finish,R1,R2,R3 194 | 195 | """ 196 | 函数名: timeseries_constraint(next_eventnode) 197 | 函数功能: 时序约束,当前活动的开始时间应该要满足后续事件能够执行的最早时间 198 | 函数执行完后当前时间满足了资源约束 199 | 输入 1 activity:活动id号 200 | 其他说明:无 201 | """ 202 | def timeseries_constraint(activity): 203 | next_eventnode=activitylist[activity,1] 204 | global cur_time 205 | if eventlist[next_eventnode].tc>cur_time: 206 | cur_time=eventlist[next_eventnode].tc 207 | time_ahead(cur_time) 208 | 209 | """ 210 | 函数名:resource_constraint(activity,mode) 211 | 函数功能: 当前活动的开始时间应该要满足当前资源能够支持其执行, 212 | 函数执行完后当前时间满足了资源约束 213 | 输入 1 activity:活动id号 214 | 输入 2 mode:模式类型,mode=0代表通常模式,mode=1代表压缩模式 215 | 其他说明: 对当前事件cur_time=0, 216 | R1当前资源量=R1资源总量 217 | R2当前资源量=R2资源总量 218 | R3当前资源量=R3资源总量 219 | """ 220 | def resource_constraint(activity,mode): 221 | global R1,R2,R3,cur_time 222 | while True: 223 | if(R1>=datasets[activity,mode,1] and R2>=datasets[activity,mode,2] and R3>=datasets[activity,mode,3]): 224 | R1-=datasets[activity,mode,1] 225 | R2-=datasets[activity,mode,2] 226 | R3-=datasets[activity,mode,3] 227 | break 228 | elif res_time: 229 | cur_time=min([i[0] for i in res_time]) 230 | time_ahead(cur_time) 231 | else: 232 | print([R1,R2,R3,activity]) 233 | exit() 234 | 235 | """ 236 | 函数名:update_list(activity,mode) 237 | 函数功能: 更新活动列表和已完成时间列表信息, 238 | 输入 1 activity:活动id号 239 | 输入 2 mode:模式类型,mode=0代表通常模式,mode=1代表压缩模式 240 | 其他说明: 无 241 | """ 242 | def update_list(activity,mode): 243 | next_eventnode=activitylist[activity,1] 244 | 245 | activity_waited.remove(activity) 246 | res_time.append([cur_time+datasets[activity,mode,0],activity,mode]) 247 | if len(activity_waited)==0 and check_finish(eventlist_finish): 248 | eventlist_finish.append(event_num-2) 249 | activity_waited.append(activity_num-1) 250 | elif next_eventnode!=event_num-2 and next_eventnode not in eventlist_finish: 251 | eventlist_finish.append(next_eventnode) 252 | for event in eventlist[next_eventnode].nextdev: 253 | eventnode=eventlist[event[0]] 254 | flag=True 255 | for i in eventnode.predev: 256 | if i not in eventlist_finish: 257 | flag=False 258 | if flag: 259 | for i in eventnode.predev: 260 | activity_waited.append(event2activity[i,eventnode.its_id]) 261 | 262 | activitylist_finish.append([activity,mode,cur_time]) 263 | 264 | """ 265 | 函数名:go_a_step(activity,mode) 266 | 函数功能: 蚂蚁按活动activity的模式mode行进一步 267 | 输入 1 activity:活动id号 268 | 输入 2 mode:模式类型,mode=0代表通常模式,mode=1代表压缩模式 269 | 输出 1 无 270 | 其他说明: 时序约束部分:当前活动的开始时间应该要满足后续事件能够执行的最早时间 271 | 资源约束部分:当前活动的开始时间应该要满足当前资源能够支持其执行 272 | 更新事件过程:当前活动执行完后,在当前活动等待列列表中删去该活动, 273 | 并把符合约束的后续活动加入到当前活动等待列表中 274 | 275 | """ 276 | def go_a_step(activity,mode): 277 | global R1,R2,R3,cur_time 278 | 279 | if activity==activity_num-1: 280 | cur_time=min([i[0] for i in res_time]) 281 | eventlist_finish.append(event_num-1) 282 | activitylist_finish.append([activity,mode,cur_time]) 283 | else: 284 | #时序约束,执行后当前时间满足时序约束 285 | timeseries_constraint(activity) 286 | 287 | #资源约束,执行后当前时间满足资源约束 288 | resource_constraint(activity,mode) 289 | 290 | #更新活动列表和已完成时间列表信息 291 | update_list(activity,mode) 292 | 293 | """ 294 | 函数名:time_ahead(time) 295 | 函数功能: 删去当前资源时间结束列表中时间小于time的项,并更新当前资源量 296 | 输入 1 time:当前时间 297 | 输出 1 无 298 | 其他说明:无 299 | """ 300 | def time_ahead(time): 301 | global R1,R2,R3 302 | for res in res_time: 303 | if res[0]<=time: 304 | R1+=datasets[res[1],res[2],1] 305 | R2+=datasets[res[1],res[2],2] 306 | R3+=datasets[res[1],res[2],3] 307 | res_time.remove(res) 308 | 309 | ##############################程序入口######################################### 310 | if __name__=="__main__": 311 | datasets,eventlist,activitylist,activity_num,event_num,event2activity,R=getData() 312 | 313 | ant_num = 10 # 蚂蚁个数 314 | alpha = 0.1 # 信息素重要程度因子 315 | beta = 1 # 启发函数重要程度因子 316 | rho = 0.2 # 信息素的挥发速度 317 | Q = 1 # 品质因子 318 | itermax = 1000 # 最大迭代次数 319 | 320 | etatable = get_etatable(activity_num) # 初始化启发函数矩阵 321 | pheromonetable=init_pheromonetable(activity_num) # 初始化信息素矩阵 322 | 323 | result_record=[] # 各代蚂蚁行走结果记录矩阵 324 | aver_time = np.zeros(itermax) # 各代路径的平均时间 325 | time_best = np.zeros(itermax) # 各代及其之前遇到的最佳时间 326 | activitylist_best = np.zeros((itermax,activity_num,3),dtype="int") # 各代及其之前遇到的最佳路径时间 327 | 328 | iter = 0 329 | # 从0代开始进行让蚂蚁在AOA图上进行搜索 330 | while iter < itermax: 331 | activitylist_result=[] # 记录当前代各蚂蚁所探求的解 332 | 333 | # 依次让各只蚂蚁寻求一组解 334 | for i in range(0,ant_num): 335 | # 初始化蚂蚁探求解的各个参数 336 | cur_time,res_time,\ 337 | eventlist_finish,activity_waited,activitylist_finish,\ 338 | R1,R2,R3=parameters_init() 339 | 340 | # 每次用轮盘法选择下一个要执行的活动,直到所有活动都被执行完毕 341 | for j in range(0,activity_num): 342 | if len(activity_waited)==1: 343 | go_a_step(activity_waited[0],0) 344 | else: 345 | probtrans = np.zeros(2*len(activity_waited)) 346 | for k in range(len(activity_waited)): 347 | probtrans[2*k] = np.power(pheromonetable[k][0],alpha)\ 348 | *np.power(etatable[k][0],alpha) 349 | probtrans[2*k+1] = np.power(pheromonetable[k][1],alpha)\ 350 | *np.power(etatable[k][1],alpha) 351 | cumsumprobtrans = (probtrans/sum(probtrans)).cumsum() 352 | cumsumprobtrans -= np.random.rand() 353 | k = np.where(cumsumprobtrans>0)[0][0] # 下一个活动 354 | pheromonetable[activity_waited[int(k/2)],k%2]=\ 355 | (1-rho)*pheromonetable[activity_waited[int(k/2)],k%2]+rho*Q/activity_num 356 | go_a_step(activity_waited[int(k/2)],k%2) 357 | 358 | # 将该只蚂蚁探求的结果保存 359 | activitylist_result.append(activitylist_finish) 360 | 361 | # 对该代蚂蚁的最佳值与当前最佳值进行比较并更新当前最佳值及路径 362 | activitylist_result=np.array(activitylist_result) 363 | aver_time[iter] = activitylist_result[:,-1,2].mean() 364 | if iter == 0: 365 | time_best[iter] = activitylist_result[:,-1,2].min() 366 | activitylist_best[iter] = activitylist_result[activitylist_result[:,-1,2].argmin()].copy() 367 | else: 368 | if activitylist_result[:,-1,2].min() > time_best[iter-1]: 369 | time_best[iter] = time_best[iter-1] 370 | activitylist_best[iter] = activitylist_best[iter-1].copy() 371 | else: 372 | time_best[iter] = activitylist_result[:,-1,2].min() 373 | activitylist_best[iter] = activitylist_result[activitylist_result[:,-1,2].argmin()].copy() 374 | 375 | # 将该代蚂蚁所探求的所有解保存下来 376 | result_record.append(activitylist_result) 377 | 378 | # 更新信息素 379 | changepheromonetable = np.zeros((activity_num,2)) 380 | for j in range(activity_num-1): 381 | activity=activitylist_best[iter][j,0] 382 | mode=activitylist_best[iter][j,1] 383 | time=activitylist_best[iter][j,2] 384 | changepheromonetable[activity,mode]+=Q/activitylist_best[iter][-1,2] 385 | pheromonetable = pheromonetable + changepheromonetable 386 | 387 | # 将蚂蚁增加一代 388 | iter+=1 389 | 390 | # 结果输出 391 | print(time_best[itermax-1]) 392 | print(activitylist_best[itermax-1]) 393 | 394 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scheduling-problems 2 | 解决调度问题的元启发式算法应用,蚁群算法(ACO)、遗传算法(GA)、粒子群算法(PSO)、模拟退火算法(SA) 3 | -------------------------------------------------------------------------------- /data.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Greatpanc/Scheduling-problems/f0eeaafb0241064dc9229a3b00fbd8e2075365a9/data.txt --------------------------------------------------------------------------------