public class Vector {
	private float[] probability;
	
	public Vector(int size) {
		probability = new float[size];
		
		for (int i = 0; i < size; i++) {
			probability[i] = 0.5f;
		}
	}

	public float getProbability(int i) {
		return probability[i];
	}
}

public void update(Individual winner, Individual looser) {
	int size = probability.length;
		
	// mise à jour des probabilités
		
	for (int i = 0; i < size; i++) {
		float rate = Constants.LEARN_RATE;
		
		if (looser.isBitSet(i) != winner.isBitSet(i)) {
			rate += Constants.NEGATIVE_LEARN_RATE;
		}
		
		probability[i] = probability[i] * (1.0f - rate) + (winner.isBitSet(i) ? 1.0f : 0.0f) * rate;
	}
	
	// mutations
	
	for (int i = 0; i < size; i++) {
		if (Rand.getFloat() < Constants.MUTATION_PROBABILITY) {
			String info = "Mutation bit " + i + ", old = " + probability[i];
			
			probability[i] *= 1.0f - Constants.MUTATION_SHIFT;
			if (Rand.getFloat() < 0.5f) {
				probability[i] += Constants.MUTATION_SHIFT;
			}
			
			System.out.println(info + ", new = " + probability[i]);
		}
	}
}

public float getConvergence() {
	int size = probability.length;
	
	float convergence = 0.0f;
	
	for (int i = 0; i < size; i++) {
		convergence += 2.0f * Math.abs(probability[i] - 0.5f);
	}
	
	convergence /= (float) size;
	
	System.out.println("Convergence = " + convergence);
	
	return convergence;
}

public class Rand {

	// remplacer par "random = new Random(0)" pour retourner d’une exécution à l’autre les mêmes valeurs aléatoires
	private static Random random = new Random();
	
	public static float getFloat() {
		return random.nextFloat();
	}
	
}

public class Individual {

	private String name;
	private String genome;
	
	public Individual(Vector vector, String name) {
		int size = vector.getSize();
		
		StringBuilder sb = new StringBuilder();
		
		for (int i = 0; i < size; i++) {
			if (Rand.getFloat() < vector.getProbability(i)) {
				sb.append("1");
			} else {
				sb.append("0");
			}
		}
		
		this.name = name;
		this.genome = sb.toString();
	}
	
	public boolean isBitSet(int i) {
		return (genome.charAt(i) == '1');
	}

	public String getName() {
		return name;
	}
	
	public String getGenome() {
		return genome;
	}
	
}

private static Map<String, Integer> cache = new HashMap<>();

public void evaluate() {
	if (cache.get(genome) == null) {		
		int weight = 0;
		int utility = 0;
		
		for (int i = 0; i < genome.length(); i++) {
			if (genome.charAt(i) == '1') {
				weight += Constants.WEIGHT[i];
				utility += Constants.UTILITY[i];
			}
		}
		
		if (weight > Constants.MAX_WEIGHT) {
			utility = -1; // solution non valide
		}
		
		cache.put(genome, utility);			
	}
}

public int getFitness() {
	return cache.get(genome);
}

public class Population {

	private static int iteration = 0;
	private Individual[] individuals;
	
	private Individual winner = null;
	private Individual looser = null;
	
	public Population(int size, Vector vector) {
		iteration++;
		individuals = new Individual[size];
		
		for (int i = 0; i < size; i++) {
			individuals[i] = new Individual(vector, iteration + "-" + (i + 1));
		}
	}
	
	public void evaluate() {
		int winnerFitness = Integer.MIN_VALUE;
		int looserFitness = Integer.MAX_VALUE;
		
		for (int i = 0; i < individuals.length; i++) {
			individuals[i].evaluate();
			
			int fitness = individuals[i].getFitness();
			System.out.println("Fitness of " + individuals[i].getName() + " = " + fitness);
			
			if (fitness > winnerFitness) {
				winner = individuals[i];
				winnerFitness = fitness;
			}
			if (fitness < looserFitness) {
				looser = individuals[i];
				looserFitness = fitness;
			}
		}
	}
	
	public Individual getWinner() {
		return winner;
	}
	
	public Individual getLooser() {
		return looser;
	}
	
}

public class Optimiser {

	public static void run(int populationSize, int genomeSize) {
		
		Individual elite = null;
		Vector vector = new Vector(genomeSize);
		
		int iteration = 0;
		
		do {
			
			iteration++;
			System.out.println("Iteration " + iteration);
			
			Population population = new Population(populationSize, vector);
			population.evaluate();
			
			Individual winner = population.getWinner();
			Individual looser = population.getLooser();
			
			System.out.println("Winner is " + winner.getName() + " (fitness : " + winner.getFitness() + ")");
			System.out.println("Looser is " + looser.getName() + " (fitness : " + looser.getFitness() + ")");
			
			if (elite == null || elite.getFitness() < winner.getFitness()) {
				elite = winner;
				System.out.println("New elite " + elite.getName() + " with fitness " + elite.getFitness() + " !");
			} else {
				System.out.println(elite.getName() + " with fitness " + elite.getFitness() + " remains elite !");
			}
			
			System.out.println("Genome of elite is : " + elite.getGenome());
			
			vector.update(elite, looser);
			
		} while (iteration < Constants.MAX_ITERATIONS && vector.getConvergence() < Constants.MAX_CONVERGENCE);
		
	}

}

Iteration 1
Fitness of 1-1 = 187
Fitness of 1-2 = -1
Fitness of 1-3 = -1
Fitness of 1-4 = 271
Winner is 1-4 (fitness : 271)
Looser is 1-2 (fitness : -1)
New elite 1-4 with fitness 271 !
Genome of elite is : 0011011100
Convergence = 0.14500001
Iteration 2
Fitness of 2-1 = 278
Fitness of 2-2 = 265
Fitness of 2-3 = 238
Fitness of 2-4 = 229
Winner is 2-1 (fitness : 278)
Looser is 2-4 (fitness : 229)
New elite 2-1 with fitness 278 !
Genome of elite is : 1010101000
Convergence = 0.15975001
Iteration 3
Fitness of 3-1 = 157
Fitness of 3-2 = 207
Fitness of 3-3 = 421
Fitness of 3-4 = 170
Winner is 3-3 (fitness : 421)
Looser is 3-1 (fitness : 157)
New elite 3-3 with fitness 421 !
Genome of elite is : 1010111101
Mutation bit 6, old = 0.69371873, new = 0.7090328
Convergence = 0.2532425
(...)
Iteration 71
Fitness of 71-1 = 421
Fitness of 71-2 = 421
Fitness of 71-3 = 421
Fitness of 71-4 = 421
Winner is 71-1 (fitness : 421)
Looser is 71-1 (fitness : 421)
3-3 with fitness 421 remains elite !
Genome of elite is : 1010111101
Convergence = 0.9900888

public class Constants {

	public static final float LEARN_RATE = 0.1f;
	public static final float NEGATIVE_LEARN_RATE = 0.075f;
	public static final float MUTATION_PROBABILITY = 0.02f;
	public static final float MUTATION_SHIFT = 0.05f;
	
	public static final int MAX_ITERATIONS = 9999;
	public static final float MAX_CONVERGENCE = 0.99f;
	
}