/** This is similar to our first example, but only maintains a certain sample size based upon the position of the mouse on the screen. This helps to visualize the character of a random number generator over different sample sizes, showing the volatility over short samples, and the stability over longer samples. */ // How many numbers are we tracking public static final int RAND_SIZE = 50; // create our tacking array int[] sample_set = new int[RAND_SIZE]; // total samples so far int total_samples = 0; // maximum number of samples to keep int sample_max = 10000; // keep track of which samples have been taken, so we can then // remove them from the sample slice Vector sampleQueue = new Vector(); // our font PFont labelFont; // Which RNG are we using 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); // Reset our number tracking reset(); } void reset() { // setup our sample array for (int i = 0; i < RAND_SIZE; i++) { sample_set[i] = 0; } // reset how many samples we've taken total_samples = 0; // clear the queue maintaining our sample slice sampleQueue.clear(); } void draw() { // clear the background fill(200, 200, 200, 100); rect(0, 0, width, height); // generate a set of random numbers 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)); } // save our random number in our tracking array sample_set[nRand]++; // keep track of total samples total_samples++; // keep track of which numbers have been added sampleQueue.add(new Integer(nRand)); /** Because we only want to track a certain slice of random numbers, we need to remove old random number outside of our sample slice from the tracking array. We pull numbers from the front of our sampleQueue until we have the correct number of samples */ while (sampleQueue.size() > sample_max) { int oldSample = ((Integer)sampleQueue.get(0)).intValue(); sampleQueue.remove(0); sample_set[oldSample]--; } } // now do some quick calculations for what the most // frequent sample is float max_freq = 0.0; float min_freq = 1.0; for (int i = 0; i < RAND_SIZE; i++) { 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 style for our bargraph stroke(15, 15, 15, 10); fill(32, 64, 128, 200); // now draw each bar for the graph, sizing the bars so the most frequent // is as tall as the screen, and all the other bars are of relative height float block_size = float(width) / float(RAND_SIZE); for (int i = 0; i < RAND_SIZE; i++) { float sample_freq = float(sample_set[i]) / total_samples; int sample_height = int(sample_freq / max_freq * float(height)); rect(i * block_size, height, block_size, -1 * sample_height); } // set our text style for the legend stroke(0, 0, 0, 100); fill(255, 255, 255, 100); // how many samples are in our slice text("Slice Size: " + sampleQueue.size(), 5, height - 25); // how many samples do we have so far text("Samples: " + total_samples, 5, height - 15); // display which RNG 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 - 5); } } void mousePressed() { // reset the tracking array reset(); // change the RNG we're using mode++; if (mode == 4) { mode = 0; } } void mouseMoved() { // scale sample_max = int(float(mouseX) / float(width) * 20000.0); } // See comments in example01 for a discussion of the remainder of this code. //****************// float getUniformRandom(int rMin, int rMax) { return random(rMin, rMax); } 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; } } }