! tie it all together

This commit is contained in:
2026-01-31 12:55:39 +01:00
parent 7703846db9
commit 320c75b2d5

View File

@@ -13,6 +13,10 @@ DATA_FILE :: "res/knapPI_12_500_1000_82.csv"
NUMBER_OF_ITEMS :: 500
CAPACITY :: 280785
POPULATION_SIZE :: 100
GENERATIONS :: 100
TOURNAMENT_SIZE :: 3
CROSSOVER_RATE :: 0.8
MUTATION_RATE :: 0.01
Item :: struct {
profit, weight: int,
@@ -71,7 +75,18 @@ destroy_population :: proc(pop: ^Population) {
}
}
tournament_selection :: proc(pop: ^Population, fitnesses: []int, k: int = 3) -> ^Chromosome {
evaluate_population :: proc(pop: ^Population) -> (res: [POPULATION_SIZE]int) {
for &chrom, i in pop {
res[i] = fitness(&chrom)
}
return
}
tournament_selection :: proc(
pop: ^Population,
fitnesses: []int,
k: int = TOURNAMENT_SIZE,
) -> ^Chromosome {
best_idx := rand.int_max(POPULATION_SIZE)
best_fitness := fitnesses[best_idx]
@@ -173,7 +188,7 @@ uniform_crossover :: proc(
return
}
bit_flip_mutation :: proc(chrom: ^Chromosome, mutation_rate: f32 = 0.01) {
bit_flip_mutation :: proc(chrom: ^Chromosome, mutation_rate: f32 = MUTATION_RATE) {
for i in 0 ..< NUMBER_OF_ITEMS {
if rand.float32() < mutation_rate {
current := bit_array.get(chrom, i)
@@ -240,11 +255,102 @@ elitism_survivor_selection :: proc(
return
}
run_ga :: proc() {
population := generate_population()
defer destroy_population(&population)
best_fitness := math.min(int)
best_generation := 0
for gen in 0 ..< GENERATIONS {
fitnesses := evaluate_population(&population)
// log best fitnesses
for f, i in fitnesses {
if f <= best_fitness {continue}
best_fitness = f
best_generation = gen
chrom := &population[i]
tot_profit, tot_weight := 0, 0
for idx in 0 ..< bit_array.len(chrom) {
if !bit_array.get(chrom, idx) {continue}
tot_profit += items[idx].profit
tot_weight += items[idx].weight
}
fmt.printfln(
"gen %d: best fitness=%d, profit=%d (capacity=%d)",
gen,
f,
tot_profit,
tot_weight,
CAPACITY,
)
}
// create offspring
offspring: Population
for i := 0; i < POPULATION_SIZE; i += 2 {
parent1 := tournament_selection(&population, fitnesses[:])
parent2 := tournament_selection(&population, fitnesses[:])
child1, child2: Chromosome
if rand.float32() < CROSSOVER_RATE {
child1, child2 = single_point_crossover(parent1, parent2)
} else {
child1 = bit_array.create(NUMBER_OF_ITEMS)^
child2 = bit_array.create(NUMBER_OF_ITEMS)^
for j in 0 ..< NUMBER_OF_ITEMS {
bit_array.set(&child1, j, bit_array.get(parent1, j))
bit_array.set(&child2, j, bit_array.get(parent2, j))
}
}
bit_flip_mutation(&child1)
bit_flip_mutation(&child2)
offspring[i] = child1
if i + 1 < POPULATION_SIZE {
offspring[i + 1] = child2
}
}
// survivor selection
offspring_fitnesses := evaluate_population(&offspring)
new_population := elitism_survivor_selection(
&population,
&offspring,
fitnesses[:],
offspring_fitnesses[:],
)
// clean-up
destroy_population(&population)
destroy_population(&offspring)
population = new_population
}
fmt.println()
fmt.printfln("final best: fitness=%d (found at generation %d)", best_fitness, best_generation)
}
main :: proc() {
items, ok := read_data(DATA_FILE)
if !ok {
fmt.eprintln("failed to read data from", DATA_FILE)
return
}
fmt.println(items)
fmt.println("running genetic algorithm for binary knapsack problem.")
fmt.printfln(
"items: %d, capacity: %d, population: %d, generations: %d",
NUMBER_OF_ITEMS,
CAPACITY,
POPULATION_SIZE,
GENERATIONS,
)
fmt.println()
run_ga()
}