mirror of
https://github.com/tig-pool-nk/tig-monorepo.git
synced 2026-02-21 17:37:21 +08:00
Align knapsack instance generation with academic benchmarks
This commit is contained in:
parent
cd12b6f72d
commit
fea23bf221
@ -77,29 +77,48 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
|
||||
fn generate_instance(seed: [u8; 32], difficulty: &Difficulty) -> Result<Challenge> {
|
||||
let mut rng = SmallRng::from_seed(StdRng::from_seed(seed).gen());
|
||||
|
||||
|
||||
// Set constant density for value generation
|
||||
let density = 0.25;
|
||||
|
||||
// Generate weights w_i in the range [1, 50]
|
||||
let weights: Vec<u32> = (0..difficulty.num_items)
|
||||
.map(|_| rng.gen_range(1..=50))
|
||||
.collect();
|
||||
// Generate values v_i in the range [50, 100]
|
||||
|
||||
// Generate values v_i in the range [1, 100] with density probability, 0 otherwise
|
||||
let values: Vec<u32> = (0..difficulty.num_items)
|
||||
.map(|_| rng.gen_range(50..=100))
|
||||
.map(|_| {
|
||||
if rng.gen_bool(density) {
|
||||
rng.gen_range(1..=100)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.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];
|
||||
|
||||
// Generate interaction values V_ij with the following properties:
|
||||
// - V_ij == V_ji (symmetric matrix)
|
||||
// - V_ii == 0 (diagonal is zero)
|
||||
// - Values are in range [1, 100] with density probability, 0 otherwise
|
||||
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 = rng.gen_range(-50..=50);
|
||||
let value = if rng.gen_bool(density) {
|
||||
rng.gen_range(1..=100)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Set both V_ij and V_ji due to symmetry
|
||||
interaction_values[i][j] = value;
|
||||
interaction_values[j][i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let max_weight: u32 = weights.iter().sum::<u32>() / 2;
|
||||
|
||||
|
||||
// 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 item_values: Vec<(usize, f32)> = (0..difficulty.num_items)
|
||||
@ -109,15 +128,16 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
(i, ratio)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
// 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 total_value = 0;
|
||||
let mut is_selected = vec![false; difficulty.num_items];
|
||||
|
||||
for &(item, _) in &item_values {
|
||||
if total_weight + weights[item] <= max_weight {
|
||||
@ -128,13 +148,14 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
total_value += interaction_values[item][prev_item];
|
||||
}
|
||||
selected_items.push(item);
|
||||
is_selected[item] = true;
|
||||
} else {
|
||||
unselected_items.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Precompute sum of interaction values with each selected item for all items
|
||||
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;
|
||||
@ -143,10 +164,17 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
}
|
||||
}
|
||||
|
||||
let mut min_selected_item_values = i32::MAX;
|
||||
for x in 0..difficulty.num_items {
|
||||
if is_selected[x] {
|
||||
min_selected_item_values = min_selected_item_values.min(interaction_sum_list[x]);
|
||||
}
|
||||
}
|
||||
|
||||
// Optimized local search with tabu list
|
||||
let max_iterations = 100;
|
||||
let mut tabu_list = vec![0; difficulty.num_items];
|
||||
|
||||
|
||||
for _ in 0..max_iterations {
|
||||
let mut best_improvement = 0;
|
||||
let mut best_swap = None;
|
||||
@ -158,30 +186,27 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
}
|
||||
|
||||
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 {
|
||||
if new_item_values_sum < best_improvement + min_selected_item_values {
|
||||
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);
|
||||
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
|
||||
{
|
||||
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];
|
||||
|
||||
@ -201,12 +226,19 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
selected_items.push(new_item);
|
||||
unselected_items.push(remove_item);
|
||||
|
||||
is_selected[new_item] = true;
|
||||
is_selected[remove_item] = false;
|
||||
|
||||
total_value += best_improvement;
|
||||
total_weight = total_weight + weights[new_item] - weights[remove_item];
|
||||
|
||||
// Update sum of interaction values after swapping items
|
||||
min_selected_item_values = i32::MAX;
|
||||
for x in 0..difficulty.num_items {
|
||||
interaction_sum_list[x] += interaction_values[x][new_item] - interaction_values[x][remove_item];
|
||||
if is_selected[x] {
|
||||
min_selected_item_values = min_selected_item_values.min(interaction_sum_list[x]);
|
||||
}
|
||||
}
|
||||
|
||||
// Update tabu list
|
||||
@ -215,17 +247,17 @@ impl crate::ChallengeTrait<Solution, Difficulty, 2> for Challenge {
|
||||
} 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;
|
||||
|
||||
|
||||
Ok(Challenge {
|
||||
seed,
|
||||
difficulty: difficulty.clone(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user