Browse Source

added everything, wrote readme, made an animation

master
Victor Giers 2 months ago
parent
commit
da281d92ef
3 changed files with 178 additions and 0 deletions
  1. 13
    0
      README.md
  2. BIN
      curveprobability.gif
  3. 165
    0
      improved_rng.pde

+ 13
- 0
README.md View File

@@ -0,0 +1,13 @@
# Improved Random Number Generator

These are some methods for generating randomness.
There is:
- A plain copy of processings random() function which is just as fast
- A simple array shuffler
- An integer-RNG which doesn't produce the same number twice in a row
- A method to return a random entry of an array of arbitrary data-type with probability weights (not using Alias)
- A sophisticated RNG to return a random float between 0 and 1 with its selection weighted by a given probability curve defined by vectors

The last part is the coolest, but also very esoteric and WIP.
This animation shows its function:
![Probability Curves]()

BIN
curveprobability.gif View File


+ 165
- 0
improved_rng.pde View File

@@ -1 +1,166 @@
import java.util.*;

g g = new g();

final static class g {
//Loops after 48 bits of random numbers - which are sufficient for most cases. This is to increase performance
Random rand = new Random();

//Array shuffler, returns randomized array of arbitrary data-type
//With this you have fair distribution of results, so you know exactly how long it takes to read the array - exactly as long as if it weren't random - when you read the shuffled array iteratively
//(compared to reading random indices of a non-shuffled array)
Object random(Object[] input) {
Collections.shuffle(Arrays.asList(input));
return input;
}

//random() functions as known from Processing
float random(float floor, float ceiling) {
return rand.nextFloat() * (ceiling - floor) + floor;
}
float random(float ceiling) {
return rand.nextFloat() * ceiling;
}

//Returns a random integer and should not repeat the same number twice
int lastResult = 0;
int random(float floor, float ceiling, boolean norepetition) {
float nextFloat = rand.nextFloat();
int result = (int)(nextFloat * (ceiling - floor) + floor);
if (norepetition) {
if ((int)floor - (int)ceiling == 0) {
return result;
}
while (lastResult == result) {
nextFloat = rand.nextFloat();
result = (int)(nextFloat * (ceiling - floor) + floor);
}
lastResult = (int)result;
}
return result;
}
int random(float ceiling, boolean norepetition) {
return this.random(0.0f, ceiling, norepetition);
}

//Returns a random entry of an array of arbitrary data-type with probabilities
Object random(Object[] input, float[] probabilities) {
if (input.length != probabilities.length) {
println("Probability amount doesn't match amount of objects to select from.");
//Instead of return(null), runtimeException (IllegalArgumentException) might be better.
return(null);
}
float probabilitySum = 0;
for (int i = 0; i < probabilities.length; i++) {
probabilitySum += probabilities[i];
}
if (probabilitySum != 1.0) {
//We could also just throw an Exception here because it's resource hungry if the user doesn't supply probabilities that add up to exactly 1.0
for (int i = 0; i < probabilities.length; i++) {
probabilities[i] = map(probabilities[i], 0, probabilitySum, 0, 1);
}
}
//Not an Alias algorithm so potentially slow on long arrays
//Random r = new Random();
float r = rand.nextFloat();
float p = 0.0f;
for (int i = 0; i < input.length; i++) {
p += probabilities[i];
if (r < p) {
return(input[i]);
}
}
//Should never be reached
return(null);
}

//Returns a random float between 0 and 1, selection weighted by a given probability curve defined by vectors
float random(float[][] probCurve) {
//Imagine an x and y coordinate system reaching from (0,0) to (1,1).
//There's a function curve that must have exactly one value per f(x).
//This function curve defines the probability y per x.
//This is how it works:
//1. Generate 2 random values x & y
//2. Check if y is in the probability curve field f(x) (beneath f(x))
//3. If so, x is the random number we're looing for, else, repeat

//NEEDS to have a y value for x = 0 and a y value for x = 1
//NEEDS to have the x values in ascending order (in the array)



boolean foundX = false;
float x = 0.0;
float y = 0.0;
while (!foundX) {
x = rand.nextFloat();
y = rand.nextFloat();
int pos = 0;
for (int i = 0; i < probCurve.length; i++) {
if (x <= probCurve[i][0]) {
pos = i;
break;
}
}
if (lineIntersect(x, y, x, y+1, probCurve[pos-1][0], probCurve[pos-1][1], probCurve[pos][0], probCurve[pos][1])) {
foundX = true;
}
}
return x;
}
boolean lineIntersect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
return true;
}
return false;
}
}


void setup() {
/*
//A dice rolling twice as many sixes
int[] rolls = new int[6];
for (int i = 0; i < 10000; i++) {
int diceRoll = (int)g.random(new Object[]{(int)g.random(0, 5), 5}, new float[]{(float)5/7, (float)2/7});
rolls[diceRoll] += 1;
}
println(rolls);
*/
}

void draw() {
/*
//Prints a random float between -20 and 5 (just the same as Processings random()-function and exactly as fast)
println(g.random(-20, 5));
*/

/*
//Prints an integer between 15 and 20, should never repeat the same number twice in a row
println(g.random(15,20, true));
*/

/*
//An array shuffler for arbitrary data types
println(g.random(new Integer[]{1, 2, 3, 4, 5}));
String StringArray[] = {"eins", "zwei", "drei", "vier", "fünf"};
println(g.random(StringArray));
Float FloatArray[] = {1.1, 1.2, 1.3, 1.4, 1.5};
println(g.random(FloatArray));
*/

/*
//Prints array entries of arbitrary data type with a given probability in floats (it's recommended to let the probability floats add up to exactly 1.0, if they don't it's way more resource hungry)
Object[] entries = {"rare", "common", "808"};
float[] probabilities = {0.1, 0.85, 0.05};
println(g.random(entries, probabilities));
*/

/*
//Prints random floats between 0 and 1 depending on a probability curve.
//Never between 0 and 0.3. Probability between 0.3 and 0.5 ramps up, and goes down again between 0.5 and 0.6, diminishing to zero at 0.95, where it ramps up again, so values between 0.95 and 1.0 are again highly probable.
println(g.random(new float[][]{{0.0, 0.0}, {0.3, 0.0}, {0.5, 1.0}, {0.6, 0.3}, {0.95,0.0}, {1.0, 1.0}}));
*/
}

Loading…
Cancel
Save