/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.components.cluster.kmeans;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import com.opendoorlogistics.api.components.ComponentExecutionApi;
final public class KMeansAlgorithm {
public interface CreateMean <T extends KMeanPoint>{
Mean<T> createMean(T copyThis);
}
public <T extends KMeanPoint> List<Mean<T>> execute(int k, int randomseed,CreateMean<T>createMean, List<T> points, ComponentExecutionApi reporter){
if(k > points.size()){
k = points.size();
}
ArrayList<Mean<T>> means = new ArrayList<>(k);
if(k==0){
return means;
}
// random choose k centres
Random random = new Random(randomseed);
ArrayList<T> tmp = new ArrayList<>(points);
Collections.shuffle(tmp, random);
for(int i=0 ;i < k ; i++){
means.add(createMean.createMean(tmp.get(i)));
}
// reset all cluster numbers
for(T point : points){
point.clusterNumber = -1;
}
int nbChanges;
int stepNb=0;
do{
// clear all assignments from the means
for(Mean<T> mean : means){
mean.clearAssigned();
}
// assign everything
nbChanges=0;
for(T pnt : points){
int closest=-1;
double closestDist = Double.MAX_VALUE;
for(int i=0 ;i < k ; i++){
Mean<T> mean = means.get(i);
double dist = mean.getMean().distance(pnt);
if(dist < closestDist){
closestDist = dist;
closest = i;
}
}
// count changes
if(pnt.clusterNumber != closest){
nbChanges++;
}
// do assignment
pnt.clusterNumber = closest;
means.get(closest).addAssigned(pnt);
}
// update means
for(Mean<T> mean : means){
if(mean.size()>0){
mean.updateMean();
}
}
reporter.postStatusMessage("K means step " + (stepNb+1));
stepNb++;
}while(nbChanges>0 && reporter.isCancelled()==false && reporter.isFinishNow()==false);
return means;
}
}