forked from basvanwesting/genetic-algorithm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathevolve_knapsack.rs
103 lines (88 loc) · 2.95 KB
/
evolve_knapsack.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use genetic_algorithm::strategy::evolve::prelude::*;
// see https://en.wikipedia.org/wiki/Knapsack_problem
// example data 10 items with (weight, value):
// (23, 505), (26, 352), (20, 458), (18, 220), (32, 354), (27, 414), (29, 498), (26, 545), (30, 473), (27, 543),
// Optimal value is 1270 with items: (18, 220), (23, 505) and (26, 545)
type WeightLimit = u16;
type Weight = u16;
type Value = u16;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
struct Item(pub Weight, pub Value);
#[derive(Clone, Debug)]
struct KnapsackFitness<'a> {
pub items: &'a Vec<Item>,
pub weight_limit: WeightLimit,
}
impl KnapsackFitness<'_> {
const EXCESS_WEIGHT_PENALTY: FitnessValue = 1000;
}
impl Fitness for KnapsackFitness<'_> {
type Genotype = BinaryGenotype;
fn calculate_for_chromosome(
&mut self,
chromosome: &FitnessChromosome<Self>,
_genotype: &FitnessGenotype<Self>,
) -> Option<FitnessValue> {
let item_indices: Vec<usize> = chromosome
.genes
.iter()
.enumerate()
.filter_map(|(i, v)| if *v { Some(i) } else { None })
.collect();
let weight: u16 = item_indices.iter().map(|i| self.items[*i].0).sum();
let value: u16 = item_indices.iter().map(|i| self.items[*i].1).sum();
// base score with total value
let mut score = value as FitnessValue;
// penalize score with excess weight, to nudge towards valid solutions
if weight > self.weight_limit {
score -= (weight - self.weight_limit) as FitnessValue * Self::EXCESS_WEIGHT_PENALTY;
}
Some(score)
}
}
fn main() {
env_logger::init();
let items: Vec<Item> = vec![
Item(23, 505),
Item(26, 352),
Item(20, 458),
Item(18, 220),
Item(32, 354),
Item(27, 414),
Item(29, 498),
Item(26, 545),
Item(30, 473),
Item(27, 543),
];
let weight_limit = 67;
let fitness = KnapsackFitness {
items: &items,
weight_limit,
};
let genotype = BinaryGenotype::builder()
.with_genes_size(items.len())
.build()
.unwrap();
println!("{}", genotype);
let mut evolve = Evolve::builder()
.with_genotype(genotype)
.with_target_population_size(100)
.with_max_stale_generations(100)
.with_fitness(fitness)
.with_mutate(MutateSingleGene::new(0.2))
.with_crossover(CrossoverSinglePoint::new())
.with_select(SelectTournament::new(4, 0.9))
.with_reporter(EvolveReporterDuration::new())
.build()
.unwrap();
evolve.call();
// println!("{}", evolve);
if let Some(best_genes) = evolve.best_genes() {
let selected_items =
best_genes
.iter()
.enumerate()
.filter_map(|(i, v)| if *v { Some(&items[i]) } else { None });
println!("selected items: {:?}", selected_items.collect::<Vec<_>>());
}
}