SubsetSamplerUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.rng.sampling;

  18. import org.apache.commons.rng.UniformRandomProvider;

  19. /**
  20.  * Utility class for selecting a subset of a sequence of integers.
  21.  */
  22. final class SubsetSamplerUtils {

  23.     /** No public construction. */
  24.     private SubsetSamplerUtils() {}

  25.     /**
  26.      * Checks the subset of length {@code k} from {@code n} is valid.
  27.      *
  28.      * <p>If {@code n <= 0} or {@code k <= 0} or {@code k > n} then no subset
  29.      * is required and an exception is raised.</p>
  30.      *
  31.      * @param n   Size of the set.
  32.      * @param k   Size of the subset.
  33.      * @throws IllegalArgumentException if {@code n <= 0} or {@code k <= 0} or
  34.      *                                  {@code k > n}.
  35.      */
  36.     static void checkSubset(int n,
  37.                             int k) {
  38.         if (n <= 0) {
  39.             throw new IllegalArgumentException("n <= 0 : n=" + n);
  40.         }
  41.         if (k <= 0) {
  42.             throw new IllegalArgumentException("k <= 0 : k=" + k);
  43.         }
  44.         if (k > n) {
  45.             throw new IllegalArgumentException("k > n : k=" + k + ", n=" + n);
  46.         }
  47.     }

  48.     /**
  49.      * Perform a partial Fisher-Yates shuffle of the domain in-place and return
  50.      * either the upper fully shuffled section or the remaining lower partially
  51.      * shuffled section.
  52.      *
  53.      * <p>The returned combination will have a length of {@code steps} for
  54.      * {@code upper=true}, or {@code domain.length - steps} otherwise.</p>
  55.      *
  56.      * <p>Sampling uses {@link UniformRandomProvider#nextInt(int)}.</p>
  57.      *
  58.      * @param domain The domain.
  59.      * @param steps  The number of shuffle steps.
  60.      * @param rng    Generator of uniformly distributed random numbers.
  61.      * @param upper  Set to true to return the upper fully shuffled section.
  62.      * @return a random combination.
  63.      */
  64.     static int[] partialSample(int[] domain,
  65.                                int steps,
  66.                                UniformRandomProvider rng,
  67.                                boolean upper) {
  68.         // Shuffle from the end but limit to the number of steps.
  69.         // Note: If 'steps' is the full length of the array then the final
  70.         // swap is redundant so can be skipped.
  71.         int swapCount = Math.min(steps, domain.length - 1);
  72.         for (int i = domain.length - 1; swapCount > 0; i--, swapCount--) {
  73.             // Swap index i with any position down to 0 (including itself)
  74.             swap(domain, i, rng.nextInt(i + 1));
  75.         }
  76.         final int size = upper ? steps : domain.length - steps;
  77.         final int from = upper ? domain.length - steps : 0;
  78.         final int[] result = new int[size];
  79.         System.arraycopy(domain, from, result, 0, size);
  80.         return result;
  81.     }

  82.     /**
  83.      * Swaps the two specified elements in the specified array.
  84.      *
  85.      * @param array the array
  86.      * @param i     the first index
  87.      * @param j     the second index
  88.      */
  89.     static void swap(int[] array, int i, int j) {
  90.         final int tmp = array[i];
  91.         array[i] = array[j];
  92.         array[j] = tmp;
  93.     }
  94. }