diff --git a/tig-challenges/src/knapsack.rs b/tig-challenges/src/knapsack.rs index 461c96d..112ec0b 100644 --- a/tig-challenges/src/knapsack.rs +++ b/tig-challenges/src/knapsack.rs @@ -52,6 +52,7 @@ pub struct Challenge { pub difficulty: Difficulty, pub weights: Vec, pub values: Vec, + pub interaction_values: Vec>, pub max_weight: u32, pub min_value: u32, } @@ -75,32 +76,57 @@ impl crate::ChallengeTrait for Challenge { fn generate_instance(seeds: [u64; 8], difficulty: &Difficulty) -> Result { let mut rngs = RngArray::new(seeds); + // Generate weights w_i in the range [1, 50] let weights: Vec = (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 = (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![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::() / 2; - // Baseline greedy algorithm - let mut sorted_value_to_weight_ratio: Vec = (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::(); + 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 for Challenge { difficulty: difficulty.clone(), weights, values, + interaction_values, max_weight, min_value, }) @@ -118,17 +145,18 @@ impl crate::ChallengeTrait 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::, _>>()? + .iter() .sum::(); + if total_weight > self.max_weight { return Err(anyhow!( "Total weight ({}) exceeded max weight ({})", @@ -136,10 +164,8 @@ impl crate::ChallengeTrait for Challenge { self.max_weight )); } - let total_value = selected_items - .iter() - .map(|&item| self.values[item]) - .sum::(); + let selected_items_vec : Vec = 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 for Challenge { } } } + +pub fn calculate_total_value(indices: &Vec, values: &Vec, interaction_values: &Vec>) -> 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 + } +} \ No newline at end of file