tsp_rs/lib.rs
1//! Crate of algorithms for solving the traveling salesman problem.
2//!
3//! # Example
4//! ```
5//! use std::time;
6//!
7//! use tsp_rs::Tour;
8//! use tsp_rs::point::Point;
9//!
10//! let tour: Vec<Point> = vec![
11//! Point::new(0., 0.),
12//! Point::new(0., 1.),
13//! Point::new(1., 0.),
14//! Point::new(1., 1.),
15//! ];
16//!
17//! let mut tour = Tour::from(&tour);
18//!
19//! tour.optimize_kopt(std::time::Duration::from_secs(1));
20//! ```
21//!
22//! _Disclaimer:_
23//!
24//! This is not a work of art, nor is it perfect (or even good?) Rust.
25//! This was written alongside my first reading of the Rust book (https://doc.rust-lang.org/book/)
26//! while trying to learn the language.
27mod kopt;
28mod nn;
29pub mod point;
30
31use std::borrow::Borrow;
32use std::collections::HashSet;
33use std::time;
34
35use rand::Rng;
36
37/// Trait used by all algorithms to calculate the cost of moving along an edge
38///
39/// # Examples
40/// An example implementation is found on `tsp::point::Point`, that implements
41/// standard euclidean distance as its metric.
42pub trait Metrizable {
43 fn cost(&self, other: &Self) -> f64;
44}
45
46/// Represents a solution to the tsp for the items T
47#[derive(Debug, Clone, PartialEq)]
48pub struct Tour<T: Metrizable> {
49 pub path: Vec<T>,
50}
51
52impl<T: Metrizable + Clone + Borrow<T>> Tour<T> {
53 /// Returns a new, empty Tour<T>
54 ///
55 /// # Example
56 /// ```
57 /// use tsp_rs::Tour;
58 /// use tsp_rs::point::Point;
59 ///
60 /// let tour: Tour<Point> = Tour::new();
61 /// ```
62 pub fn new() -> Tour<T> {
63 Tour {
64 path: Vec::new() as Vec<T>,
65 }
66 }
67
68 /// Returns a tour from `nodes: Vec<T>` passed in where the tour
69 /// is nodes[0] -> nodes[1] -> ... nodes[nodes.len() - 1] -> nodes[0]
70 ///
71 /// # Example
72 /// ```
73 /// use tsp_rs::Tour;
74 /// use tsp_rs::point::Point;
75 ///
76 /// let nodes = vec![
77 /// Point::new(0., 0.),
78 /// Point::new(1., 0.),
79 /// Point::new(1., 1.),
80 /// Point::new(0., 1.),
81 /// ];
82 ///
83 /// let tour = Tour::from(&nodes);
84 /// ```
85 pub fn from(nodes: &Vec<T>) -> Tour<T>
86 where
87 T: Clone,
88 {
89 Tour {
90 path: (*nodes).clone(),
91 }
92 }
93
94 /// Returns the length of a tour.
95 ///
96 /// # Example
97 /// let tour = Tour::from(&some_points);
98 /// let total_cost = tour.tour_len();
99 pub fn tour_len(&self) -> f64 {
100 if self.path.len() <= 0 {
101 return 0.;
102 }
103
104 let mut sum = 0.;
105 let mut prev = self.path.last().unwrap();
106 for curr in &self.path {
107 sum += prev.cost(&curr);
108 prev = &curr;
109 }
110 sum
111 }
112
113 /// Improves the tour in place using the 2opt heuristic (with 3opt kicks if it gets stuck)
114 ///
115 /// # Examples
116 ///
117 /// ```
118 /// use std::time;
119 ///
120 /// use tsp_rs::Tour;
121 /// use tsp_rs::point::Point;
122 ///
123 /// let nodes = vec![
124 /// Point::new(0., 0.),
125 /// Point::new(1., 0.),
126 /// Point::new(1., 1.),
127 /// Point::new(0., 1.),
128 /// ];
129 ///
130 /// let mut tour = Tour::from(&nodes);
131 ///
132 /// tour.optimize_kopt(time::Duration::from_secs(1));
133 /// ```
134 pub fn optimize_kopt(&mut self, timeout: time::Duration) {
135 self.optimize_nn();
136 let start_time = time::Instant::now();
137 let max_iter_withouth_impr = self.path.len() ^ 2;
138 let mut iter_without_impr = 0;
139 let mut best_tour_length = std::f64::MAX;
140 let mut best_tour: Vec<T> = Vec::new();
141 loop {
142 match kopt::k_opt(2, self) {
143 Some(_) => {
144 iter_without_impr = 0;
145 }
146 None => {
147 iter_without_impr += 1;
148 if iter_without_impr > max_iter_withouth_impr {
149 let current_tour_length = self.tour_len();
150 if current_tour_length < best_tour_length {
151 best_tour = self.path.clone();
152 best_tour_length = current_tour_length;
153 }
154 kopt::k_opt(4, self); // kick
155 iter_without_impr = 0;
156 }
157 }
158 }
159 if start_time.elapsed() > timeout {
160 break;
161 }
162 }
163 let current_tour_length = self.tour_len();
164 if current_tour_length < best_tour_length {
165 best_tour = self.path.clone();
166 }
167 self.path = best_tour;
168 }
169
170 /// Constructs a tour inplace using the nearest neighbor heuristic
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use tsp_rs::Tour;
176 /// use tsp_rs::point::Point;
177 ///
178 /// let nodes = vec![
179 /// Point::new(0., 0.),
180 /// Point::new(1., 0.),
181 /// Point::new(1., 1.),
182 /// Point::new(0., 1.),
183 /// ];
184 ///
185 /// let mut tour = Tour::from(&nodes);
186 ///
187 /// tour.optimize_nn();
188 /// ```
189 pub fn optimize_nn(&mut self)
190 where
191 T: Metrizable + Clone,
192 {
193 let mut path = Vec::new();
194 let nodes = index_path(&self.path);
195 let mut visited = HashSet::new();
196
197 let start_index: usize = rand::thread_rng().gen_range(0, nodes.len());
198 let mut curr = &nodes[start_index].value.clone();
199 path.push(curr.clone());
200 visited.insert(nodes[start_index].index);
201
202 loop {
203 match nn::nearest_neighbor(curr, &nodes, &mut visited) {
204 Some(next) => {
205 path.push(next.clone());
206 curr = &next;
207 }
208 None => {
209 if visited.len() == nodes.len() {
210 break;
211 }
212 }
213 };
214 }
215
216 self.path = path;
217 }
218}
219
220#[derive(Clone)]
221pub(crate) struct IndexedT<T> {
222 pub index: usize,
223 pub value: T,
224}
225
226#[inline]
227pub(crate) fn index_path<T>(path: &Vec<T>) -> Vec<IndexedT<&T>> {
228 path.iter()
229 .enumerate()
230 .map(|(index, value)| IndexedT { index, value })
231 .collect()
232}