我在java中编写一个Time表生成器,使用AI方法来满足硬约束并帮助找到最佳解决方案。到目前为止,我已经实现了迭代构造(最受约束的第一个启发式)和模拟退火,我正在实现遗传算法。
有关问题的一些信息,以及我如何表示它: 我有一系列活动,房间,功能(活动需要和房间满足),学生和老虎机 问题在于为每个活动分配一个插槽和一个房间,这样就不需要学生在一个插槽中参加两个活动,所有分配的房间都满足了必要的要求。
我有一个评分函数,如果作业对软约束违规进行评分,则每个集合都有一个评分函数,因此要点是最小化。
我实现GA的方式是从迭代构造生成的填充开始(可以保留事件未分配)然后执行正常步骤:评估,选择,交叉,变异并保持最佳状态。冲洗并重复。
我的问题是我的解决方案似乎改善得太少了。无论我做什么,人群往往随机健身,并被困在那里。请注意,此适合度始终不同,但仍会出现下限。
我怀疑问题出在我的交叉功能中,这就是它背后的逻辑:
随机选择两个作业进行划分。让我们称他们为A和B分配。对于B的所有事件,请执行以下步骤(选择B的事件是随机的):
在A中获取相应的事件并比较分配。可能会发生3种不同的情况。
在任何其他情况下,事件都是未分配的。
这会创建一个孩子的父母的一些任务,一些是母亲的,所以在我看来它是一个有效的功能。而且,它并没有打破任何严格的限制。
至于变异,我使用我的SA的邻近功能给我另一个基于孩子的作业,然后替换那个孩子。
再一次。通过这种设置,初始种群为100,GA运行并且总是倾向于稳定在一些随机(高)适应值。有人可以给我一个关于我可能做错什么的指针吗?
由于
编辑:格式化并清除一些内容
答案 0 :(得分:0)
我认为GA只有在解决方案的一部分(向量的一部分)作为解决方案的独立部分具有重要性时才有意义,因此交叉功能在两个解向量之间集成了解的有效部分。就像DNA序列的某一部分控制或影响个体的特定方面一样 - 例如,眼睛颜色就是一个基因。然而,在这个问题中,解决方案向量的不同部分相互影响,使得交叉几乎毫无意义。这导致(我的猜测)算法在单个解决方案中相当快速地收敛,不同的交叉和突变只会对适应性产生负面影响。
我不相信GA是解决这个问题的正确工具。
答案 1 :(得分:0)
如果您能提供原始问题陈述,我将能够为您提供更好的解决方案。以下是我目前的答案。
遗传算法不是满足硬约束的最佳工具。这是一个可以使用整数程序解决的分配问题,整数程序是线性程序的特例。
线性程序允许用户最小化或最大化由目标函数(分级函数)建模的某个目标。目标函数由各个决策(或决策变量)的总和以及目标函数的值或贡献来定义。线性程序允许您的决策变量为十进制值,但整数程序强制决策变量为整数值。
那么,你的决定是什么?您的决定是将学生分配到老虎机。这些插槽具有事件需要和房间满足的功能。
在您的情况下,您希望最大化分配到广告位的学生数量。
你也有约束。在您的情况下,学生最多只能参加一个活动。
下面的网站提供了一个关于如何建模整数程序的好教程。
http://people.brunel.ac.uk/~mastjjb/jeb/or/moreip.html
对于特定于Java的实现,请使用以下链接。
http://javailp.sourceforge.net/
SolverFactory factory = new SolverFactoryLpSolve(); // use lp_solve
factory.setParameter(Solver.VERBOSE, 0);
factory.setParameter(Solver.TIMEOUT, 100); // set timeout to 100 seconds
/**
* Constructing a Problem:
* Maximize: 143x+60y
* Subject to:
* 120x+210y <= 15000
* 110x+30y <= 4000
* x+y <= 75
*
* With x,y being integers
*
*/
Problem problem = new Problem();
Linear linear = new Linear();
linear.add(143, "x");
linear.add(60, "y");
problem.setObjective(linear, OptType.MAX);
linear = new Linear();
linear.add(120, "x");
linear.add(210, "y");
problem.add(linear, "<=", 15000);
linear = new Linear();
linear.add(110, "x");
linear.add(30, "y");
problem.add(linear, "<=", 4000);
linear = new Linear();
linear.add(1, "x");
linear.add(1, "y");
problem.add(linear, "<=", 75);
problem.setVarType("x", Integer.class);
problem.setVarType("y", Integer.class);
Solver solver = factory.get(); // you should use this solver only once for one problem
Result result = solver.solve(problem);
System.out.println(result);
/**
* Extend the problem with x <= 16 and solve it again
*/
problem.setVarUpperBound("x", 16);
solver = factory.get();
result = solver.solve(problem);
System.out.println(result);
// Results in the following output:
// Objective: 6266.0 {y=52, x=22}
// Objective: 5828.0 {y=59, x=16}
答案 2 :(得分:0)
我首先要测量直接发生的事情。例如,在你的“任何其他案例”中,什么部分的任务都属于所有人,因此无所事事?
另外,虽然我们无法从给出的信息中真正判断出来,但似乎你的任何动作都不能进行“交换”,这可能是一个问题。如果时间表受到严格约束,那么一旦找到可行的方法,很可能您将无法将课程从A室移至B室,因为B室将被使用。您需要考虑将类从A移动到B以及将类从B移动到A的方法。
您有时也可以通过允许违反约束来改进。你可以允许它,而不是禁止交叉从来违反约束,但是与违规的“不良”成比例地惩罚适应性。
最后,您的其他运营商也可能存在问题。如果您的选择和替换操作员过于激进,您可以非常快速地收敛到比您开始时稍微好一些的东西。一旦你收敛,单独的突变很难让你重新进入富有成效的搜索。
答案 3 :(得分:0)
我认为GA对于这个问题没有任何问题,有些人无论如何都讨厌遗传算法。
以下是我要检查的内容:
首先你提到你的GA稳定在一个随机的“高”健身值,但这不是一件好事吗?在您的情况下,“高”健身是对应好还是坏?你有可能在你的代码的一部分中偏向“高”适应度而在另一部分中偏向“低”适应度,从而导致看似随机的结果。
我认为您希望对交叉操作背后的逻辑更加谨慎。基本上所有3种情况都有很多情况,其中任何一种选择都不会导致所有交叉个体的适应度增加,但是你仍在使用“资源”(一种可能用于另一种情况的任务)我认识到GA传统上会通过交叉进行分配会导致更糟糕的行为,但是无论如何你已经在交叉阶段进行了一些计算,为什么不选择一个实际上会提高健康水平的人呢?根本不交叉?
要考虑的可选注释:虽然您的迭代构造方法非常有趣,但这可能会导致您过度复杂的Gene表示可能会导致交叉问题。是否可以将单个解决方案建模为位或整数的数组(或2D数组)?即使数组结果很长,也许值得使用更简单的交叉程序。我建议谷歌搜索“ga基因表示时间表”你可能会发现一种你更喜欢的方法,可以更容易地扩展到许多人(100是一个相当小的人口规模的GA,但我知道你还在测试,也有多少代?)。
最后一点,我不确定您使用的语言是什么,但如果是Java并且您不需要手动编写GA代码,我建议您查看ECJ。也许即使您必须手动编码,它也可以帮助您发展您的代表或繁殖渠道。
答案 4 :(得分:0)
GA的新人可以犯下许多标准错误:
一般来说,在进行交叉时,请确保孩子有可能继承那些首先使父母或父母获胜的孩子。换句话说,选择基因组表示,其中基因组的“基因”片段具有对问题陈述的有意义的映射。一个常见的错误是将所有内容编码为位向量,然后在交叉中将位向量分割为随机位置,将位向量表示的好东西分开,从而破坏使个体浮动到顶部的事物作为一个好的候选者。 (有限的)整数的向量可能是更好的选择,其中整数可以被突变替换而不是交叉。使得父母获胜者不能保留某些东西(不一定是100%,但必须是某些方面)意味着你实际上是在进行随机搜索,这种搜索不会比线性搜索更好。
一般来说,使用的突变比您想象的要少得多。突变主要是为了保持人口的多样性。如果你的初始人口不包含任何具有部分优势的东西,那么你的人口对于手头的问题来说太小了,一般来说,高突变率将无济于事。
在这种特定情况下,您的交叉功能太复杂了。不要设置旨在使所有解决方案有效进入交叉的约束。相反,交叉功能应该可以自由地生成无效的解决方案,目标函数的工作是有点(而不是完全)惩罚无效的解决方案。如果您的GA有效,那么最终答案将不包含任何无效的分配,前提是100%有效的分配是可能的。坚持交叉的有效性阻止了有效的解决方案通过无效的解决方案将捷径转移到其他更有效的解决方案上。
我建议任何认为自己编写效果不佳的GA的人进行以下测试:运行GA几次,并记下达到可接受结果所需的代数。然后用随机选择替换获胜者选择步骤和目标函数(无论你使用什么 - 锦标赛,排名等),然后再次运行它。如果你仍然大致以与真实评估者/目标函数相同的速度收敛,那么你实际上并没有一个正常运行的GA。很多人说GA不起作用,他们的代码中出现了一些错误,这意味着GA的收敛速度和随机搜索一样慢,这足以让任何人从技术中解脱出来。