InterviewPrepKit

Home / Coding / Machine Learning Coding / Clustering & Neighbors / K-Means++ Initialization

K-Means++ Initialization

hard 00:00
Solving tips
  • Track one running vector of each point's squared distance to its nearest chosen centroid; update it with an element-wise minimum after every pick instead of recomputing against all centroids.
  • The sampling weight is the squared distance, not the distance. Normalize those weights into a probability vector and draw one index from it.
  • Draw every random choice from a single seeded rng (default_rng(seed)) so the first uniform pick and every weighted pick are reproducible.

Random initialization is the weakest part of Lloyd’s k-means: seed two centroids inside the same blob and the algorithm can converge to a bad local optimum. K-means++ fixes this by spreading the initial centroids out. It picks the first center at random, then biases every later pick toward points far from the centers already chosen. The interview task is to turn that into vectorized NumPy plus a correct weighted sample.

Definition

Let D(x) be the Euclidean distance from a point x to the nearest centroid chosen so far. The seeding procedure is:

1. Choose the first centroid c_1 uniformly at random from the rows of X.
2. For each remaining centroid:
     - compute D(x)^2 for every point x (distance to its nearest chosen centroid)
     - choose the next centroid to be point x with probability D(x)^2 / sum_x D(x)^2
3. Stop once k centroids have been chosen.

The weighting by D(x)^2 (squared, not raw distance) is the defining detail: points that are already close to a chosen center get near-zero weight, while far-away points dominate the draw.

Task

Complete kmeans_plus_plus_init(X, k, seed) so it returns the (k, d) array of chosen centroids in pick order. Create one generator with np.random.default_rng(seed) and use it for both the first uniform pick and every subsequent weighted pick. Maintain a running vector of each point’s squared distance to its nearest chosen centroid, updating it with an element-wise minimum after each new centroid rather than recomputing against all centroids. Do not call sklearn, scipy, or any built-in k-means or sampling helper beyond the NumPy generator.

Example

X = np.array([[0.0,  0.0],
              [0.0,  1.0],
              [1.0,  0.0],
              [10.0, 10.0],
              [10.0, 11.0],
              [11.0, 10.0]])
kmeans_plus_plus_init(X, k=2, seed=0)
# -> array([[11., 10.],
#           [ 0.,  0.]])

With seed=0 the first uniform draw lands on row 5, [11, 10], in the top-right blob. Every point in that blob then has a tiny squared distance and near-zero weight, so the second pick is drawn almost entirely from the far blob and lands on [0, 0] — the two seeds sit in different clusters, which is the whole point of k-means++.

Constraints

  • 1 <= k <= n, X has shape (n, d) with 1 <= n <= 10^4, 1 <= d <= 10^3.
  • Use the passed-in seed through a single np.random.default_rng(seed); the same seed must always return the same centroids.
  • Each returned centroid must be an actual row of X, and the result is returned in the order the centroids were picked.
  • Weight the draw by squared distance, and keep the running nearest-distance vector via an element-wise minimum (do not recompute distances to already-chosen centroids).

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug