k-medoids/initialization.mjs

// Initialization helpers, ported from src/initialization.rs (RNG approximated for JS)

/** Use the first k objects (0..k-1) as initial medoids. */
export function first_k(k) {
  return Array.from({ length: k }, (_, i) => i);
}

/** Sample k DISTINCT indices from [0, n) using Floyd's algorithm. rng: () => float in [0,1). */
export function sample(rng, n, k) {
  if (k > n) throw new Error('`amount` of samples must be less than or equal to `length`');
  const chosen = new Set();
  const res = [];
  for (let j = n - k; j < n; j++) {
    const t = Math.floor(rng() * (j + 1)); // 0..j inclusive
    const v = chosen.has(t) ? j : t;
    chosen.add(v);
    res.push(v);
  }
  return res;
}

/** Random initialization: k distinct medoid indices in [0, n). */
export function random_initialization(n, k, rng = Math.random) {
  return sample(rng, n, k);
}

/** Fisher-Yates shuffle of [0, n). Used by rand_fasterpam / par_fasterpam. */
export function shuffle(rng, n) {
  const a = Array.from({ length: n }, (_, i) => i);
  for (let i = n - 1; i > 0; i--) {
    const j = Math.floor(rng() * (i + 1));
    const tmp = a[i]; a[i] = a[j]; a[j] = tmp;
  }
  return a;
}