use std::{cmp::Reverse, collections::BinaryHeap};
use crate::solvers::Solve;
use crate::solvers::SolverConfig;
use crate::DistanceMatrix;
use crate::{error::PhomoError, solvers::error::SolverError};
#[derive(Debug, Default)]
pub struct Greedy {
config: SolverConfig,
}
impl Greedy {
pub fn new(config: SolverConfig) -> Self {
Self { config }
}
}
impl Solve for Greedy {
fn solve(&mut self, distance_matrix: &DistanceMatrix) -> Result<Vec<usize>, PhomoError> {
if distance_matrix.columns < distance_matrix.rows * self.config.max_tile_occurrences {
return Err(SolverError::TooFewColumns {
rows: distance_matrix.rows,
columns: distance_matrix.columns,
}
.into());
}
let n_cells = distance_matrix.rows;
let n_tiles = distance_matrix.columns;
let mut n_appearances = vec![0; n_tiles];
let mut heap = BinaryHeap::with_capacity(n_cells);
for row_idx in 0..n_cells {
let mut best_tile = 0;
let mut best_distance = distance_matrix.get(row_idx, best_tile);
for col_idx in 1..n_tiles {
let distance = distance_matrix.get(row_idx, col_idx);
if distance < best_distance {
best_distance = distance;
best_tile = col_idx;
}
}
heap.push(Reverse((best_distance, row_idx, best_tile)));
}
let mut assignments = vec![0; n_cells];
let mut filled_count = 0;
while let Some(Reverse((_, cell_idx, tile_idx))) = heap.pop() {
if filled_count == n_cells {
break;
}
if n_appearances[tile_idx] < self.config.max_tile_occurrences {
assignments[cell_idx] = tile_idx;
n_appearances[tile_idx] += 1;
filled_count += 1;
} else {
let mut next_best_tile = 0;
let mut next_best_distance = i64::MAX;
for (tile_idx, n) in n_appearances.iter().enumerate() {
if *n < self.config.max_tile_occurrences {
let distance = distance_matrix.get(cell_idx, tile_idx);
if distance < next_best_distance {
next_best_distance = distance;
next_best_tile = tile_idx;
}
}
}
heap.push(Reverse((next_best_distance, cell_idx, next_best_tile)));
}
}
Ok(assignments)
}
}