/*
* Artificial Intelligence for Humans
* Volume 1: Fundamental Algorithms
* Java Version
* http://www.aifh.org
* http://www.jeffheaton.com
*
* Code repository:
* https://github.com/jeffheaton/aifh
* Copyright 2013 by Jeff Heaton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package com.heatonresearch.aifh.examples.discrete;
import com.heatonresearch.aifh.discrete.DiscreteAnneal;
import com.heatonresearch.aifh.randomize.GenerateRandom;
import com.heatonresearch.aifh.randomize.MersenneTwisterGenerateRandom;
/**
* This example program shows how to use discrete simulated annealing to find solutions to the Knapsack problem.
* <p/>
* http://en.wikipedia.org/wiki/Knapsack_problem
*/
public class KnapsackAnneal extends DiscreteAnneal {
/**
* Number of items to choose from.
*/
public static final int NUM_ITEMS_TO_CHOOSE = 25;
/**
* The max weight of the knapsack.
*/
public static final int KNAPSACK_MAX_WEIGHT = 50;
/**
* The max weight for an item.
*/
public static final int ITEM_MAX_WEIGHT = 20;
/**
* The max value for an item.
*/
public static final int ITEM_MAX_VALUE = 1000;
/**
* The profit for each item.
*/
private final int[] profit = new int[NUM_ITEMS_TO_CHOOSE + 1];
/**
* The weight for each item.
*/
private final int[] weight = new int[NUM_ITEMS_TO_CHOOSE + 1];
/**
* The current items taken.
*/
private final boolean[] currentTaken;
/**
* A backup of the items taken, in case we need to revert.
*/
private final boolean[] backupTaken;
/**
* The best set of items so far.
*/
private final boolean[] bestTaken;
/**
* A random number generator.
*/
private final GenerateRandom rnd = new MersenneTwisterGenerateRandom();
/**
* Construct the object and init.
*/
public KnapsackAnneal() {
super(1000, 40000, 0.001);
this.currentTaken = new boolean[NUM_ITEMS_TO_CHOOSE];
this.backupTaken = new boolean[NUM_ITEMS_TO_CHOOSE];
this.bestTaken = new boolean[NUM_ITEMS_TO_CHOOSE];
for (int i = 0; i < this.currentTaken.length; i++) {
this.currentTaken[i] = this.rnd.nextBoolean();
}
balance();
}
/**
* Run the example.
*/
public void run() {
// Generate a random set of items.
for (int n = 0; n < NUM_ITEMS_TO_CHOOSE; n++) {
profit[n] = (int) (Math.random() * ITEM_MAX_VALUE);
weight[n] = (int) (Math.random() * ITEM_MAX_WEIGHT);
}
// now begin main loop, and find a minimum
while (!done()) {
this.iteration();
System.out.println("Iteration #" + getK() + ", Best Score=" + this.getBestScore() + "," + getStatus());
}
// print results
System.out.println("item" + "\t" + "profit" + "\t" + "weight" + "\t" + "take");
for (int n = 0; n < NUM_ITEMS_TO_CHOOSE; n++) {
System.out.println((n + 1) + "\t" + profit[n] + "\t" + weight[n] + "\t" + bestTaken[n]);
}
}
/**
* {@inheritDoc}
*/
@Override
public void backupState() {
System.arraycopy(this.currentTaken, 0, this.backupTaken, 0, this.currentTaken.length);
}
/**
* {@inheritDoc}
*/
@Override
public void restoreState() {
System.arraycopy(this.backupTaken, 0, this.currentTaken, 0, this.currentTaken.length);
}
/**
* {@inheritDoc}
*/
@Override
public void foundNewBest() {
System.arraycopy(this.currentTaken, 0, this.bestTaken, 0, this.currentTaken.length);
}
/**
* {@inheritDoc}
*/
@Override
public void moveToNeighbor() {
// check for strange case where we have everything!
// This means that the max allowed knapsack weight is greater than the total of grabbing everything.
// This is kind of pointless, but don't go into an endless loop!
boolean holdingEverythingAlready = true;
for (final boolean aCurrentTaken : this.currentTaken) {
if (!aCurrentTaken) {
holdingEverythingAlready = false;
break;
}
}
if (!holdingEverythingAlready) {
// try to add something
int pt = this.rnd.nextInt(this.currentTaken.length); // prime
while (this.currentTaken[pt]) {
pt = this.rnd.nextInt(this.currentTaken.length);
}
// add the item we found
this.currentTaken[pt] = true;
// We probably need to drop something now.
balance();
}
}
/**
* {@inheritDoc}
*/
@Override
public double evaluate() {
if (calculateTotalWeight() > KNAPSACK_MAX_WEIGHT) {
return 0;
}
int result = 0;
for (int i = 0; i < this.currentTaken.length; i++) {
if (this.currentTaken[i]) {
result += this.profit[i];
}
}
return result;
}
/**
* @return The total weight.
*/
private int calculateTotalWeight() {
int result = 0;
for (int i = 0; i < this.currentTaken.length; i++) {
if (this.currentTaken[i]) {
result += this.weight[i];
}
}
return result;
}
/**
* Balance and keep below max weight.
*/
private void balance() {
while (calculateTotalWeight() > KNAPSACK_MAX_WEIGHT) {
final int remove = rnd.nextInt(this.currentTaken.length);
this.currentTaken[remove] = false;
}
}
/**
* The main method.
*
* @param args Not used.
*/
public static void main(final String[] args) {
final KnapsackAnneal prg = new KnapsackAnneal();
prg.run();
}
}