ArraySampler.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.  * Utilities for shuffling an array in-place.
  21.  *
  22.  * <p>Shuffles use the <a
  23.  * href="https://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm">
  24.  * Fisher-Yates</a> algorithm.
  25.  *
  26.  * @since 1.6
  27.  */
  28. public final class ArraySampler {
  29.     /** Class contains only static methods. */
  30.     private ArraySampler() {}

  31.     /**
  32.      * Shuffles the entries of the given array.
  33.      *
  34.      * @param rng Source of randomness.
  35.      * @param array Array whose entries will be shuffled (in-place).
  36.      * @return a reference to the given array
  37.      */
  38.     public static boolean[] shuffle(UniformRandomProvider rng, boolean[] array) {
  39.         for (int i = array.length; i > 1; i--) {
  40.             swap(array, i - 1, rng.nextInt(i));
  41.         }
  42.         return array;
  43.     }

  44.     /**
  45.      * Shuffles the entries of the given array.
  46.      *
  47.      * @param rng Source of randomness.
  48.      * @param array Array whose entries will be shuffled (in-place).
  49.      * @return a reference to the given array
  50.      */
  51.     public static byte[] shuffle(UniformRandomProvider rng, byte[] array) {
  52.         for (int i = array.length; i > 1; i--) {
  53.             swap(array, i - 1, rng.nextInt(i));
  54.         }
  55.         return array;
  56.     }

  57.     /**
  58.      * Shuffles the entries of the given array.
  59.      *
  60.      * @param rng Source of randomness.
  61.      * @param array Array whose entries will be shuffled (in-place).
  62.      * @return a reference to the given array
  63.      */
  64.     public static char[] shuffle(UniformRandomProvider rng, char[] array) {
  65.         for (int i = array.length; i > 1; i--) {
  66.             swap(array, i - 1, rng.nextInt(i));
  67.         }
  68.         return array;
  69.     }

  70.     /**
  71.      * Shuffles the entries of the given array.
  72.      *
  73.      * @param rng Source of randomness.
  74.      * @param array Array whose entries will be shuffled (in-place).
  75.      * @return a reference to the given array
  76.      */
  77.     public static double[] shuffle(UniformRandomProvider rng, double[] array) {
  78.         for (int i = array.length; i > 1; i--) {
  79.             swap(array, i - 1, rng.nextInt(i));
  80.         }
  81.         return array;
  82.     }

  83.     /**
  84.      * Shuffles the entries of the given array.
  85.      *
  86.      * @param rng Source of randomness.
  87.      * @param array Array whose entries will be shuffled (in-place).
  88.      * @return a reference to the given array
  89.      */
  90.     public static float[] shuffle(UniformRandomProvider rng, float[] array) {
  91.         for (int i = array.length; i > 1; i--) {
  92.             swap(array, i - 1, rng.nextInt(i));
  93.         }
  94.         return array;
  95.     }

  96.     /**
  97.      * Shuffles the entries of the given array.
  98.      *
  99.      * @param rng Source of randomness.
  100.      * @param array Array whose entries will be shuffled (in-place).
  101.      * @return a reference to the given array
  102.      */
  103.     public static int[] shuffle(UniformRandomProvider rng, int[] array) {
  104.         for (int i = array.length; i > 1; i--) {
  105.             swap(array, i - 1, rng.nextInt(i));
  106.         }
  107.         return array;
  108.     }

  109.     /**
  110.      * Shuffles the entries of the given array.
  111.      *
  112.      * @param rng Source of randomness.
  113.      * @param array Array whose entries will be shuffled (in-place).
  114.      * @return a reference to the given array
  115.      */
  116.     public static long[] shuffle(UniformRandomProvider rng, long[] array) {
  117.         for (int i = array.length; i > 1; i--) {
  118.             swap(array, i - 1, rng.nextInt(i));
  119.         }
  120.         return array;
  121.     }

  122.     /**
  123.      * Shuffles the entries of the given array.
  124.      *
  125.      * @param rng Source of randomness.
  126.      * @param array Array whose entries will be shuffled (in-place).
  127.      * @return a reference to the given array
  128.      */
  129.     public static short[] shuffle(UniformRandomProvider rng, short[] array) {
  130.         for (int i = array.length; i > 1; i--) {
  131.             swap(array, i - 1, rng.nextInt(i));
  132.         }
  133.         return array;
  134.     }

  135.     /**
  136.      * Shuffles the entries of the given array.
  137.      *
  138.      * @param <T> Type of the items.
  139.      * @param rng Source of randomness.
  140.      * @param array Array whose entries will be shuffled (in-place).
  141.      * @return a reference to the given array
  142.      */
  143.     public static <T> T[] shuffle(UniformRandomProvider rng, T[] array) {
  144.         for (int i = array.length; i > 1; i--) {
  145.             swap(array, i - 1, rng.nextInt(i));
  146.         }
  147.         return array;
  148.     }

  149.     /**
  150.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  151.      *
  152.      * @param rng Source of randomness.
  153.      * @param array Array whose entries will be shuffled (in-place).
  154.      * @param from Lower-bound (inclusive) of the sub-range.
  155.      * @param to Upper-bound (exclusive) of the sub-range.
  156.      * @return a reference to the given array
  157.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  158.      */
  159.     public static boolean[] shuffle(UniformRandomProvider rng, boolean[] array, int from, int to) {
  160.         final int length = to - checkFromToIndex(from, to, array.length);
  161.         for (int i = length; i > 1; i--) {
  162.             swap(array, from + i - 1, from + rng.nextInt(i));
  163.         }
  164.         return array;
  165.     }

  166.     /**
  167.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  168.      *
  169.      * @param rng Source of randomness.
  170.      * @param array Array whose entries will be shuffled (in-place).
  171.      * @param from Lower-bound (inclusive) of the sub-range.
  172.      * @param to Upper-bound (exclusive) of the sub-range.
  173.      * @return a reference to the given array
  174.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  175.      */
  176.     public static byte[] shuffle(UniformRandomProvider rng, byte[] array, int from, int to) {
  177.         final int length = to - checkFromToIndex(from, to, array.length);
  178.         for (int i = length; i > 1; i--) {
  179.             swap(array, from + i - 1, from + rng.nextInt(i));
  180.         }
  181.         return array;
  182.     }

  183.     /**
  184.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  185.      *
  186.      * @param rng Source of randomness.
  187.      * @param array Array whose entries will be shuffled (in-place).
  188.      * @param from Lower-bound (inclusive) of the sub-range.
  189.      * @param to Upper-bound (exclusive) of the sub-range.
  190.      * @return a reference to the given array
  191.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  192.      */
  193.     public static char[] shuffle(UniformRandomProvider rng, char[] array, int from, int to) {
  194.         final int length = to - checkFromToIndex(from, to, array.length);
  195.         for (int i = length; i > 1; i--) {
  196.             swap(array, from + i - 1, from + rng.nextInt(i));
  197.         }
  198.         return array;
  199.     }

  200.     /**
  201.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  202.      *
  203.      * @param rng Source of randomness.
  204.      * @param array Array whose entries will be shuffled (in-place).
  205.      * @param from Lower-bound (inclusive) of the sub-range.
  206.      * @param to Upper-bound (exclusive) of the sub-range.
  207.      * @return a reference to the given array
  208.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  209.      */
  210.     public static double[] shuffle(UniformRandomProvider rng, double[] array, int from, int to) {
  211.         final int length = to - checkFromToIndex(from, to, array.length);
  212.         for (int i = length; i > 1; i--) {
  213.             swap(array, from + i - 1, from + rng.nextInt(i));
  214.         }
  215.         return array;
  216.     }

  217.     /**
  218.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  219.      *
  220.      * @param rng Source of randomness.
  221.      * @param array Array whose entries will be shuffled (in-place).
  222.      * @param from Lower-bound (inclusive) of the sub-range.
  223.      * @param to Upper-bound (exclusive) of the sub-range.
  224.      * @return a reference to the given array
  225.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  226.      */
  227.     public static float[] shuffle(UniformRandomProvider rng, float[] array, int from, int to) {
  228.         final int length = to - checkFromToIndex(from, to, array.length);
  229.         for (int i = length; i > 1; i--) {
  230.             swap(array, from + i - 1, from + rng.nextInt(i));
  231.         }
  232.         return array;
  233.     }

  234.     /**
  235.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  236.      *
  237.      * @param rng Source of randomness.
  238.      * @param array Array whose entries will be shuffled (in-place).
  239.      * @param from Lower-bound (inclusive) of the sub-range.
  240.      * @param to Upper-bound (exclusive) of the sub-range.
  241.      * @return a reference to the given array
  242.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  243.      */
  244.     public static int[] shuffle(UniformRandomProvider rng, int[] array, int from, int to) {
  245.         final int length = to - checkFromToIndex(from, to, array.length);
  246.         for (int i = length; i > 1; i--) {
  247.             swap(array, from + i - 1, from + rng.nextInt(i));
  248.         }
  249.         return array;
  250.     }

  251.     /**
  252.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  253.      *
  254.      * @param rng Source of randomness.
  255.      * @param array Array whose entries will be shuffled (in-place).
  256.      * @param from Lower-bound (inclusive) of the sub-range.
  257.      * @param to Upper-bound (exclusive) of the sub-range.
  258.      * @return a reference to the given array
  259.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  260.      */
  261.     public static long[] shuffle(UniformRandomProvider rng, long[] array, int from, int to) {
  262.         final int length = to - checkFromToIndex(from, to, array.length);
  263.         for (int i = length; i > 1; i--) {
  264.             swap(array, from + i - 1, from + rng.nextInt(i));
  265.         }
  266.         return array;
  267.     }

  268.     /**
  269.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  270.      *
  271.      * @param rng Source of randomness.
  272.      * @param array Array whose entries will be shuffled (in-place).
  273.      * @param from Lower-bound (inclusive) of the sub-range.
  274.      * @param to Upper-bound (exclusive) of the sub-range.
  275.      * @return a reference to the given array
  276.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  277.      */
  278.     public static short[] shuffle(UniformRandomProvider rng, short[] array, int from, int to) {
  279.         final int length = to - checkFromToIndex(from, to, array.length);
  280.         for (int i = length; i > 1; i--) {
  281.             swap(array, from + i - 1, from + rng.nextInt(i));
  282.         }
  283.         return array;
  284.     }

  285.     /**
  286.      * Shuffles the entries of the given array in the range {@code [from, to)}.
  287.      *
  288.      * @param <T> Type of the items.
  289.      * @param rng Source of randomness.
  290.      * @param array Array whose entries will be shuffled (in-place).
  291.      * @param from Lower-bound (inclusive) of the sub-range.
  292.      * @param to Upper-bound (exclusive) of the sub-range.
  293.      * @return a reference to the given array
  294.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  295.      */
  296.     public static <T> T[] shuffle(UniformRandomProvider rng, T[] array, int from, int to) {
  297.         final int length = to - checkFromToIndex(from, to, array.length);
  298.         for (int i = length; i > 1; i--) {
  299.             swap(array, from + i - 1, from + rng.nextInt(i));
  300.         }
  301.         return array;
  302.     }

  303.     /**
  304.      * Swaps the two specified elements in the array.
  305.      *
  306.      * @param array Array.
  307.      * @param i First index.
  308.      * @param j Second index.
  309.      */
  310.     private static void swap(boolean[] array, int i, int j) {
  311.         final boolean tmp = array[i];
  312.         array[i] = array[j];
  313.         array[j] = tmp;
  314.     }

  315.     /**
  316.      * Swaps the two specified elements in the array.
  317.      *
  318.      * @param array Array.
  319.      * @param i First index.
  320.      * @param j Second index.
  321.      */
  322.     private static void swap(byte[] array, int i, int j) {
  323.         final byte tmp = array[i];
  324.         array[i] = array[j];
  325.         array[j] = tmp;
  326.     }

  327.     /**
  328.      * Swaps the two specified elements in the array.
  329.      *
  330.      * @param array Array.
  331.      * @param i First index.
  332.      * @param j Second index.
  333.      */
  334.     private static void swap(char[] array, int i, int j) {
  335.         final char tmp = array[i];
  336.         array[i] = array[j];
  337.         array[j] = tmp;
  338.     }

  339.     /**
  340.      * Swaps the two specified elements in the array.
  341.      *
  342.      * @param array Array.
  343.      * @param i First index.
  344.      * @param j Second index.
  345.      */
  346.     private static void swap(double[] array, int i, int j) {
  347.         final double tmp = array[i];
  348.         array[i] = array[j];
  349.         array[j] = tmp;
  350.     }

  351.     /**
  352.      * Swaps the two specified elements in the array.
  353.      *
  354.      * @param array Array.
  355.      * @param i First index.
  356.      * @param j Second index.
  357.      */
  358.     private static void swap(float[] array, int i, int j) {
  359.         final float tmp = array[i];
  360.         array[i] = array[j];
  361.         array[j] = tmp;
  362.     }

  363.     /**
  364.      * Swaps the two specified elements in the array.
  365.      *
  366.      * @param array Array.
  367.      * @param i First index.
  368.      * @param j Second index.
  369.      */
  370.     private static void swap(int[] array, int i, int j) {
  371.         final int tmp = array[i];
  372.         array[i] = array[j];
  373.         array[j] = tmp;
  374.     }

  375.     /**
  376.      * Swaps the two specified elements in the array.
  377.      *
  378.      * @param array Array.
  379.      * @param i First index.
  380.      * @param j Second index.
  381.      */
  382.     private static void swap(long[] array, int i, int j) {
  383.         final long tmp = array[i];
  384.         array[i] = array[j];
  385.         array[j] = tmp;
  386.     }

  387.     /**
  388.      * Swaps the two specified elements in the array.
  389.      *
  390.      * @param array Array.
  391.      * @param i First index.
  392.      * @param j Second index.
  393.      */
  394.     private static void swap(short[] array, int i, int j) {
  395.         final short tmp = array[i];
  396.         array[i] = array[j];
  397.         array[j] = tmp;
  398.     }

  399.     /**
  400.      * Swaps the two specified elements in the array.
  401.      *
  402.      * @param array Array.
  403.      * @param i First index.
  404.      * @param j Second index.
  405.      */
  406.     private static void swap(Object[] array, int i, int j) {
  407.         final Object tmp = array[i];
  408.         array[i] = array[j];
  409.         array[j] = tmp;
  410.     }

  411.     /**
  412.      * Checks if the sub-range from fromIndex (inclusive) to toIndex (exclusive) is
  413.      * within the bounds of range from 0 (inclusive) to length (exclusive).
  414.      *
  415.      * <p>This function provides the functionality of
  416.      * {@code java.utils.Objects.checkFromToIndex} introduced in JDK 9. The <a
  417.      * href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Objects.html#checkFromToIndex(int,int,int)">Objects</a>
  418.      * javadoc has been reproduced for reference.
  419.      *
  420.      * <p>The sub-range is defined to be out of bounds if any of the following
  421.      * inequalities is true:
  422.      * <ul>
  423.      * <li>{@code fromIndex < 0}
  424.      * <li>{@code fromIndex > toIndex}
  425.      * <li>{@code toIndex > length}
  426.      * <li>{@code length < 0}, which is implied from the former inequalities
  427.      * </ul>
  428.      *
  429.      * @param fromIndex Lower-bound (inclusive) of the sub-range.
  430.      * @param toIndex Upper-bound (exclusive) of the sub-range.
  431.      * @param length Upper-bound (exclusive) of the range
  432.      * @return fromIndex if the sub-range is within the bounds of the range
  433.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  434.      */
  435.     private static int checkFromToIndex(int fromIndex, int toIndex, int length) {
  436.         // Checks as documented above
  437.         if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) {
  438.             throw new IndexOutOfBoundsException(
  439.                     String.format("Range [%d, %d) out of bounds for length %d", fromIndex, toIndex, length));
  440.         }
  441.         return fromIndex;
  442.     }
  443. }