重写ArrayList对象

时间:2017-05-17 15:58:02

标签: java oop arraylist

尝试在这里研究遗传算法,我无法弄清楚为什么同一个数组会在几行后返回不同的结果。

这是代码:

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;
        }
    }

}

有什么想法吗?谢谢!

3 个答案:

答案 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的常量与解决方案生成的计算分开。另外,作为旁白,字段应以小写字母开头,而不是最初的对象(floatbyte),如果它们是不可变的,则应为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。

您必须实现可克隆接口并在手动克隆每个字段时覆盖克隆方法。