/** This example is designed to highlight the differences between random number generation methods. Click on the screen to change the method being visualized. What is being measured on screen is how often numbers in the range defined in RAND_SIZE have occurred with the current number generator. */ // Over what scale of numbers do we want to measure public static final int RAND_SIZE = 100; // An array to track how often each number is generated int[] sample_set = new int[RAND_SIZE]; // Keep track of how many numbers we've generated int total_samples = 0; // Our font PFont labelFont; // The current generator to use, value from 0 to 3 int mode = 0; void setup() { // setup the environment size(500, 300); colorMode(RGB, 255, 255, 255, 100); background(10); // load the font labelFont = loadFont("Courier-Oblique-20.vlw"); textFont(labelFont, 10); // we use reset to clear out the number tracking array reset(); } void reset() { // set all of the values in our tracking array to 0 for (int i = 0; i < RAND_SIZE; i++) { sample_set[i] = 0; } // reset how many samples we've taken total_samples = 0; } void draw() { // fill in the background fill(200, 200, 200, 100); rect(0, 0, width, height); /* To speed things up we generate 2x our random sample size. With each pass we generate a new random number, and increment that spot in our tracking array. */ for (int i = 0; i < RAND_SIZE * 2; i++) { int nRand = 0; if (mode == 0) { nRand = int(getUniformRandom(0, RAND_SIZE)); } else if (mode == 1) { nRand = int(getGaussianRandom(0, RAND_SIZE)); } else if (mode == 2) { nRand = int(getStrangeRandom(0, RAND_SIZE)); } else if (mode == 3) { nRand = int(getInverseGaussian(0, RAND_SIZE)); } // Keep track of the number we generated sample_set[nRand]++; // Keep track of the total number of samples so far total_samples++; } // now do some quick calculations for what the most // frequent sample is. We track both the most and // least frequent numbers. What we're really interested // in is the frequency itself, not necessarily which number // is most frequent float max_freq = 0.0; float min_freq = 1.0; for (int i = 0; i < RAND_SIZE; i++) { // Figure how how frequently this number appears float sample_freq = float(sample_set[i]) / float(total_samples); if (sample_freq > max_freq) { max_freq = sample_freq; } if (sample_freq < min_freq) { min_freq = sample_freq; } } // set the drawing properties for our bar graph stroke(15, 15, 15, 10); fill(32, 64, 128, 200); /* Here we'll draw out our bar graph of all of the samples we've taken. We want the most frequent sample to be as tall as the screen, with all of the other samples scaled within the height of the screen. */ // This is how wide our bar is float block_size = float(width) / float(RAND_SIZE); for (int i = 0; i < RAND_SIZE; i++) { // figure out how frequent this sample is float sample_freq = float(sample_set[i]) / total_samples; // determine how tall the bar for this item should be int sample_height = int(sample_freq / max_freq * float(height)); // draw the bar rect(i * block_size, height, block_size, -1 * sample_height); } // Set the drawing style for our legend stroke(0, 0, 0, 100); fill(255, 255, 255, 100); // Display how many samples we've taken text("Samples: " + total_samples, 5, height - 15); // Display what random generator we're using if (mode == 0) { text("Mode: Uniform", 5, height - 5); } else if (mode == 1) { text("Mode: Gaussian", 5, height - 5); } else if (mode == 2) { text("Mode: Strange", 5, height - 5); } else if (mode == 3) { text("Mode: Inverse Gaussian", 5, height -4); } } void mousePressed() { // reset the number tracking reset(); // change our mode for generating random numbers mode++; if (mode == 4) { mode = 0; } } /** Generate a uniform random number using the Processing random number method. */ float getUniformRandom(int rMin, int rMax) { return random(rMin, rMax); } /** Generate a number scaled to a gaussian curve. We won't go into the math and algorithm used here, but you're welcome to explore what's happening here on your own. */ float getGaussianRandom(int rMin, int rMax) { // setup a simple gaussian distribution float[] gausSet = {0.01, 0.01, 0.01, 0.05, 0.3, 0.4, 0.3, 0.05, 0.01, 0.01}; int range = rMax - rMin; float rangeScale = float(range) / gausSet.length; // loop through this test until we succeed while (true) { // generate a test number in our range float testRandom = random(rMin, rMax); // see if this is under our curve // scale our random number down to our gaussian scale float gausScale = (testRandom / rangeScale); // figure out which number this lies between and if that averaged // number is under the curve float leftProbability = gausSet[int(gausScale)]; float rightProbability = 0.0; if (int(gausScale) + 1 == gausSet.length) { rightProbability = gausSet[gausSet.length - 1]; } else { rightProbability = gausSet[int(gausScale) + 1]; } float probDiff = 0.0; probDiff = rightProbability - leftProbability; probDiff = leftProbability + (probDiff * ( gausScale - float(int(gausScale)))); if (random(0, 1) < probDiff) { return testRandom; } } } float getStrangeRandom(int rMin, int rMax) { // setup a simple gaussian distribution float[] gausSet = {0.5, 0.01, 0.5, 0.01, 0.5, 0.01, 0.5, 0.01, 0.5, 0.01}; int range = rMax - rMin; float rangeScale = float(range) / gausSet.length; // loop through this test until we succeed while (true) { // generate a test number in our range float testRandom = random(rMin, rMax); // see if this is under our curve // scale our random number down to our gaussian scale float gausScale = (testRandom / rangeScale); // figure out which number this lies between and if that averaged // number is under the curve float leftProbability = gausSet[int(gausScale)]; float rightProbability = 0.0; if (int(gausScale) + 1 == gausSet.length) { rightProbability = gausSet[gausSet.length - 1]; } else { rightProbability = gausSet[int(gausScale) + 1]; } float probDiff = 0.0; probDiff = rightProbability - leftProbability; probDiff = leftProbability + (probDiff * ( gausScale - float(int(gausScale)))); if (random(0, 1) < probDiff) { return testRandom; } } } float getInverseGaussian(int rMin, int rMax) { // setup a simple gaussian distribution //float[] gausSet = {0.01, 0.01, 0.01, 0.05, 0.3, 0.4, 0.3, 0.05, 0.01, 0.01}; float[] gausSet = {0.4, 0.4, 0.4, 0.3, 0.05, 0.01, 0.05, 0.3, 0.4, 0.4}; int range = rMax - rMin; float rangeScale = float(range) / gausSet.length; // loop through this test until we succeed while (true) { // generate a test number in our range float testRandom = random(rMin, rMax); // see if this is under our curve // scale our random number down to our gaussian scale float gausScale = (testRandom / rangeScale); // figure out which number this lies between and if that averaged // number is under the curve float leftProbability = gausSet[int(gausScale)]; float rightProbability = 0.0; if (int(gausScale) + 1 == gausSet.length) { rightProbability = gausSet[gausSet.length - 1]; } else { rightProbability = gausSet[int(gausScale) + 1]; } float probDiff = 0.0; probDiff = rightProbability - leftProbability; probDiff = leftProbability + (probDiff * ( gausScale - float(int(gausScale)))); if (random(0, 1) < probDiff) { return testRandom; } } }