Improving the baseline algorithm for QKP

This commit is contained in:
Fatigue24 2024-11-03 02:29:14 +02:00
parent ff208a5152
commit eeec85ae5b

View File

@ -102,31 +102,127 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
// Precompute the ratio between the total value (value + sum of interactive values) and
// weight for each item. Pair the ratio with the item's weight and index
let mut value_weight_ratios: Vec<(usize, f32, u32)> = (0..difficulty.num_items)
let mut item_values: Vec<(usize, f32)> = (0..difficulty.num_items)
.map(|i| {
let total_value = values[i] as i32 + interaction_values[i].iter().sum::<i32>();
let weight = weights[i];
let ratio = total_value as f32 / weight as f32;
(i, ratio, weight)
let ratio = total_value as f32 / weights[i] as f32;
(i, ratio)
})
.collect();
// Sort the list of tuples by value-to-weight ratio in descending order
value_weight_ratios.sort_unstable_by(|&(_, ratio_a, _), &(_, ratio_b, _)| {
ratio_b.partial_cmp(&ratio_a).unwrap()
});
// Sort the list of ratios in descending order
item_values.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
// Step 1: Initial solution obtained by greedily selecting items based on value-weight ratio
let mut selected_items = Vec::with_capacity(difficulty.num_items);
let mut unselected_items = Vec::with_capacity(difficulty.num_items);
let mut total_weight = 0;
let mut selected_indices = Vec::new();
for &(i, _, weight) in &value_weight_ratios {
if total_weight + weight <= max_weight {
selected_indices.push(i);
total_weight += weight;
let mut total_value = 0;
for &(item, _) in &item_values {
if total_weight + weights[item] <= max_weight {
total_weight += weights[item];
total_value += values[item] as i32;
for &prev_item in &selected_items {
total_value += interaction_values[item][prev_item];
}
selected_items.push(item);
} else {
unselected_items.push(item);
}
}
selected_indices.sort_unstable();
// Step 2: Improvement of solution with Local Search and Tabu-List
// Precompute sum of interaction values with each selected item for all items(if i==j, then interaction_values[i][j] == 0)
let mut interaction_sum_list = vec![0; difficulty.num_items];
for x in 0..difficulty.num_items {
interaction_sum_list[x] = values[x] as i32;
for &item in &selected_items {
interaction_sum_list[x] += interaction_values[x][item];
}
}
// Optimized local search with tabu list
let max_iterations = 100;
let mut tabu_list = vec![0; difficulty.num_items];
let mut min_value = calculate_total_value(&selected_indices, &values, &interaction_values);
for _ in 0..max_iterations {
let mut best_improvement = 0;
let mut best_swap = None;
for i in 0..unselected_items.len() {
let new_item = unselected_items[i];
if tabu_list[new_item] > 0 {
continue;
}
let new_item_values_sum = interaction_sum_list[new_item];
// Greedy assumption that remove_item_values_sum + interaction_values[new_item][remove_item] is higher or equal to zero
// It's not only a huge optimization, but it also produces better solutions most of the time
if new_item_values_sum < best_improvement {
continue;
}
// Compute minimal weight of remove_item required to put new_item
let min_weight = weights[new_item] as i32 - (max_weight as i32 - total_weight as i32);
for j in 0..selected_items.len() {
let remove_item = selected_items[j];
if tabu_list[remove_item] > 0 {
continue;
}
// Don't check the weight if there is enough remaining capacity
if min_weight < 0
{
// Skip a remove_item if the remaining capacity after removal is insufficient to push a new_item
let removed_item_weight = weights[remove_item] as i32;
if removed_item_weight < min_weight {
continue;
}
}
let remove_item_values_sum = interaction_sum_list[remove_item];
let value_diff = new_item_values_sum - remove_item_values_sum - interaction_values[new_item][remove_item];
if value_diff > best_improvement {
best_improvement = value_diff;
best_swap = Some((i, j));
}
}
}
if let Some((unselected_index, selected_index)) = best_swap {
let new_item = unselected_items[unselected_index];
let remove_item = selected_items[selected_index];
selected_items.swap_remove(selected_index);
unselected_items.swap_remove(unselected_index);
selected_items.push(new_item);
unselected_items.push(remove_item);
total_value += best_improvement;
total_weight = total_weight + weights[new_item] - weights[remove_item];
// Update sum of interaction values after swapping items
for x in 0..difficulty.num_items {
interaction_sum_list[x] += interaction_values[x][new_item] - interaction_values[x][remove_item];
}
// Update tabu list
tabu_list[new_item] = 3;
tabu_list[remove_item] = 3;
} else {
break; // No improvement found, terminate local search
}
// Decrease tabu counters
for t in tabu_list.iter_mut() {
*t = if *t > 0 { *t - 1 } else { 0 };
}
}
let mut min_value = calculate_total_value(&selected_items, &values, &interaction_values);
min_value = (min_value as f32 * (1.0 + difficulty.better_than_baseline as f32 / 1000.0))
.round() as u32;