Submitted knapsack/quadkp_improved

This commit is contained in:
FiveMovesAhead 2025-10-17 14:00:51 +01:00
parent a2fbd8c435
commit c2aa0d5945
3 changed files with 209 additions and 1 deletions

View File

@ -104,7 +104,8 @@
// c003_a053
// c003_a054
pub mod quadkp_improved;
pub use quadkp_improved as c003_a054;
// c003_a055

View File

@ -0,0 +1,23 @@
# TIG Code Submission
## Submission Details
* **Challenge Name:** knapsack
* **Submission Name:** quadkp_improved
* **Copyright:** 2024 Rootz
* **Identity of Submitter:** Rootz
* **Identity of Creator of Algorithmic Method:** null
* **Unique Algorithm Identifier (UAI):** null
## License
The files in this folder are under the following licenses:
* TIG Benchmarker Outbound License
* TIG Commercial License
* TIG Inbound Game License
* TIG Innovator Outbound Game License
* TIG Open Data License
* TIG THV Game License
Copies of the licenses can be obtained at:
https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses

View File

@ -0,0 +1,184 @@
use anyhow::{anyhow, Result};
use serde_json::{Map, Value};
use tig_challenges::knapsack::*;
pub fn solve_challenge(
challenge: &Challenge,
save_solution: &dyn Fn(&Solution) -> Result<()>,
hyperparameters: &Option<Map<String, Value>>,
) -> Result<()> {
Err(anyhow!("This algorithm is no longer compatible."))
}
// Old code that is no longer compatible
#[cfg(none)]
mod dead_code {
// TIG's UI uses the pattern `tig_challenges::<challenge_name>` to automatically detect your algorithm's challenge
use anyhow::Result;
use rand::{SeedableRng, Rng, rngs::StdRng};
use tig_challenges::knapsack::*;
pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result<Option<Solution>> {
let mut solution = Solution {
sub_solutions: Vec::new(),
};
for sub_instance in &challenge.sub_instances {
match solve_sub_instance(sub_instance)? {
Some(sub_solution) => solution.sub_solutions.push(sub_solution),
None => return Ok(None),
}
}
Ok(Some(solution))
}
pub fn solve_sub_instance(challenge: &SubInstance) -> Result<Option<SubSolution>> {
let vertex_count = challenge.weights.len();
let mut item_scores: Vec<(usize, f32)> = (0..vertex_count)
.map(|index| {
let interaction_sum: i32 = challenge.interaction_values[index].iter().sum();
let secondary_score = challenge.values[index] as f32 / challenge.weights[index] as f32;
let combined_score = (challenge.values[index] as f32 * 0.75 + interaction_sum as f32 * 0.15 + secondary_score * 0.1)
/ challenge.weights[index] as f32;
(index, combined_score)
})
.collect();
item_scores.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
let mut selected_items = Vec::with_capacity(vertex_count);
let mut unselected_items = Vec::with_capacity(vertex_count);
let mut current_weight = 0;
let mut current_value = 0;
for &(index, _) in &item_scores {
if current_weight + challenge.weights[index] <= challenge.max_weight {
current_weight += challenge.weights[index];
current_value += challenge.values[index] as i32;
for &selected in &selected_items {
current_value += challenge.interaction_values[index][selected];
}
selected_items.push(index);
} else {
unselected_items.push(index);
}
}
let mut mutation_rates = vec![0; vertex_count];
for index in 0..vertex_count {
mutation_rates[index] = challenge.values[index] as i32;
for &selected in &selected_items {
mutation_rates[index] += challenge.interaction_values[index][selected];
}
}
let max_generations = 150;
let mut cooling_schedule = vec![0; vertex_count];
let mut rng = StdRng::seed_from_u64(challenge.seed[0] as u64);
for generation in 0..max_generations {
let mut best_gain = 0;
let mut best_swap = None;
for (u_index, &mutant) in unselected_items.iter().enumerate() {
if cooling_schedule[mutant] > 0 {
continue;
}
let mutant_fitness = mutation_rates[mutant];
let extra_weight = challenge.weights[mutant] as i32 - (challenge.max_weight as i32 - current_weight as i32);
if mutant_fitness < 0 {
continue;
}
for (c_index, &selected) in selected_items.iter().enumerate() {
if cooling_schedule[selected] > 0 {
continue;
}
if extra_weight > 0 && (challenge.weights[selected] as i32) < extra_weight {
continue;
}
let interaction_penalty = (challenge.interaction_values[mutant][selected] as f32 * 0.3) as i32;
let fitness_gain = mutant_fitness - mutation_rates[selected] - interaction_penalty;
if fitness_gain > best_gain {
best_gain = fitness_gain;
best_swap = Some((u_index, c_index));
}
}
}
if let Some((u_index, c_index)) = best_swap {
let added_item = unselected_items[u_index];
let removed_item = selected_items[c_index];
selected_items.swap_remove(c_index);
unselected_items.swap_remove(u_index);
selected_items.push(added_item);
unselected_items.push(removed_item);
current_value += best_gain;
current_weight = current_weight + challenge.weights[added_item] - challenge.weights[removed_item];
if current_weight > challenge.max_weight {
continue;
}
for index in 0..vertex_count {
mutation_rates[index] += challenge.interaction_values[index][added_item]
- challenge.interaction_values[index][removed_item];
}
cooling_schedule[added_item] = 3;
cooling_schedule[removed_item] = 3;
}
if current_value as u32 >= challenge.baseline_value {
return Ok(Some(SubSolution { items: selected_items }));
}
for cooling_rate in cooling_schedule.iter_mut() {
*cooling_rate = if *cooling_rate > 0 { *cooling_rate - 1 } else { 0 };
}
if current_value as u32 > (challenge.baseline_value * 9 / 10) {
let high_potential_items: Vec<usize> = unselected_items
.iter()
.filter(|&&i| challenge.values[i] as i32 > (challenge.baseline_value as i32 / 4))
.copied()
.collect();
for &item in high_potential_items.iter().take(2) {
if current_weight + challenge.weights[item] <= challenge.max_weight {
selected_items.push(item);
unselected_items.retain(|&x| x != item);
current_weight += challenge.weights[item];
current_value += challenge.values[item] as i32;
for &selected in &selected_items {
if selected != item {
current_value += challenge.interaction_values[item][selected];
}
}
if current_value as u32 >= challenge.baseline_value {
return Ok(Some(SubSolution { items: selected_items }));
}
}
}
}
}
if current_value as u32 >= challenge.baseline_value && current_weight <= challenge.max_weight {
Ok(Some(SubSolution { items: selected_items }))
} else {
Ok(None)
}
}
}