车间调度问题
问题描述 六工四机问题
一个车间有四台机器,要求加工6个作业
分工序,每个机器加工不同的工序
M1 | M2 | M3 | M4 | |
---|---|---|---|---|
J1 | 8 | 4 | 5 | 3 |
J2 | 2 | 6 | 5 | 6 |
J3 | 12 | 5 | 4 | 2 |
J4 | 4 | 1 | 4 | 9 |
J5 | 6 | 2 | 7 | 3 |
J6 | 5 | 6 | 9 | 3 |
遗传算法基本步骤
步骤1:选择编码策略,将实际问题的参数集合X表示位串。
步骤2:根据实际问题的优化目标,定义适应度值函数。
步骤3:确定遗传策略,包括群体大小n、选择、交叉、变异操作的方法,确定交叉概率Pc,变异概率Pm等参数。
步骤4:产生初始群体P。
步骤5:计算群体中所有个体的适应度值。
步骤6:按照遗传策略,对群体中的每一个个体选择、复制、交叉、变异操作,形成下一代群体。
步骤6:判断群体性能是否满足终止条件,不满足则返回第6步,进行下一次迭代;满足条件则完成计算,输出结果。
主要内容:编码→确定规则→跑算法→输出
import random # 产生随机数
# pop是种群,二元组(可行解的总完成时间,编码列表)
pop = [(ComputeStartTimes(g, I)[-1], g) for g in InitPopulation(ps, I)]
mit = input("迭代次数")
for it in range(1, mit+1):
random.shuffle(pop) # 随机打乱pop中的个体
hpop = len(pop)/2 # hpop是种群的一半
for i in range(hpop):
if random() < 0.3: # 交叉概率
# 通过交叉生成两个新元素
ch1 = Crossover(pop[i][1], pop[hpop+i][1], I)
ch2 = Crossover(pop[hpop+i][1], pop[i][1], I)
if random() < 0.1: # 如果变异概率小于0.1
ch1 = Mutation(ch1)
if random() < 0.1:
ch2 = Mutation(ch2)
pop.append((ComputeStartTimes(ch1, I)[-1], ch1)) # 将两个新解放回种群
pop.append((ComputeStartTimes(ch2, I)[-1], ch2))
# 按时间跨度顺序进行排序并选择出优胜者
pop.sort()
pop = pop[:ps]
return pop[0] # 返回完成时间最小的染色体
主要函数
自文件中读入instance对象中
def LoadInstance(fname):
f = open(fname, 'r')"""打开配置文件 Init.txt"""
head = f.readline().split()"""读入第一行"""
n, m = int(head[0]), int(head[1])"""将工件数和机器数存在n和m两个变量中"""
I = []""" 构造上文中的“jobs列表”"""
for l in f:"""从文件中依次读入每行数据,一行对应一个工件"""
l = l.split()
if len(l) < 3: continue """如果少于3个数则跳过该行"""
ntasks = int(l[0])"""ntasks存放该工件包含的工序道数,Init每行对应一个工件""""
I.append([])"""每行一个子list代表一个工件的信息"""
for j in range(ntasks):
mid = int(l[j*2+1])
dur = int(l[j*2+2])
I[-1].append((mid, dur))"""每个工件有多道工序,每个工序对应一个有序偶(机器数,加工时间)"""
return Instance(I, m)"""返回Instance类的一个实例"""
class Instance:
"""Class representing an instance of the JSP."""
def __init__(self, jobs, m):
self.jobs = jobs
self.n = len(jobs) # number of jobs
self.m = m # number of machines
def __getitem__(self, i):
return self.jobs[i]
def __len__(self):
return len(self.jobs)
交叉
def Crossover(p1, p2, I)://p1,p2是两条待交叉的染色体,I存放工序和工件的信息(见编码部分)
"""Crossover operation for the GA. Generalized Order Crossover (GOX)."""
def Index(p1, I)://这个函数接收两个参数p1和I,返回p1对应的有序偶list,n存储的是工件总数
ct = [0 for j in range(n)]
s = []
for i in p1:
s.append((i, ct[i]))
ct[i] = ct[i] + 1
return s
idx_p1 = Index(p1, I) //p1的有序偶list,p1即上文的Parent2
idx_p2 = Index(p2, I) //p2的有序偶list,p2即上文的Parent1
nt = len(idx_p1) //p1的有序偶list的长度
i = randint(1, nt-1)//1到nt-1间的随机数
j = randint(0, nt-1)//0 到nt-1间的随机数
k = randint(0, nt-1)//k为Parent1插入Parent2时的插入点位置
//implant 相当于上面的“(A,2)(C,1)(A,3)(B,3)”即从Parent1抽取的片段
implant = idx_p1[j:min(j+i,nt)] + idx_p1[:i - min(j+i,nt) + j]
lft_child = idx_p2[:k]
rgt_child = idx_p2[k:]
for jt in implant://从Parent1删除与插入片段重复的有序偶
if jt in lft_child: lft_child.remove(jt)
if jt in rgt_child: rgt_child.remove(jt)
//Child:即相当于BABACABCCB
child = [ job for (job, task) in lft_child + implant + rgt_child ]
return child
变异
def Mutation(p)://p是解的编码
nt = len(p)//nt存放染色体的长度
i = randint(0, nt - 1)//i是0到nt-1之间的一个随机数
j = randint(0, nt - 1)//j是0到nt-1之间的一个随机数
m = [job for job in p]//将解p复制到临时变量m中
m[i], m[j] = m[j], m[i]//交换m中i和j位置的值
return m//返回m
输出结果
def FormatSolution(s, C, I):
T = [0 for j in range(n)]
S = [[0 for t in I[j]] for j in range(n)]//n存储的是工件总数
for i in range(len(s)):"""遍历染色体"""
j = s[i]"""获得i的工件号j """
t = T[j]"""获得i是j的第几道工序t"""
S[j][t] = C[i]"""将i的加工时间存到S的相应位置中"""
T[j] = T[j] + 1"""工件j的工序累加器+1 """
return S
C语言的深度搜索方法
#include <stdio.h>
#include <string.h>
#include <algorithm> #调用memset函数
using namespace std;
int totalStep, minTime;
int n, m;
int Step[105];
struct Job #工作
{
int machine;
#在哪一台机器 int len;
#工作时长
}
job[105][105];
int jobEnd[105][105];
int jobStep[105]; //步骤
int machineWorkTime[105];
struct Recording
{
int start;
int ed;
int job;
int machine;
} best[105], now[105];
void init() #初始化
{
totalStep = 0;
#总计步骤 minTime = 999999999;
memset(jobStep, 0, sizeof(jobStep));
#将数字拷贝到内存中 memset(machineWorkTime, 0, sizeof(machineWorkTime));
memset(best, 0, sizeof(best));
memset(jobEnd, -1, sizeof(jobEnd));
}
void dfs(int step, int time) #深度搜索主函数
{
if (time >= minTime) //越界
return;
if (step == totalStep) //终止条件
{
minTime = time;
for (int i = 0; i < totalStep; i++)
best[i] = now[i];
return;
}
for (int i = 0; i < n; i++)
{
int j = jobStep[i];
if (j >= Step[i]) //没有访问过
continue;
int thisMachine = job[i][j].machine;
now[step].machine = thisMachine;
now[step].job = i + 1;
int temp = machineWorkTime[thisMachine];
int beginTime = max(jobEnd[i][j - 1], machineWorkTime[thisMachine]);
now[step].start = beginTime;
machineWorkTime[thisMachine] = beginTime + job[i][j].len;
jobEnd[i][j] = now[step].ed = machineWorkTime[thisMachine];
jobStep[i]++; //标记已经访问
dfs(step + 1, max(time, machineWorkTime[thisMachine]));
jobStep[i]--;
machineWorkTime[thisMachine] = temp;
}
}
int main()
{
printf("输入工作数量:");
scanf("%d", &n);
printf("输入机器数量:");
scanf("%d", &m);
init();
for (int i = 1; i <= n; i++)
{
printf("输入工作%d的步骤数量:", i);
scanf("%d", &Step[i]);
totalStep += Step[i];
for (int j = 0; j < Step[i]; j++)
{
printf(" 步骤%d运行于第%d台机器所消耗时间:", j + 1, i);
scanf("%d", &job[i][j].len);
}
}
dfs(0, 0);
printf("最短时间为:%d\n", minTime);
int gantt[105][105];
for (int i = 0; i < totalStep; i++)
{
for (int j = best[i].start; j < best[i].ed; j++)
{
gantt[best[i].machine][j] = best[i].job;
}
}
for (int i = 0; i < m; i++)
for (int j = 0; j < minTime; j++)
printf("%d%c", gantt[i][j], j == minTime - 1 ? '\n' : ' ');
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】