use std::vec;
#[derive(Debug)]
pub(crate) struct KMeansResult {
pub(crate) means: Vec<Vec<usize>>,
pub(crate) assignments: Vec<u8>,
}
pub(crate) struct KMeansProblem<'a> {
pub(crate) dimension: usize,
pub(crate) data: &'a [Vec<u8>],
pub(crate) num_iterations: usize,
pub(crate) num_clusters: usize,
}
impl<'a> KMeansProblem<'a> {
pub(crate) fn solve(self) -> KMeansResult {
let mut centers = vec![];
for i in 0..self.num_clusters {
centers.push(
self.data[i * self.data.len() / self.num_clusters]
.iter()
.map(|x| *x as f32)
.collect::<Vec<_>>(),
);
}
let mut cluster_assignments = vec![0; self.data.len()];
for _ in 0..self.num_iterations {
for (data_point_number, point) in self.data.iter().enumerate() {
let mut min_assignment = 0;
let mut min_distance: Option<_> = None;
for (cluster_number, center) in centers.iter().enumerate() {
let distance = euclidean_distance(center, point);
if let Some(min_distance_value) = min_distance {
if distance < min_distance_value {
min_distance = Some(distance);
min_assignment = cluster_number;
}
} else {
min_distance = Some(distance);
min_assignment = cluster_number;
}
}
cluster_assignments[data_point_number] = min_assignment as u8;
}
for i in centers.iter_mut() {
*i = vec![0.0; self.dimension];
}
let mut cluster_sizes = vec![0usize; self.num_clusters];
for (data_point, assigment) in self.data.iter().zip(cluster_assignments.iter()) {
let current_cluster_size = cluster_sizes[*assigment as usize];
for (entry, point) in centers[*assigment as usize]
.iter_mut()
.zip(data_point.iter())
{
*entry = ((*entry as f32) * current_cluster_size as f32 + (*point as f32))
/ (current_cluster_size as f32 + 1.0);
}
cluster_sizes[*assigment as usize] += 1;
}
}
KMeansResult {
assignments: cluster_assignments,
means: centers
.iter()
.map(|x| {
x.iter()
.map(|x| (*x * 10_000.0) as usize)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>(),
}
}
}
fn euclidean_distance(v1: &[f32], v2: &[u8]) -> f32 {
((v1.iter()
.zip(v2.iter())
.map(|(x, y)| (*x - *y as f32).powi(2)))
.sum::<f32>())
.sqrt()
}