docs: public static ModifiableDBIDs randomSample(DBIDs source, int k, Random random) {
      if(k < 0 || k > source.size()) {
        throw new IllegalArgumentException("Illegal value for size of random sample: " + k + " > " + source.size() + " or < 0");
      }
      // Fast, and we're single-threaded here anyway.
      random = (random != null) ? random : new FastNonThreadsafeRandom();
  
      // TODO: better balancing for different sizes
      // Two methods: constructive vs. destructive
      if(k < source.size() >> 2) {
        ArrayDBIDs aids = DBIDUtil.ensureArray(source);
        DBIDArrayIter iter = aids.iter();
        final int size = aids.size();
        HashSetModifiableDBIDs sample = DBIDUtil.newHashSet(k);
        while(sample.size() < k) {
          sample.add(iter.seek(random.nextInt(size)));
        }
        return sample;
      }
      else {
        ArrayModifiableDBIDs sample = DBIDUtil.newArray(source);
        randomShuffle(sample, random, k);
        // Delete trailing elements
        for(int i = sample.size() - 1; i >= k; i--) {
          sample.remove(i);
        }
        return sample;
      }
    }