#include "Random.hh" #include "kofn.hh" // O(n) implementation. static void _kOfN_largeK (int k, int n, int* values) { assert (k > 0); assert (k <= n); int j = 0; for (int i = 0; i < n; i++) { double prob = (double) (k - j) / (n - i); assert (prob <= 1); double x = Random::rand.fp (); if (x < prob) { values[j++] = i; } } assert (j == k); } // O(k*lg(k)) implementation; constant factor is about 2x the constant // factor for the O(n) implementation. static void _kOfN_smallK (int k, int n, int* values) { assert (k > 0); assert (k <= n); if (k == 1) { values[0] = Random::rand.i32 (0, n - 1); return; } int leftN = n / 2; int rightN = n - leftN; int leftK = 0; int rightK = 0; for (int i = 0; i < k; i++) { int x = Random::rand.i32 (0, n - i - 1); if (x < leftN - leftK) { leftK++; } else { rightK++; } } if (leftK > 0) { _kOfN_smallK (leftK, leftN, values); } if (rightK > 0) { _kOfN_smallK (rightK, rightN, values + leftK); } for (int i = leftK; i < k; i++) { values[i] += leftN; } } // Return k randomly selected integers from the interval [0,n), in // increasing sorted order. void kOfN (int k, int n, int* values) { assert (k >= 0); assert (n >= 0); if (k == 0) { return; } static double log2 = log (2); double klogk = k * log (k) / log2; if (klogk < n / 2) { _kOfN_smallK (k, n, values); } else { _kOfN_largeK (k, n, values); } }