466 lines
14 KiB
Java
466 lines
14 KiB
Java
package eva2.problems;
|
|
|
|
|
|
import eva2.optimization.individuals.AbstractEAIndividual;
|
|
import eva2.optimization.individuals.InterfaceDataTypeBinary;
|
|
import eva2.optimization.strategies.InterfaceOptimizer;
|
|
import eva2.tools.math.RNG;
|
|
import eva2.util.annotation.Description;
|
|
|
|
import java.util.BitSet;
|
|
|
|
/**
|
|
* Maximize the value of the knapsack without exceeding the weight limit.
|
|
*/
|
|
@Description("Maximize the value of the knapsack without exceeding the weight limit")
|
|
public class BKnapsackProblem extends AbstractProblemBinary implements java.io.Serializable {
|
|
|
|
private int limit = 5000;
|
|
private double punish = 2.0;
|
|
private double localSearch = 0.0;
|
|
private boolean lamarckism = false;
|
|
private double problemSpecificInit = 0.0;
|
|
static final int[][] items = {
|
|
{334, -328},
|
|
{303, -291},
|
|
{228, -232},
|
|
{229, -226},
|
|
{346, -347},
|
|
{256, -257},
|
|
{243, -243},
|
|
{334, -328},
|
|
{382, -377},
|
|
{236, -234},
|
|
{301, -295},
|
|
{215, -209},
|
|
{298, -305},
|
|
{313, -313},
|
|
{219, -212},
|
|
{215, -214},
|
|
{250, -244},
|
|
{380, -373},
|
|
{319, -317},
|
|
{392, -396},
|
|
{304, -304},
|
|
{249, -252},
|
|
{337, -331},
|
|
{368, -365},
|
|
{356, -354},
|
|
{267, -260},
|
|
{258, -251},
|
|
{248, -252},
|
|
{391, -391},
|
|
{277, -273},
|
|
{293, -297},
|
|
{213, -214},
|
|
{223, -218},
|
|
{353, -359},
|
|
{337, -338},
|
|
{389, -396},
|
|
{251, -256},
|
|
{344, -340},
|
|
{243, -236},
|
|
{368, -367},
|
|
{380, -378},
|
|
{274, -269},
|
|
{289, -291},
|
|
{346, -353},
|
|
{271, -268},
|
|
{286, -281},
|
|
{294, -292},
|
|
{279, -282},
|
|
{226, -221},
|
|
{344, -337},
|
|
{353, -352},
|
|
{266, -265},
|
|
{391, -393},
|
|
{202, -199},
|
|
{224, -223},
|
|
{333, -330},
|
|
{369, -368},
|
|
{301, -308},
|
|
{241, -237},
|
|
{240, -233},
|
|
{236, -238},
|
|
{217, -212},
|
|
{308, -308},
|
|
{324, -320},
|
|
{218, -220},
|
|
{356, -358},
|
|
{236, -234},
|
|
{310, -299},
|
|
{382, -382},
|
|
{350, -357},
|
|
{377, -382},
|
|
{359, -351},
|
|
{322, -321},
|
|
{351, -347},
|
|
{315, -309},
|
|
{389, -382},
|
|
{212, -213},
|
|
{212, -209},
|
|
{352, -352},
|
|
{217, -215},
|
|
{369, -366},
|
|
{398, -399},
|
|
{320, -323},
|
|
{343, -353},
|
|
{329, -322},
|
|
{338, -332},
|
|
{289, -294},
|
|
{341, -339},
|
|
{201, -194},
|
|
{234, -235},
|
|
{392, -399},
|
|
{344, -347},
|
|
{371, -380},
|
|
{286, -288},
|
|
{210, -219},
|
|
{220, -218},
|
|
{253, -247},
|
|
{392, -390},
|
|
{319, -324},
|
|
{392, -386}};
|
|
|
|
public BKnapsackProblem() {
|
|
super();
|
|
}
|
|
|
|
public BKnapsackProblem(BKnapsackProblem b) {
|
|
//AbstractOptimizationProblem
|
|
cloneObjects(b);
|
|
// BKnapsackProblem
|
|
this.limit = b.limit;
|
|
this.punish = b.punish;
|
|
this.localSearch = b.localSearch;
|
|
this.lamarckism = b.lamarckism;
|
|
}
|
|
|
|
@Override
|
|
public int getProblemDimension() {
|
|
return items.length;
|
|
}
|
|
|
|
/**
|
|
* This method returns a deep clone of the problem.
|
|
*
|
|
* @return the clone
|
|
*/
|
|
@Override
|
|
public Object clone() {
|
|
return new BKnapsackProblem(this);
|
|
}
|
|
|
|
/**
|
|
* This method inits the Problem to log multiruns
|
|
*/
|
|
@Override
|
|
public void initializeProblem() {
|
|
// nothing to initialize here
|
|
}
|
|
|
|
protected void initIndy(int k, AbstractEAIndividual indy) {
|
|
indy.initialize(this);
|
|
if (RNG.flipCoin(this.problemSpecificInit)) {
|
|
BitSet tmpSet = new BitSet();
|
|
tmpSet.clear();
|
|
|
|
while (evaluate(tmpSet)[1] > 0) {
|
|
tmpSet.set(RNG.randomInt(0, items.length - 1));
|
|
}
|
|
((InterfaceDataTypeBinary) indy).setBinaryGenotype(tmpSet);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method evaluates a single individual and sets the fitness values
|
|
*
|
|
* @param individual The individual that is to be evalutated
|
|
*/
|
|
@Override
|
|
public void evaluate(AbstractEAIndividual individual) {
|
|
BitSet tmpBitSet;
|
|
double[] result;
|
|
|
|
tmpBitSet = ((InterfaceDataTypeBinary) individual).getBinaryData();
|
|
result = this.evaluate(tmpBitSet);
|
|
if (RNG.flipCoin(this.localSearch)) {
|
|
// first remove surplus assets
|
|
while (result[1] > 0) {
|
|
// search for an element to replace
|
|
int weakest = tmpBitSet.nextSetBit(0);
|
|
for (int i = 0; i < items.length; i++) {
|
|
if (tmpBitSet.get(i)) {
|
|
if (((-items[i][1]) / ((double) items[i][0])) < ((-items[weakest][1]) / ((double) items[weakest][0]))) {
|
|
weakest = i;
|
|
}
|
|
if (((-items[i][1]) / ((double) items[i][0])) == ((-items[weakest][1]) / ((double) items[weakest][0]))) {
|
|
if (RNG.flipCoin(0.5)) {
|
|
weakest = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// remove the weakest
|
|
tmpBitSet.clear(weakest);
|
|
result = this.evaluate(tmpBitSet);
|
|
}
|
|
// now lets see if we can replace some guy with a more efficient one
|
|
int weakest = tmpBitSet.nextSetBit(0);
|
|
if (weakest >= 0) {
|
|
int stronger = -1;
|
|
for (int i = 0; i < items.length; i++) {
|
|
if (tmpBitSet.get(i)) {
|
|
if (((-items[i][1]) / ((double) items[i][0])) < ((-items[weakest][1]) / ((double) items[weakest][0]))) {
|
|
weakest = i;
|
|
}
|
|
if (((-items[i][1]) / ((double) items[i][0])) == ((-items[weakest][1]) / ((double) items[weakest][0]))) {
|
|
if (RNG.flipCoin(0.5)) {
|
|
weakest = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tmpBitSet.clear(weakest);
|
|
result = this.evaluate(tmpBitSet);
|
|
int weight = 0;
|
|
for (int i = 0; i < items.length; i++) {
|
|
if (tmpBitSet.get(i)) {
|
|
weight += items[i][0];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < items.length; i++) {
|
|
if (items[i][0] < this.limit - weight) {
|
|
// possible candidate
|
|
if (stronger < 0) {
|
|
stronger = i;
|
|
} else {
|
|
if (items[i][1] < items[stronger][1]) {
|
|
stronger = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (stronger >= 0) {
|
|
tmpBitSet.set(stronger);
|
|
}
|
|
result = this.evaluate(tmpBitSet);
|
|
}
|
|
|
|
if (this.lamarckism) {
|
|
((InterfaceDataTypeBinary) individual).setBinaryGenotype(tmpBitSet);
|
|
}
|
|
}
|
|
result[0] += 5100;
|
|
individual.SetFitness(0, result[0]);
|
|
}
|
|
|
|
/**
|
|
* This is a eva2.problems.simple method that evaluates a given Individual. The fitness
|
|
* values of the individual will be set inside this method.
|
|
*
|
|
* @param b The BitSet that is to be evaluated.
|
|
* @param l The length of the BitSet.
|
|
* @return Double[]
|
|
*/
|
|
@Override
|
|
public double[] evaluate(BitSet b) {
|
|
double[] result = new double[3];
|
|
|
|
int l = items.length;
|
|
if (getProblemDimension() != l) {
|
|
System.err.println("Error in BKnapsack!");
|
|
}
|
|
result[0] = 0;
|
|
result[1] = 0; // the weight exceed
|
|
result[2] = 0; // net worth
|
|
for (int i = 0; i < l; i++) {
|
|
if (b.get(i)) {
|
|
result[1] += items[i][0];
|
|
result[2] += items[i][1];
|
|
} else {
|
|
b.clear(i);
|
|
}
|
|
}
|
|
// write the solution
|
|
result[1] = Math.max(0, result[1] - this.limit);
|
|
result[0] = (this.punish * result[1]) + result[2];
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method allows you to output a string that describes a found solution
|
|
* in a way that is most suiteable for a given problem.
|
|
*
|
|
* @param individual The individual that is to be shown.
|
|
* @return The description.
|
|
*/
|
|
@Override
|
|
public String getSolutionRepresentationFor(AbstractEAIndividual individual) {
|
|
BitSet tmpBitSet;
|
|
double[] report;
|
|
InterfaceDataTypeBinary tmpIndy;
|
|
String result = "Single Knapsack problem:\n";
|
|
|
|
tmpIndy = (InterfaceDataTypeBinary) individual;
|
|
tmpBitSet = tmpIndy.getBinaryData();
|
|
report = this.evaluate(tmpBitSet);
|
|
result += individual.getStringRepresentation() + "\n";
|
|
result += "Is worth: " + Math.abs(report[2]) + " and ";
|
|
if (report[1] == 0) {
|
|
result += "does not exceed the weight limit!";
|
|
} else {
|
|
result += "exceeds the weight limit by " + report[1];
|
|
}
|
|
result += "\n";
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method returns a string describing the optimization problem.
|
|
*
|
|
* @param opt The Optimizer that is used or had been used.
|
|
* @return The description.
|
|
*/
|
|
@Override
|
|
public String getStringRepresentationForProblem(InterfaceOptimizer opt) {
|
|
StringBuilder result = new StringBuilder(100);
|
|
|
|
result.append("Knapsack Problem:\n");
|
|
result.append("The task is to find a packing for a knapsack with limited size(");
|
|
result.append(this.limit);
|
|
result.append(") and maximal value.\n");
|
|
result.append("The default setting with limit=5000 allows a knapsack with value 5100. Note: value reads negative.");
|
|
result.append("Available items {(weight/value),...}: {");
|
|
for (int i = 0; i < BKnapsackProblem.items.length; i++) {
|
|
result.append("(");
|
|
result.append(BKnapsackProblem.items[i][0]);
|
|
result.append("; ");
|
|
result.append(BKnapsackProblem.items[i][1]);
|
|
result.append("),");
|
|
}
|
|
result.append("}\n");
|
|
result.append("Parameters:\n");
|
|
result.append("Punish rate: ");
|
|
result.append(this.punish);
|
|
result.append("\n");
|
|
result.append("Solution representation:\n");
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* This method allows the CommonJavaObjectEditorPanel to read the
|
|
* name to the current object.
|
|
*
|
|
* @return The name.
|
|
*/
|
|
@Override
|
|
public String getName() {
|
|
return "Single Knapsack Problem";
|
|
}
|
|
|
|
/**
|
|
* This method allows you to set the number of mulitruns that are to be performed,
|
|
* necessary for stochastic optimizers to ensure reliable results.
|
|
*
|
|
* @param punish The number of multiruns that are to be performed
|
|
*/
|
|
public void setPunishment(double punish) {
|
|
this.punish = punish;
|
|
}
|
|
|
|
public double getPunishment() {
|
|
return this.punish;
|
|
}
|
|
|
|
public String punishmentTipText() {
|
|
return "Rate of punishment if the Knapsack exceeds the weight limit.";
|
|
}
|
|
|
|
/**
|
|
* This method allows you to choose the EA individual
|
|
*
|
|
* @param indy The EAIndividual type
|
|
*/
|
|
public void setEAIndividual(InterfaceDataTypeBinary indy) {
|
|
this.template = (AbstractEAIndividual) indy;
|
|
}
|
|
|
|
public InterfaceDataTypeBinary getEAIndividual() {
|
|
return (InterfaceDataTypeBinary) this.template;
|
|
}
|
|
|
|
public String EAIndividualTipText() {
|
|
return "Choose the EA-individual to use.";
|
|
}
|
|
|
|
/**
|
|
* This method allows you to toggle the problemspecific
|
|
* initialization method, gives the chance of problem
|
|
* specific initialization
|
|
*
|
|
* @param b gives the chance of problemspecific initialization.
|
|
*/
|
|
public void setProblemSpecificInit(double b) {
|
|
this.problemSpecificInit = b;
|
|
}
|
|
|
|
public double getProblemSpecificInit() {
|
|
return this.problemSpecificInit;
|
|
}
|
|
|
|
public String problemSpecificInitTipText() {
|
|
return "Gives the chance of problemspecific initialization.";
|
|
}
|
|
|
|
/**
|
|
* This method allows you to toggle the problemspecific
|
|
* local search method, gives the chance of problem
|
|
* specific local search
|
|
*
|
|
* @param b gives the chance of problemspecific local search.
|
|
*/
|
|
public void setLocalSearch(double b) {
|
|
this.localSearch = b;
|
|
}
|
|
|
|
public double getLocalSearch() {
|
|
return this.localSearch;
|
|
}
|
|
|
|
public String localSearchTipText() {
|
|
return "Gives the chance of local search";
|
|
}
|
|
|
|
/**
|
|
* This method allows you to toggle the use of Lamarckism.
|
|
*
|
|
* @param b toggles lamarckism.
|
|
*/
|
|
public void setLamarckism(boolean b) {
|
|
this.lamarckism = b;
|
|
}
|
|
|
|
public boolean getLamarckism() {
|
|
return this.lamarckism;
|
|
}
|
|
|
|
public String lamarckismTipText() {
|
|
return "Lamarckism alters the genotype after the local search";
|
|
}
|
|
|
|
public int getWeightLimit() {
|
|
return limit;
|
|
}
|
|
|
|
public void setWeightLimit(int mLimit) {
|
|
limit = mLimit;
|
|
}
|
|
|
|
public String weightLimitTipText() {
|
|
return "Weight limit for the knapsack problem";
|
|
}
|
|
}
|