Change batch/parallel Ranged Ingredient rolls from uniform random to gaussian random#4212
Change batch/parallel Ranged Ingredient rolls from uniform random to gaussian random#4212DilithiumThoride wants to merge 7 commits into
Conversation
|
You could just call it a In our case, we are essentially rolling The mean (Taken from Wikipedia, but this can be confirmed with the formulas for expected value and variance) Then, when rolling If we were to continue doing these rolls of If you want to learn more (and with visuals), 3b1b has a great video on the topic. Simply put, you just need a sample from a normal distribution that has the mean and variance that we want. This is fairly easy because the normal distribution is defined very conveniently. You can just take a random sample from the standard normal distribution (via a call to float mean = parallel * (min + max) / 2f;
int s = max - min + 1;
float sd = Math.sqrt(parallel * (s * s - 1) / 12f);
return random.nextGaussian() * sd + mean;Also I just looked and Mojang already has a ValueProvider that does this - |
|
That makes this all so much easier to both understand and implement. Thank you. |
|
I thought about it again and there is a small inconsistency in the math here. Our Glad to have helped you learn though :^) |
|
I way overthought my prior statement. If n is sufficiently large it's trivial to just pluck a value out of the normal distribution given a starting uniform distribution: static double randomNormal(int n, int min, int max) {
double mean = n * ((min + max) / 2.0);
double diff = max - min;
double sd = Math.sqrt(n * Math.pow(diff, 2) / 12.0);
double unbounded = (mean + sd * random.nextGaussian());
return Math.max(n * min, Math.min(n * max, unbounded));
}Technically works for any n but the variance at lower n will be significant. |
I'm going to implement this solution. It's almost the same as Kross's solution, I just more fully understand the explanation and justification this time now that it's seven months in the future. |

What
When Ranged Ingredients are run with Batch-Parallel counts, the current implementation simply but naively just multiplies the minimum and maximum bounds by the Batch-Parallel count. While this is fast and simple it means that Batch-Parallel Ranged Ingredient runs have a huge amount of variability and inconsistency in their input/output values, relative to running those same recipes without batching or parallels.
This PR alters the Batch-Parallel roll behavior to pull from a Gaussian Normal roll rather than a uniform random roll. This means that, especially at high Batch-Parallel counts, Ranged Ingredients will behave in a way that's spiritually more similar to the Guaranteed Rolls mechanic present in Chanced Ingredients, rather than being pure uniform random. (And still without running all of those individual rolls.
Implementation Details
Mojang has give us MarsagliaPolarGaussian and ClampedNormalInt provider classes to work with, meaning all that needs to be done by us is pipe in an existing IntProvider and hand them the modified limit bounds, mean, and standard deviation. However, because I don't know how basically anything in statistics works, I had to call several people (@krossgg ; @Storijophe ; @Zoryn4163 ) for help explaining the math.
AI Usage
Outcome
Batch-Parallel recipe runs on Ranged Ingredients will produce input/output amounts that are much more similar to if they had been run individually-consecutively that number of times.
How Was This Tested
Automated test suite and a lot of debug breakpoints to get roll values and see that they're actually looking Normal and not excessive or bounds-breaking. Most testing was done against #4884 ; but this PR is not blocked by that one.
Potential Compatibility Issues
IntProvider type replacement only applies to the default case of UniformInt (or another ClampedNormalInt) so any other provider types will be unaffected.