Guest User

ClusteredGaussianListGenerator

a guest
Oct 1st, 2013
154
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 8.49 KB | None | 0 0
  1. package se.chalmers.dat255.sleepfighter.challenge.sort;
  2.  
  3. import java.util.BitSet;
  4. import java.util.Random;
  5.  
  6. import se.chalmers.dat255.sleepfighter.utils.collect.PrimitiveArrays;
  7. import se.chalmers.dat255.sleepfighter.utils.math.IntMath;
  8. import se.chalmers.dat255.sleepfighter.utils.math.RandomMath;
  9.  
  10. /**
  11.  * ClusteredGaussianListGenerator is an implementation of NumberListGenerator
  12.  * provides a list of numbers that have clusters with low standard deviation (stdDev),
  13.  * and high stdDev as a whole.
  14.  *
  15.  * We want a list that looks like for example:
  16.  * [131, 135, 136, 315, 317, 328, 931, 935, 941].
  17.  * There is nothing special about the numbers in themselves,
  18.  * other than that the whole set has a large stdDev while
  19.  * the tripples form clusters with low stdDev internally.
  20.  *
  21.  * In order to accomplish this, we first randomize a list
  22.  * with outerSize which make up the higher numbers like: x00
  23.  * where x is a randomized digit. This value is standard-distributed from 1-9.
  24.  *
  25.  * After, we do for each number in the higher/outer numbers
  26.  * a new list of innerSize which is calculated as higher[i] + randGaussian(innerRange)
  27.  * where innerRange is some random generated range within [1, 99] if say numDigits == 3.
  28.  *
  29.  * @author Centril<[email protected]> / Mazdak Farrokhzad.
  30.  * @version 1.0
  31.  * @since Oct 1, 2013
  32.  */
  33. public class ClusteredGaussianListGenerator implements NumberListGenerator {
  34.     private static final double INNER_STANDARD_DEVIATIONS = 2.0;
  35.     private static final int MAX_GAUSS_ITERATIONS = 100;
  36.  
  37.     private int numDigits = 3;
  38.  
  39.     @Override
  40.     public void setNumDigits( int digits ) {
  41.         if ( this.numDigits < 2 ) {
  42.             throw new IllegalArgumentException( "n(digits) must be > 1, but " + digits + " was provided" );
  43.         }
  44.  
  45.         this.numDigits = digits;
  46.     }
  47.  
  48.     @Override
  49.     public int getNumDigits() {
  50.         return this.numDigits;
  51.     }
  52.  
  53.     /**
  54.      * {@inheritDoc}<br/>
  55.      * Implementation note: the size must have integer factors: <code>size = a * b, where a, b ∈  Z+</code>
  56.      */
  57.     @Override
  58.     public int[] generateList( Random rng, int size ) {
  59.         // outer = index 0, inner = index 1.
  60.         int[] sizes = computeSizes( size );
  61.         int[] numbers = new int[sizes[0] * sizes[1]];
  62.         int outerMultiplier = com.google.common.math.IntMath.pow( 10, this.numDigits - 1 );
  63.         int innerMax = outerMultiplier - 1;
  64.  
  65.         // Fill outer array first.
  66.         int[] outer = new int[sizes[0]];
  67.         for ( int i = 0; i < sizes[0]; ++i ) {
  68.             outer[i] = RandomMath.nextRandomRanged( rng, 1, 9 ) * outerMultiplier;
  69.         }
  70.  
  71.         // Fill inner array for each outer array.
  72.         for ( int i = 0; i < sizes[0]; ++i ) {
  73.             // Calculate bounds [min, max].
  74.             int[] innerBounds = new int[] { RandomMath.nextRandomNon10( rng, 1, innerMax ), RandomMath.nextRandomNon10( rng, 1, innerMax ) };
  75.             int diff = innerBounds[1] - innerBounds[0];
  76.             if ( diff < 0 ) {
  77.                 // Wrong order, swap!
  78.                 PrimitiveArrays.swap( innerBounds, 0, 1 );
  79.                 diff = -diff;
  80.             }
  81.             if ( diff < sizes[1] ) {
  82.                 // Difference is too small, make sure we got room!
  83.                 innerBounds[0] = Math.max( 1, innerBounds[0] - sizes[1] );
  84.                 innerBounds[1] = innerBounds[0] + sizes[1];
  85.  
  86.                 diff = innerBounds[1] - innerBounds[0];
  87.             }
  88.  
  89.             BitSet bits = new BitSet( diff );
  90.             boolean filledModulo10 = false;
  91.  
  92.             // Now do the filling.
  93.             int[] inner = new int[sizes[1]];
  94.             for ( int j = 0; j < sizes[1]; ++j ) {
  95.                 inner[j] = RandomMath.nextGaussianNon10( rng, innerBounds[0], innerBounds[1], MAX_GAUSS_ITERATIONS, INNER_STANDARD_DEVIATIONS );
  96.  
  97.                 // Protect against same numbers all the time, can we do away with this loop? not O(n) but still...
  98.                 boolean hasDuplicate = false;
  99.                 for ( int k = 0; k < j; ++k ) {
  100.                     if ( inner[k] == inner[j] ) {
  101.                         hasDuplicate = true;
  102.                     }
  103.                 }
  104.  
  105.                 if ( hasDuplicate ) {
  106.                     if ( !filledModulo10 ) {
  107.                         // Set all numbers that end with 0 in BitSet, we don't want them!
  108.                         // This assumes that neither innerBounds[0, 1] are modulo 10.
  109.                         for ( int l = ((innerBounds[0] / 10) + 1) * 10; l <= innerBounds[1]; l += 10 ) {
  110.                             bits.set( l - innerBounds[0] );
  111.                         }
  112.  
  113.                         filledModulo10 = true;
  114.                     }
  115.  
  116.                     // Find first false bit.
  117.                     // This beats the idea of randomness, but avoiding duplicates is more important!
  118.                     inner[j] = bits.nextClearBit( 0 ) + innerBounds[0];
  119.                 }
  120.  
  121.                 bits.set( inner[j] - innerBounds[0] );
  122.  
  123.                 numbers[sizes[1] * i + j] = outer[i] + inner[j];
  124.             }
  125.         }
  126.  
  127.         return numbers;
  128.     }
  129.  
  130.     /* --------------------------------
  131.      * Private interface.
  132.      * --------------------------------
  133.      */
  134.  
  135.     /**
  136.      * Computes outer & inner sizes.
  137.      *
  138.      * @param size the size to break down.
  139.      * @return the sizes.
  140.      */
  141.     private int[] computeSizes( int size ) {
  142.         if ( size < 1 ) {
  143.             throw new IllegalArgumentException( "Only integers > 0 are allowed" );
  144.         }
  145.  
  146.         int[] sizes = IntMath.findClosestFactors( size );
  147.  
  148.         if ( sizes == null ) {
  149.             throw new IllegalArgumentException( "There is no 2-factor solution for the size: " + size );
  150.         }
  151.  
  152.         return sizes;
  153.     }
  154. }
  155.  
  156. package se.chalmers.dat255.sleepfighter.utils.math;
  157.  
  158. import java.util.Random;
  159.  
  160. /**
  161.  * RandomMath provides utility methods for dealing with random numbers.
  162.  *
  163.  * @author Centril<[email protected]> / Mazdak Farrokhzad.
  164.  * @version 1.0
  165.  * @since Sep 30, 2013
  166.  */
  167. public class RandomMath {
  168.     /**
  169.      * Calculates the first random number in range [min, max] that is not modulo 10.
  170.      *
  171.      * @param rng the random number generator.
  172.      * @param min the minimum number, inclusive.
  173.      * @param max the maximum number, inclusive.
  174.      * @return the randomly generated number.
  175.      */
  176.     public static int nextRandomNon10( Random rng, int min, int max )  {
  177.         int r;
  178.         while ( (r = nextRandomRanged( rng, min, max )) % 10 == 0 );
  179.         return r;
  180.     }
  181.  
  182.     /**
  183.      * Calculates random number in range [min, max]
  184.      *
  185.      * @param rng the random number generator.
  186.      * @param min the minimum number, inclusive.
  187.      * @param max the maximum number, inclusive.
  188.      * @return the randomly generated number.
  189.      */
  190.     public static int nextRandomRanged( Random rng, int min, int max ) {
  191.         // Maybe use nextGaussian here instead?
  192.         return min + (int) (rng.nextDouble() * ((max - min) + 1));
  193.     }
  194.  
  195.     /**
  196.      * <p>Returns a random generated gaussian / normal distributed<br/>
  197.      * number with given mean and standard deviation.</p>
  198.      *
  199.      * <p>99.7% of all numbers lie within the 3rd standard deviation, see:<br/>
  200.      * http://en.wikipedia.org/wiki/68-95-99.7_rule</p>
  201.      *
  202.      * @see http://en.wikipedia.org/wiki/68-95-99.7_rule
  203.      * @param rng the random number generator.
  204.      * @param mean the mean to distribute around.
  205.      * @param stdDev the standard deviation, which in layman terms can be put as how much the curve is stretched.
  206.      * @return the randomly generated number.
  207.      */
  208.     public static double nextGaussian( Random rng, double mean, double stdDev ) {
  209.         return mean + rng.nextGaussian() * stdDev;
  210.     }
  211.  
  212.     /**
  213.      * <p>Returns a random generated pseudo-gaussian / normal<br/>
  214.      * distributed number in a given range [min, max].<br/>
  215.      * It is not gaussian in the sense that it has a range<br/>
  216.      * whereas gaussian distributions are unbounded.</p>
  217.      *
  218.      * @param rng the random number generator.
  219.      * @param min the minimum value, inclusive.
  220.      * @param max the maximum value, inclusive.
  221.      * @param maxIterations the hard limit of iterations to run before ending loop, used as a safety measure.
  222.      * @param deviations how many standard deviations to use.
  223.      * @return the random number.
  224.      */
  225.     public static int nextGaussianRanged( Random rng, int min, int max, int maxIterations, double deviations ) {
  226.         // See http://stackoverflow.com/questions/1303368/how-to-generate-normally-distributed-random-from-an-integer-range?rq=1
  227.         int r = 0;
  228.         for ( int i = 0; (i < maxIterations) && ((r = (int) nextGaussian( rng, min + (max - min) / 2.0, (max - min) / 2.0 / deviations )) > max || r < min); ++i );
  229.         return r;
  230.     }
  231.  
  232.     /**
  233.      * Like {@link #nextGaussianRanged(Random, int, int, int)} but excluding numbers modulo 10.
  234.      *
  235.      * @param rng the random number generator.
  236.      * @param min the minimum value, inclusive.
  237.      * @param max the maximum value, inclusive.
  238.      * @param maxIterations the hard limit of iterations to run before ending loop, used as a safety measure.
  239.      * @param deviations how many standard deviations to use.
  240.      * @return the random number.
  241.      */
  242.     public static int nextGaussianNon10( Random rng, int min, int max, int maxIterations, double deviations ) {
  243.         int r;
  244.         while ( (r = nextGaussianRanged( rng, min, max, maxIterations, deviations )) % 10 == 0 );
  245.         return r;
  246.     }
  247. }
Advertisement
Add Comment
Please, Sign In to add comment