From d8b8ece8a78c3e24207f4e6f007dbe0be9f3734a Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Thu, 22 Jan 2026 04:42:06 +0300 Subject: [PATCH] Annealing simulation base impl --- lab1/Cargo.toml | 1 + lab1/src/algo/board.rs | 4 ++ lab1/src/algo/misc.rs | 65 +++++++++++++++++++++++++++++++ lab1/src/algo/mod.rs | 6 ++- lab1/src/algo/simulation.rs | 77 +++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 lab1/src/algo/misc.rs create mode 100644 lab1/src/algo/simulation.rs diff --git a/lab1/Cargo.toml b/lab1/Cargo.toml index 782b9b3..377dc39 100644 --- a/lab1/Cargo.toml +++ b/lab1/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" workspace = true [dependencies] +rand = "0.9.2" eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow"] } [profile.dev.package.eframe] diff --git a/lab1/src/algo/board.rs b/lab1/src/algo/board.rs index 7914e21..39c2bdb 100644 --- a/lab1/src/algo/board.rs +++ b/lab1/src/algo/board.rs @@ -10,6 +10,10 @@ impl Board { data: vec![0usize; size].into_boxed_slice(), }; } + + pub fn len(&self) -> usize { + return self.data.len(); + } } impl Index for Board { diff --git a/lab1/src/algo/misc.rs b/lab1/src/algo/misc.rs new file mode 100644 index 0000000..a5b7507 --- /dev/null +++ b/lab1/src/algo/misc.rs @@ -0,0 +1,65 @@ +use super::Board; +use rand::random_range; +use std::ops::Range; + +pub(super) fn initializeBoard(b: &mut Board) { + for i in 0..b.len() { + b[i] = i; + } +} + +fn random_range_with_hole(range: Range, hole: usize) -> usize { + assert!( + range.contains(&hole), + "Hole not in range: {hole} !in {}..{}", + range.start, + range.end + ); + let shrunk = random_range(range.start..(range.end - 1)); + if shrunk >= hole { + return shrunk + 1; + } else { + return shrunk; + } +} + +pub(super) fn swapRandomQueensOnce(b: &mut Board) { + let i1 = random_range(0..b.len()); + let i2 = random_range_with_hole(0..b.len(), i1); + + let tmp = b[i1]; + b[i1] = b[i2]; + b[i2] = tmp; +} + +pub(super) fn isOnDiagonal(c1: (usize, usize), c2: (usize, usize)) -> bool { + let dist = c1.0.abs_diff(c2.0); + + let diagonalPos; + if c1.1 > c2.1 { + diagonalPos = c1.1.checked_sub(dist) + } else { + diagonalPos = c1.1.checked_add(dist) + } + + match diagonalPos { + None => { + // if coordinate over(under)flowed, it can be eq to another valid coordinate + return false; + } + Some(i2e) => return c2.1 == i2e, + } +} + +#[test] +fn isOnDiagonal_test1() { + assert!(isOnDiagonal((0, 0), (1, 1))) +} +#[test] +fn isOnDiagonal_test2() { + assert!(isOnDiagonal((1, 0), (0, 1))) +} +#[test] +fn isOnDiagonal_test3() { + assert!(!isOnDiagonal((0, 0), (0, 1))) +} \ No newline at end of file diff --git a/lab1/src/algo/mod.rs b/lab1/src/algo/mod.rs index 1a9bdc2..2b657db 100644 --- a/lab1/src/algo/mod.rs +++ b/lab1/src/algo/mod.rs @@ -1 +1,5 @@ -mod board; \ No newline at end of file +mod board; +mod misc; +mod simulation; + +pub(crate) use board::Board; \ No newline at end of file diff --git a/lab1/src/algo/simulation.rs b/lab1/src/algo/simulation.rs new file mode 100644 index 0000000..90a3867 --- /dev/null +++ b/lab1/src/algo/simulation.rs @@ -0,0 +1,77 @@ +use eframe::emath::Numeric; +use rand::{random, random_range}; +use crate::algo::Board; +use crate::algo::misc::{initializeBoard, isOnDiagonal, swapRandomQueensOnce}; + +pub(super) fn calculateEnergy(b: &Board) -> f64 { + let mut conflictsCount = 0usize; + for i1 in 0..b.len() { + for i2 in (i1 + 1)..b.len() { + if isOnDiagonal((i1, b[i1]), (i2, b[i2])) { + conflictsCount += 1; + } + } + } + + return conflictsCount.to_f64() +} + +#[derive(Clone)] +struct State { + board: Board, + energy: f64 +} + +pub struct AnnealingConfig { + pub initialTemperature: f64, + pub targetTemperature: f64, + pub cooldownCoefficient: f64, + pub iterationsPerAge: usize +} + +pub(crate) fn simulateAnnealing(len: usize, config: &AnnealingConfig) -> Board { + assert!(config.targetTemperature < config.initialTemperature); + assert!((0f64..1f64).contains(&config.cooldownCoefficient)); + + let mut currentState = State { + board: Board::alloc(len), + energy: f64::INFINITY + }; + initializeBoard(&mut currentState.board); + currentState.energy = calculateEnergy(¤tState.board); + + let mut bestState = currentState.clone(); + + let mut currentTemperature = config.initialTemperature; + + while currentTemperature > config.targetTemperature { + + for _ in 0..config.iterationsPerAge { + let mut newState = currentState.clone(); + swapRandomQueensOnce(&mut newState.board); + newState.energy = calculateEnergy(&newState.board); + + if newState.energy <= currentState.energy { + currentState = newState + } else { + let test = random::(); + let delta = newState.energy - currentState.energy; + let calc = (-delta/test).exp(); + + if calc > test { + currentState = newState + } else { + continue; + } + } + + if currentState.energy < bestState.energy { + bestState = currentState.clone(); + } + } + + currentTemperature *= config.cooldownCoefficient; + } + + return bestState.board; +} \ No newline at end of file