I would like to add a kind of ripple to an array of known values of type double. I point that out because Random.Next / Random.NextDouble() behave different.
Choose a random number (symmetric about zero) for each step between successive numbers. Then, add it to the first, and subtract it from the second:
for(int i=1; i<length; i++) {
dx = (rng.NextDouble() - 0.5) * scale;
arr[i-1] += dx;
arr[i] -= dx;
}
This should ensure that the sum of the array stays the same (modulo floating point error), while the array elements are all modified.
One way you could do this is to generate N random numbers between 0 and 1 (exclusive). Sum them. Then divide each number by the sum. You now have a list of N random numbers that sum to 1. Now, multiply each of those numbers by your desired sum to get the numbers that will go into your final array.
If you want your values to be +/- some percentage, then use Random.Next
to generate the random numbers within some range and sum them. Then divide by the total to get the list of numbers that sums to 1. The final step is the same.
Below is my solution.
Basically, it "jitters" each value by some specified percentage, and then checks the difference between the original total and the "jittered" total. In order to make the final total match the original total, it adds a flat amount to each "jittered" value.
I feel like this is not a great solution from a mathematical perspective, because I think that adding the flat amount to each value will probably distort the true percentage of abberation for each value. There is probably a more mathematically correct way to apply the remainder across the set of values in such a way as to preserve the intended percentage of abberation, but I imagine that doing so would require several passes, whereas this solution completes in a set number of passes.
// prepare data
double[] values = new double[20];
for (int i = 0; i < values.Length; i++)
{
values[i] = 40.0;
}
// get the original total
double originalTotal = 0.0;
for (int i = 0; i < values.Length; i++)
{
originalTotal += values[i];
}
// specify an abberation percentage
double x = 0.05;
// jitter each value +/- the abberation percentage
// also capture the total of the jittered values
Random rng = new Random();
double intermediateTotal = 0.0;
for (int i = 0; i < values.Length; i++)
{
values[i] += values[i] * (rng.NextDouble() - 0.5) * (2.0 * x);
intermediateTotal += values[i];
}
// calculate the difference between the original total and the current total
double remainder = originalTotal - intermediateTotal;
// add a flat amount to each value to make the totals match
double offset = remainder / values.Length;
for (int i = 0; i < values.Length; i++)
{
values[i] += offset;
}
// calculate the final total to verify that it matches the original total
double finalTotal = 0.0;
for (int i = 0; i < values.Length; i++)
{
finalTotal += values[i];
}
Another method would be to loop through the array and perturb by percentage value. Once complete, calculate how far off the total is, and add the overage amount spread equally throughout all the numbers. Here's some sample code:
var test = Enumerable.Repeat<double>(40, 100).ToArray();
var percent = 0.5d;
var rand = new Random();
var expectedTotal = test.Sum();
var currentTotal = 0d;
var numCount = test.Count();
for (var i = 0; i < numCount; i++)
{
var num = test[i];
var range = num * percent * 2;
var newNum = num + (rand.NextDouble() - 0.5) * (range);
currentTotal += newNum;
test[i] = newNum;
}
var overage = (expectedTotal - currentTotal);
for (var i = 0; i < numCount; i++)
test[i] += overage / numCount;