Pour des tailles suffisamment petites, une méta heuristique (comme la recherche locale) pourrait fonctionner correctement.
public class WeightedKnapsackProblem {
private int numberOfBins = 0;
private int numberOfItems = 0;
private int[][] scoreMatrix;
private int[] maxItemsPerBin;
public WeightedKnapsackProblem(int[][] score, int[] maxItemsPerBin){
this.numberOfItems = score.length;
this.numberOfBins = score[0].length;
this.scoreMatrix = score;
this.maxItemsPerBin = maxItemsPerBin;
}
public int score(int[] assignment){
int s = 0;
for(int i=0;i<numberOfItems;i++){
int item = i;
int bin = assignment[item];
s += scoreMatrix[item][bin];
}
return s;
}
public int cost(int[] assignment){
int c = 0;
int[] tmp = new int[numberOfBins];
for(int i=0;i<numberOfItems;i++){
tmp[assignment[i]]++;
}
for(int i=0;i<numberOfBins;i++){
if(tmp[i] > maxItemsPerBin[i])
c++;
}
return c;
}
private java.util.Random RANDOM = new java.util.Random(System.currentTimeMillis());
private int[] mutate(int[] orig){
int[] out = new int[orig.length];
for(int i=0;i<orig.length;i++)
out[i] = orig[i];
out[RANDOM.nextInt(out.length)] = RANDOM.nextInt(numberOfBins);
return out;
}
public int[] localSearch(){
// initial assignment
int[] a0 = new int[numberOfItems];
for(int i=0;i<numberOfItems;i++)
a0[i] = RANDOM.nextInt(numberOfBins);
// max score for any item
int max = scoreMatrix[0][0];
for(int i=0;i<scoreMatrix.length;i++)
for(int j=0;j<scoreMatrix[i].length;j++)
max = java.lang.Math.max(max, scoreMatrix[i][j]);
// local search
int[] a1 = mutate(a0);
int c0 = score(a0) - cost(a0) * max * max;
int c1 = score(a1) - cost(a1) * max * max;
for(int i=0;i<1000;i++){
if(c1 > c0){
a0 = a1;
c0 = c1;
}
a1 = mutate(a0);
c1 = score(a1) - cost(a1) * max;
}
// return
return a0;
}
public int[] repeatedLocalSearch(int k){
// max score for any item
int max = scoreMatrix[0][0];
for(int i=0;i<scoreMatrix.length;i++)
for(int j=0;j<scoreMatrix[i].length;j++)
max = java.lang.Math.max(max, scoreMatrix[i][j]);
int[] a0 = localSearch();
int c0 = score(a0) - cost(a0) * max * max;
for(int i=0;i<k;i++){
int[] a1 = localSearch();
int c1 = score(a1) - cost(a1) * max * max;
if(c1 > c0){
c0 = c1;
a0 = a1;
}
}
return a0;
}
}
Ce programme génère essentiellement une affectation aléatoire des articles aux bacs, et tente itérativement d'améliorer cette situation initiale.
Comme cette technique permet de se piéger facilement dans des optimums locaux, il est judicieux de la répéter plusieurs fois, avec des configurations de départ différentes (aléatoires).
Le programme suivant utilise la classe WeightedKnapsackProblem pour générer une solution possible :
int[][] score = { {9,5,2,3},
{8,9,2,1},
{3,2,1,4},
{1,2,1,2},
{7,8,9,2},
{0,1,2,3}
};
int[] maxItemsPerBin = {2,1,2,1};
WeightedKnapsackProblem wkp = new WeightedKnapsackProblem(score, maxItemsPerBin);
int[] assignment = wkp.repeatedLocalSearch(10);
System.out.println(wkp.score(assignment));
System.out.println(wkp.cost(assignment));
System.out.println(Arrays.toString(assignment));
Cela s'imprime :
34
0
[0, 1, 0, 3, 2, 2]
En d'autres termes, le problème de démonstration peut être résolu pour un score maximal de 34.
Le nombre d'articles mal placés (qui dépasseraient la capacité de la poubelle) est de 0.
Et la mission est :
- premier article dans le premier bac
- deuxième article dans le deuxième bac
- troisième article dans le premier bac
- quatrième article dans le quatrième bac
- cinquième et sixième articles dans le troisième bac