src/kmeans.cpp
// Finds the nearest centroid to the given object o.  We have the current
// best assignment for o.  Call it c.  (If this is the first iteration,
// then choose a cluster c arbitrarily.)  We will start by only considering
// the nearest neighbors of c.  Let orig_dist be D(o, c).  Maintain the
// closest cluster found which might be the original cluster or might be
// one of the neighbors.  If we ever get to a neighboring cluster c' such
// that D(o, c') >= 2 * orig_dist then we can quit (triangle inequality).
// If we get to the end of the neighbors list then there are two possibilities
// 1) we found a new best candidate; or 2) we did not.  In case (2) we have
// no choice but to do an exhaustive search through all clusters which we do
// with ExhaustiveNearest().  Hopefully this doesn't happen too often.  In
// case (1) we can instead repeat the process we just followed, this time
// using the neighbors list of the new best candidate.
Finds the nearest centroid to the given object o.  We have the current
best assignment for o.  Call it c.  (If this is the first iteration,
then choose a cluster c arbitrarily.)  We will start by only considering
the nearest neighbors of c.  Let orig_dist be D(o, c).  Maintain the
closest cluster found which might be the original cluster or might be
one of the neighbors.  If we ever get to a neighboring cluster c' such
that D(o, c') >= 2 * orig_dist then we can quit (triangle inequality).
If we get to the end of the neighbors list then there are two possibilities
1) we found a new best candidate; or 2) we did not.  In case (2) we have
no choice but to do an exhaustive search through all clusters which we do
with ExhaustiveNearest().  Hopefully this doesn't happen too often.  In
case (1) we can instead repeat the process we just followed, this time
using the neighbors list of the new best candidate.
Finds the nearest centroid to the given object o . We have the current best assignment for o . Call it c . ( If this is the first iteration then choose a cluster c arbitrarily . ) We will start by only considering the nearest neighbors of c . Let orig_dist be D ( o c ) . Maintain the closest cluster found which might be the original cluster or might be one of the neighbors . If we ever get to a neighboring cluster c ' such that D ( o c ' ) > = 2 * orig_dist then we can quit ( triangle inequality ) . If we get to the end of the neighbors list then there are two possibilities 1 ) we found a new best candidate ; or 2 ) we did not . In case ( 2 ) we have no choice but to do an exhaustive search through all clusters which we do with ExhaustiveNearest () . Hopefully this doesn ' t happen too often . In case ( 1 ) we can instead repeat the process we just followed this time using the neighbors list of the new best candidate .
int KMeansThread::Nearest(int o, float *obj, double *ret_min_dist) {
  int orig_best_c = assignments_[o];
  if (orig_best_c == -1) {
    int c;
    for (c = 0; c < num_clusters_; ++c) {
      if (cluster_sizes_[c] > 0) {
	orig_best_c = c;
	break;
      }
    }
    if (c == num_clusters_) {
      fprintf(stderr, "No clusters with non-zero size?!?\n");
      exit(-1);
    }
  }
  float orig_dist = 0;
  for (int d = 0; d < dim_; ++d) {
    float ov = obj[d];
    float cm = means_[orig_best_c][d];
    float dim_delta = ov - cm;
    orig_dist += dim_delta * dim_delta;
  }
  orig_dist = sqrt(orig_dist);
  ++dist_count_;
  if (neighbor_vectors_ == NULL) {
    return ExhaustiveNearest(o, obj, orig_best_c, orig_dist, ret_min_dist);
  }
  float min_dist = orig_dist;
  int best_c = orig_best_c;
  while (true) {
    const vector< pair<float, int> > &v = neighbor_vectors_[best_c];
    int num = v.size();
    for (int i = 0; i < num; ++i) {
      float intra_dist = v[i].first;
      int c = v[i].second;
      if (cluster_sizes_[c] == 0) continue;
      if (intra_dist >= 2 * orig_dist) {
	++abbreviated_count_;
	*ret_min_dist = min_dist;
	return best_c;
      }
      float *cluster_means = means_[c];
      float dist_sq = 0;
      for (int d = 0; d < dim_; ++d) {
	float ov = obj[d];
	float cm = cluster_means[d];
	float dim_delta = ov - cm;
	dist_sq += dim_delta * dim_delta;
      }
      float dist = sqrt(dist_sq);
      ++dist_count_;
      if (dist < min_dist) {
	best_c = c;
	min_dist = dist;
      } else if (dist == min_dist) {
	if (c < best_c) best_c = c;
      }
    }
    if (best_c == orig_best_c) {
      ++exhaustive_count_;
      return ExhaustiveNearest(o, obj, best_c, min_dist, ret_min_dist);
    } else {
      orig_best_c = best_c;
      orig_dist = min_dist;
      continue;
    }
  }
  fprintf(stderr, "Should never get here\n");
  exit(-1);
}
int KMeansThread :: Nearest ( int o , float * obj , double * ret_min_dist ) { int orig_best_c = assignments_ [ o ] ; if ( orig_best_c == -1 ) { int c ; for ( c = 0 ; c < num_clusters_ ; ++ c ) { if ( cluster_sizes_ [ c ] > 0 ) { orig_best_c = c ; break ; } } if ( c == num_clusters_ ) { fprintf ( stderr , " \n " ) ; exit ( -1 ) ; } } float orig_dist = 0 ; for ( int d = 0 ; d < dim_ ; ++ d ) { float ov = obj [ d ] ; float cm = means_ [ orig_best_c ] [ d ] ; float dim_delta = ov - cm ; orig_dist += dim_delta * dim_delta ; } orig_dist = sqrt ( orig_dist ) ; ++ dist_count_ ; if ( neighbor_vectors_ == NULL ) { return ExhaustiveNearest ( o , obj , orig_best_c , orig_dist , ret_min_dist ) ; } float min_dist = orig_dist ; int best_c = orig_best_c ; while ( true ) { const vector < pair < float , int > > & v = neighbor_vectors_ [ best_c ] ; int num = v . size ( ) ; for ( int i = 0 ; i < num ; ++ i ) { float intra_dist = v [ i ] . first ; int c = v [ i ] . second ; if ( cluster_sizes_ [ c ] == 0 ) continue ; if ( intra_dist >= 2 * orig_dist ) { ++ abbreviated_count_ ; * ret_min_dist = min_dist ; return best_c ; } float * cluster_means = means_ [ c ] ; float dist_sq = 0 ; for ( int d = 0 ; d < dim_ ; ++ d ) { float ov = obj [ d ] ; float cm = cluster_means [ d ] ; float dim_delta = ov - cm ; dist_sq += dim_delta * dim_delta ; } float dist = sqrt ( dist_sq ) ; ++ dist_count_ ; if ( dist < min_dist ) { best_c = c ; min_dist = dist ; } else if ( dist == min_dist ) { if ( c < best_c ) best_c = c ; } } if ( best_c == orig_best_c ) { ++ exhaustive_count_ ; return ExhaustiveNearest ( o , obj , best_c , min_dist , ret_min_dist ) ; } else { orig_best_c = best_c ; orig_dist = min_dist ; continue ; } } fprintf ( stderr , " \n " ) ; exit ( -1 ) ; }
Finds the nearest centroid to the given object o.
Finds the nearest centroid to the given object o .
// Initialize orig_best_c to the current assignment.  This will make the // triangle inequality based optimization work better. // Arbitrarily set orig_best_c to first cluster with non-zero size // For testing purposes, if we can just call ExhaustiveNearest() here if // we suspect a bug in the optimized code below. // Has to be orig_dist, not min_dist. // We sorted neighbors by distance so we can skip all other clusters // In case of tie, choose the lower numbered cluster // We got to the end of the neighbors list and we a) could not prove // that we had the best cluster, and b) didn't find a better candidate // best cluster.  All we can do now is an exhaustive search of all // the clusters. // We got to the end of the neighbors list and we could not prove // that we had the best cluster, *but* we did find a better candidate // cluster.  So we can now try to search the neighbors of this new // better candidate.