use clap::Parser;
use std::path::PathBuf;
use std::time::Instant;
use toolbox_rs::{complete_graph::CompleteGraph, tsplib};
#[derive(Parser, Debug)]
#[clap(name = "tsp_solver", about = "A simple TSP solver")]
struct Args {
#[clap(short, long, value_parser)]
input: PathBuf,
#[clap(short, long)]
nearest_neighbor: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let args = Args::parse();
println!(r#" ___ _ "#);
println!(r#" / __| ___ | | __ __ ___ _ _ "#);
println!(r#" \__ \ / _ \ | | \ V / / -_) | '_| "#);
println!(r#" |___/ \___/ _|_|_ _\_/_ \___| _|_|_ "#);
println!(r#" _|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""| "#);
println!(r#" "`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-' "#);
println!("Reading TSP file: {}", args.input.display());
let start = Instant::now();
let sites =
tsplib::read_tsp_file(args.input.to_str().unwrap()).expect("graph could not be read");
println!("Read {} sites in {:?}", sites.len(), start.elapsed());
let start = Instant::now();
println!("Building distance matrix...");
let num_nodes = sites.len();
let mut graph = CompleteGraph::new(num_nodes);
for i in 0..num_nodes {
for j in 0..num_nodes {
if i == j {
continue; }
let distance = tsplib::euclidean_distance(&sites[i], &sites[j]);
*graph.get_mut(i, j) = distance;
}
}
println!("Built distance matrix in {:?}", start.elapsed());
if args.nearest_neighbor {
let start = Instant::now();
let (_tour, length) = solve_nearest_neighbor(&graph);
println!(
"Nearest neighbor tour length: {} (computed in {:?})",
length,
start.elapsed()
);
println!("Tour length: {:?}", _tour.len());
} else {
println!(
"No solving method specified. Use --nearest-neighbor to solve using nearest neighbor heuristic."
);
}
Ok(())
}
fn solve_nearest_neighbor(graph: &CompleteGraph<i32>) -> (Vec<usize>, i32) {
let n = graph.num_nodes();
if n <= 1 {
return (vec![0], 0);
}
let mut tour = Vec::with_capacity(n);
let mut visited = vec![false; n];
let mut current = 0; let mut tour_length = 0;
tour.push(current);
visited[current] = true;
for _ in 1..n {
let mut next = usize::MAX;
let mut min_distance = i32::MAX;
for j in 0..n {
if !visited[j] && current != j && graph[(current, j)] < min_distance {
next = j;
min_distance = graph[(current, j)];
}
}
if next == usize::MAX {
break;
}
tour.push(next);
visited[next] = true;
tour_length += min_distance;
current = next;
}
tour_length += graph[(current, 0)];
(tour, tour_length)
}