尝试在这里研究遗传算法,我无法弄清楚为什么同一个数组会在几行后返回不同的结果。
这是代码:
Population(int PopulationSize, ArrayList<Vessel> vessels) {
this.PopulationSize = PopulationSize;
solutions = new ArrayList(PopulationSize);
for (int i = 0; i < PopulationSize; i++) {
Solution s = new Solution(vessels);
solutions.add(s);
System.out.println(i+" : "+solutions.get(i).getFitness());
}
for (int i = 0; i < solutions.size(); i++) {
System.out.println(solutions.get(i).getFitness());
}
}
这是输出:
0 : 4432.4956
1 : 2673.922
2 : 5166.998
3 : 4396.5454
4 : 5687.8555
5 : 2907.695
6 : 5005.9937
7 : 3289.161
8 : 3302.1948
9 : 5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
更多源代码。
Solution.java
package geneticalgorithm;
import static java.lang.Math.abs;
import java.util.ArrayList;
public class Solution {
ArrayList<Vessel> vessels;
public Solution(ArrayList<Vessel> vessels) {
this.vessels = new ArrayList<>(vessels);
generateSolution();
}
public void generateSolution() {
do {
for (Vessel vessel : vessels) {
vessel.generateParams();
}
} while(!isValid());
}
/* Getters */
public Vessel getIndividual(int index) {
return vessels.get(index);
}
// Save individual
public void saveIndividual(int index, Vessel v) {
vessels.set(index, v);
}
public float getFitness() {
float fitness = 0, BPCost = 0, CHDelay=0, CBDelay=0;
for (Vessel v : vessels) {
BPCost += v.DistancePenaltyCost*abs(v.X-v.LowestCostBerthing);
CHDelay += v.TimePenaltyCost*(abs(2*(v.Y + v.HandlingTime - v.SailingTime))/2);
CBDelay += v.BunkeringDelayCost*v.BunkeringNeeded*(abs(2*(v.BA+v.BunkeringProcessing-v.SailingTime))/2);
}
fitness = BPCost + CHDelay + CBDelay;
return fitness;
}
public boolean isValid() {
boolean XOverlaps, YOverlaps;
for (Vessel vTemp : vessels) {
for (Vessel v : vessels) {
// Initialization
XOverlaps = YOverlaps = false;
// Checking for X overlaps
if(v.X > vTemp.X && v.X < (vTemp.X + vTemp.Length))
XOverlaps = true;
// Checking for Y overlaps
if(v.Y >= vTemp.Y && v.Y <= (vTemp.Y + vTemp.HandlingTime))
YOverlaps = true;
// Breaking the loop
if(XOverlaps && YOverlaps)
return false;
}
}
return true;
}
}
Vessel.java
package geneticalgorithm;
import java.util.Random;
public class Vessel {
Float X,Y,BA;
Byte Z;
Float ETA;
Float Length;
Float LowestCostBerthing;
Float HandlingTime;
Float SailingTime;
Float DistancePenaltyCost;
Float TimePenaltyCost;
Float BunkeringDelayCost;
Byte BunkeringNeeded;
Float BunkeringProcessing;
void generateParams() {
Random rand = new Random();
Float MinBerth = 0.0f, MaxBerth = LowestCostBerthing + Length;
if(LowestCostBerthing - Length > 0)
MinBerth = LowestCostBerthing - Length;
if(MaxBerth > DataLoader.Quay)
MaxBerth = DataLoader.Quay - Length;
X = rand.nextFloat() * (MaxBerth - MinBerth) + MinBerth;
Y = rand.nextFloat() * ((ETA+24) - (ETA+1)) + (ETA+1);
if(BunkeringNeeded==1)
{
Z = (byte) Math.round(Math.random()); // 0 OR 1
if(Z!=0) {
BA = Y;
} else if(Y - ETA < BunkeringProcessing) {
BA = SailingTime;
} else if(Y - ETA > BunkeringProcessing) {
BA = ETA;
}
} else {
Z = 0;
BA = 0.0f;
}
}
}
有什么想法吗?谢谢!
答案 0 :(得分:2)
尝试在这里研究遗传算法,我无法弄清楚为什么同一个数组会在几行后返回不同的结果。
每个Solution
都在Vessel
个对象的列表中传递。此列表用于在每个vessels
内创建另一个Solution
列表,但重要的是要注意新列表仍包含原始 Vessel
对象。
this.vessels = new ArrayList<>(vessels);
此代码不会创建新的Vessel
对象 - 它只是创建相同对象的新列表。这意味着即使您正在创建许多不同的Solution
,每个人都在处理相同的 Vessel
。每当调用generateSolution()
时,它就会在相同的共享vessel.generateParams()
上调用Vessel
并更新先前Solution
个对象中的值。这就是为什么最后Solution
生成的值是当您返回列表时打印出来的值。
我不确定目的是什么,但如果您对此感到惊讶,那么您可能希望在每个解决方案中添加新 Vessel
对象,而不是重复使用同样的清单。
修改强>
在查看Vessel
对象时,您会看到一些真正常量的字段:
Float Length;
Float LowestCostBerthing;
Float HandlingTime;
你还有其他一些计算领域:
Float X,Y,BA;
Byte Z;
您真的应该考虑将有关Vessel
的常量与解决方案生成的计算分开。另外,作为旁白,字段应以小写字母开头,而不是最初的对象(float
,byte
),如果它们是不可变的,则应为final
。
一个想法是拥有一个VesselCalculation
对象来保存计算并且还有一个Vessel
字段:
public class VesselCalculation {
private final Vessel vessel;
private float x, y, ba;
private byte z;
然后,Solution
会生成一个VesselCalculation
的列表,该列表将被修改。 Vessel
个对象将与不同的对象共享
VesselCalculation
个对象,但会保持不变。
private final List<VesselCalculation> vesselCalculations = new ArrayList<>();
public Solution(ArrayList<Vessel> vessels) {
for (Vessel vessel : vessels) {
vesselCalculations.add(new VesselCalculation(vessel));
}
generateSolution();
}
然后, generateSolution()
只会对此特定VesselCalculation
的本地Solution
对象进行更改。
答案 1 :(得分:1)
getFitness()
似乎只使用从vessels
中提取的值。
由于每个Solution
都包含相同的vessels
实例,因此在给定特定状态getFitness()
的情况下,每个解决方案都会返回vessels
的相同响应。
Solution
的构造函数调用generateSolution()
,修改vessels
的内容。这就是每次创建新getFitness()
时Solution
结果都会发生变化的原因。
我不知道您所需算法的详细信息,但似乎每个Solution
都应该拥有自己的列表副本。它是否应该在其构造函数中进行复制,或者是否构造函数负责创建该副本。
因为Vessel
对象本身是可变的(并且使用了可变性),所以您还需要复制它们,而不是制作包含对相同列表的引用的另一个列表。
在这种情况下,使用不可变集合和不可变对象可以为您节省大量时间。
答案 2 :(得分:0)
如果您遇到同样的问题,我可以使用this solution解决此问题。我们的想法是循环遍历数组,并在克隆它们时逐个复制每个元素。在ArrayList上调用clone()函数没有效果,但是在保持相同引用的同时克隆ArrayList。
您必须实现可克隆接口并在手动克隆每个字段时覆盖克隆方法。