Update knapsack to QKP with negative interaction values

This commit is contained in:
Just van Stam 2024-09-19 15:01:31 +02:00
parent 6ae5d622af
commit bf87fb7928
No known key found for this signature in database

View File

@ -52,6 +52,7 @@ pub struct Challenge {
pub difficulty: Difficulty,
pub weights: Vec<u32>,
pub values: Vec<u32>,
pub interaction_values: Vec<Vec<i32>>,
pub max_weight: u32,
pub min_value: u32,
}
@ -75,32 +76,57 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
fn generate_instance(seeds: [u64; 8], difficulty: &Difficulty) -> Result<Challenge> {
let mut rngs = RngArray::new(seeds);
// Generate weights w_i in the range [1, 50]
let weights: Vec<u32> = (0..difficulty.num_items)
.map(|_| rngs.get_mut().gen_range(1..50))
.map(|_| rngs.get_mut().gen_range(1..=50))
.collect();
// Generate values v_i in the range [50, 100]
let values: Vec<u32> = (0..difficulty.num_items)
.map(|_| rngs.get_mut().gen_range(1..50))
.map(|_| rngs.get_mut().gen_range(50..=100))
.collect();
// Generate interactive values V_ij in the range [1, 50], with V_ij == V_ji and V_ij where i==j is 0.
let mut interaction_values: Vec<Vec<i32>> = vec![vec![0; difficulty.num_items]; difficulty.num_items];
for i in 0..difficulty.num_items {
for j in (i + 1)..difficulty.num_items {
let value = rngs.get_mut().gen_range(-50..=50);
interaction_values[i][j] = value;
interaction_values[j][i] = value;
}
}
let max_weight: u32 = weights.iter().sum::<u32>() / 2;
// Baseline greedy algorithm
let mut sorted_value_to_weight_ratio: Vec<usize> = (0..difficulty.num_items).collect();
sorted_value_to_weight_ratio.sort_by(|&a, &b| {
let ratio_a = values[a] as f64 / weights[a] as f64;
let ratio_b = values[b] as f64 / weights[b] as f64;
// 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)
.map(|i| {
let tot_value = values[i] as i32 + interaction_values[i].iter().sum::<i32>();
let weight = weights[i];
let ratio = tot_value as f32 / weight as f32;
(i, ratio, weight)
})
.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()
});
let mut total_weight = 0;
let mut min_value = 0;
for &item in &sorted_value_to_weight_ratio {
if total_weight + weights[item] > max_weight {
continue;
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;
} else {
break;
}
min_value += values[item];
total_weight += weights[item];
}
min_value = (min_value as f64 * (1.0 + difficulty.better_than_baseline as f64 / 1000.0))
selected_indices.sort_unstable();
let mut min_value = calculate_total_value(&selected_indices, &values, &interaction_values);
min_value = (min_value as f32 * (1.0 + difficulty.better_than_baseline as f32 / 1000.0))
.round() as u32;
Ok(Challenge {
@ -108,6 +134,7 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
difficulty: difficulty.clone(),
weights,
values,
interaction_values,
max_weight,
min_value,
})
@ -118,17 +145,18 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
if selected_items.len() != solution.items.len() {
return Err(anyhow!("Duplicate items selected."));
}
if let Some(item) = selected_items
.iter()
.find(|&&item| item >= self.weights.len())
{
return Err(anyhow!("Item ({}) is out of bounds", item));
}
let total_weight = selected_items
.iter()
.map(|&item| self.weights[item])
.map(|&item| {
if item >= self.weights.len() {
return Err(anyhow!("Item ({}) is out of bounds", item));
}
Ok(self.weights[item])
}).collect::<Result<Vec<_>, _>>()?
.iter()
.sum::<u32>();
if total_weight > self.max_weight {
return Err(anyhow!(
"Total weight ({}) exceeded max weight ({})",
@ -136,10 +164,8 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
self.max_weight
));
}
let total_value = selected_items
.iter()
.map(|&item| self.values[item])
.sum::<u32>();
let selected_items_vec : Vec<usize> = selected_items.into_iter().collect();
let total_value = calculate_total_value(&selected_items_vec, &self.values, &self.interaction_values);
if total_value < self.min_value {
Err(anyhow!(
"Total value ({}) does not reach minimum value ({})",
@ -151,3 +177,29 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
}
}
}
pub fn calculate_total_value(indices: &Vec<usize>, values: &Vec<u32>, interaction_values: &Vec<Vec<i32>>) -> u32 {
let mut indices = indices.clone();
indices.sort_unstable();
let mut total_value = 0i32;
// Sum the individual values
for &i in &indices {
total_value += values[i] as i32;
}
// Sum the interactive values for pairs in indices
for i in 0..indices.len() {
for j in (i + 1)..indices.len() {
let idx_i = indices[i];
let idx_j = indices[j];
total_value += interaction_values[idx_i][idx_j];
}
}
match total_value {
v if v < 0 => 0u32,
v => v as u32
}
}