Could anyone give me a hint on how to generate "smooth" random numbers?
Here's what I mean by smooth:
The random numbers shall be used in a game, e.g. for wind direction and strength (does anyone remember goood old "Worms"?). Of course setting random numbers for those values every second or so would look awfully choppy.
I would rather have some kind of smooth oscillation in a given value range. Sort of like a sine wave but much more random.
Does anyone get what I'm after? ;-)
Any ideas on how to achieve this kind of behavior would be appreciated.
If you want the delta (change) to be small, just generate a small random number for the delta.
For example, instead of:
windspeed = random (100) # 0 thru 99 inclusive
use something like:
windspeed = windspeed - 4 + random (9) # -4 + 0..8 gives -4..4
if windspeed > 99: windspeed = 99
elif windspeed < 0: windspeed = 0
That way, your wind speed is still kept within the required bounds and it only ever changes gradually.
This will work for absolute values like speed, and also for direction if the thing you're changing gradually is the angle from a fixed direction.
It can pretty well be used for any measurement.
Alternatively, if you want to ensure that the windspeed changes with a possibly large delta, but slowly, you can generate your target windspeed as you currently do but move gradually toward it:
windspeed = 50
target = windspeed
while true:
# Only set new target if previous target reached.
if target == windspeed:
target = random (100)
# Move gradually toward target.
if target > windspeed:
windspeed = windspeed + max (random (4) + 1, target - windspeed)
else:
windspeed = windspeed - max (random (4) + 1, target - windspeed)
sleep (1)
Perlin (or better simplex) noise would be the first method that comes to mind when generating smoothed noise. It returns a number between 1 and -1, which will add or subtract from the current value. You can multiple that to make it seem less subtle or better yet... make the lowest wind value -1 and highest wind value 1.
Then simply have a seeder as a counter (1,2,3... etc) as the perlin/simplex input keep the values 'smooth'.
I created a new version of a smooth random number. The idea is that our random number is going to be within limits = [average - oscillation, average + oscillation], and will change everytime [-varianciance, +variance].
But, if it reaches the limits, our variance is going to be reduce.
E.g. numbers from [0, 100], with variance of 10. If the current value = 8, then the variance will be [0, 18]
python code:
def calculate_smooth_random(current_value, average, oscillation, variance):
max_value = average + oscillation
min_value = average - oscillation
max_limit = min(max_value, current_value + variance)
min_limit = max(min_value, current_value - variance)
total_variance = max_limit - min_limit
current_value = min_limit + random.random() * total_variance
print("current_value: {}".format(current_value))
return current_value
Image for distribution with values:
average: 20
oscillation: 10
variance: 5
Related
This question already has answers here:
Get random double (floating point) value from random byte array between 0 and 1 in C#?
(6 answers)
random double in four different interval
(2 answers)
C#: Random.NextDouble and including borders of the custom interval
(4 answers)
Closed 1 year ago.
The following code generates a double number in the range [0,1) which means that 1 is exclusive.
var random = new Random();
random.NextDouble();
I am looking for some smart way to generate a random double number in the range [0,1]. It means that 1 is inclusive. I know that the probability of generating 0 or 1 is really low, but imagine that I want to implement a correct mathematical function that requires from me the inclusive limits. How can I do it?
The question is: What is the correct way of generating random in the range [0,1]. If there is no such way, I would love to learn it also.
After taking a shower, I have conceived of a potential solution based on my understanding of how a random floating point generator works. My solution makes three assumptions, which I believe to be reasonable, however I can not verify if these assumptions are correct or not. Because of this, the following code is purely academic in nature, and I would not recommend its use in practice. The assumptions are as follows:
The distribution of random.NextDouble() is uniform
The difference between any two adjacent numbers in the range produced by random.NextDouble() is a constant epsilon e
The maximum value generated by random.NextDouble() is equal to 1 - e
Provided that those three assumptions are correct, the following code generates random doubles in the range [0, 1].
// For the sake of brevity, we'll omit the finer details of reusing a single instance of Random
var random = new Random();
double RandomDoubleInclusive() {
double d = 0.0;
int i = 0;
do {
d = random.NextDouble();
i = random.Next(2);
} while (i == 1 && d > 0)
return d + i;
}
This is somewhat difficult to conceptualize, but the essence is somewhat like the below coin-flipping explanation, except instead of a starting value of 0.5, you start at 1, and if at any point the sum exceeds 1, you restart the entire process.
From an engineering standpoint, this code is a blatant pessimization with little practical advantage. However, mathematically, provided that the original assumptions are correct, the result will be as mathematically sound as the original implementation.
Below is the original commentary on the nature of random floating point values and how they're generated.
Original Reply:
Your question carries with it a single critical erroneous assumption: Your use of the word "Correct". We are working with floating point numbers. We abandoned correctness long ago.
What follows is my crude understanding of how a random number generator produces a random floating point value.
You have a coin, a sum starting at zero, and a value starting at one half (0.5).
Flip the coin.
If heads, add the value to the sum.
Half the value.
Repeat 23 times.
You have just generated a random number. Here are some properties of the number (for reference, 2^23 is 8,388,608, and 2^(-23) is the inverse of that, or approximately 0.0000001192):
The number is one of 2^23 possible values
The lowest value is 0
The highest value is 1 - 2^(-23);
The smallest difference between any two potential values is 2^(-23)
The values are evenly distributed across the range of potential values
The odds of getting any one value are completely uniform across the range
Those last two points are true regardless of how many times you flip the coin
The process for generating the number was really really easy
That last point is the kicker. It means if you can generate raw entropy (i.e. perfectly uniform random bits), you can generate an arbitrarily precise number in a very useful range with complete uniformity. Those are fantastic properties to have. The only caveat is that it doesn't generate the number 1.
The reason that caveat is seen as acceptable is because every other aspect of the generation is so damned good. If you're trying to get a high precision random value between 0 and 1, chances are you don't actually care about landing on 1 any more than you care about landing on 0.38719, or any other random number in that range.
While there are methods for getting 1 included in your range (which others have stated already), they're all going to cost you in either speed or uniformity. I'm just here to tell you that it might not actually be worth the tradeoff.
If you want uniform distribution, it‘s harder than it seams. Look how NextDouble is implemented.
There’re ways to produce uniformly distributed numbers in arbitrary intervals, an easy one is selectively discarding some of the generated values. Here’s how I would do that for your problem.
/// <summary>Utility function to generate random 64-bit numbers</summary>
static ulong nextUlong( Random rand )
{
Span<byte> buffer = stackalloc byte[ 8 ];
rand.NextBytes( buffer );
return BitConverter.ToUInt64( buffer );
}
/// <summary>Generate a random number in [ 0 .. +1 ] interval, inclusive.</summary>
public static double nextDoubleInclusive( Random rand )
{
// We need uniformly distributed integer in [ 0 .. 2^53 ]
// The interval contains ( 2^53 + 1 ) distinct values.
// The complete range of ulong is [ 0 .. 2^64 - 1 ], 2^64 distinct values.
// 2^64 / ( 2^53 + 1 ) is about 2047.99, here's why
// https://www.wolframalpha.com/input/?i=2%5E64+%2F+%28+2%5E53+%2B+1+%29
const ulong discardThreshold = 2047ul * ( ( 1ul << 53 ) + 1 );
ulong src;
do
{
src = nextUlong( rand );
}
while( src >= discardThreshold );
// Got uniformly distributed value in [ 0 .. discardThreshold ) interval
// Dividing by 2047 gets us a uniformly distributed value in [ 0 .. 2^53 ]
src /= 2047;
// Produce the result
return src * ( 1.0 / ( 1ul << 53 ) );
}
Usually, knowing that NextDouble() has a finite range, we multiply the value to suit the range we need.
For this reason it is common to create your own wrapper to produce the next business value when the built in logic does not meet your requirements.
For this particular example, why not just post process the result, when zero get the value from Next(0,2)
public static double NextInclude1(this Random rand = null)
{
rand = rand ?? new Random();
var result = rand.NextDouble();
if (result == 0) result = rand.Next(0,2);
return result;
}
You can implement your own bias for 0 or 1 as a result by varying the comparison to zero, if you do that though you are likely to create an exclusion range, so after the comparison you may need to return the next NextDouble()
public static double NextInclude1(this Random rand = null)
{
rand = rand ?? new Random();
var result = rand.NextDouble();
if (result < 0.2)
result = rand.Next(0,2);
else
result = rand.NextDouble();
return result;
}
This particular example results in an overall bias for 0, it's up to you to determine the specific parameters that you would accept, overall NextDouble() is your base level tool for most of your custom Random needs.
The Random.Next method returns an integer value in the range [0..Int32.MaxValue) (the exclusive range-end is denoted by the right parenthesis). So if you want to make the value 1.0 a possible result of the NextDouble method (source code), you could do this:
/// <summary>Returns a random floating-point number that is greater than or equal to 0.0,
/// and less than or equal to 1.0.</summary>
public static double NextDoubleInclusive(this Random random)
{
return (random.Next() * (1.0 / (Int32.MaxValue - 1)));
}
This fiddle verifies that the expression (Int32.MaxValue - 1) * (1.0 / (Int32.MaxValue - 1)) evaluates to 1.0.
This definitely works, You can check the distribution here https://dotnetfiddle.net/SMMOrM
Random random = new Random();
double result = (int)(random.NextDouble() * 10) > 4
? random.NextDouble()
: 1 - random.NextDouble();
Update
Agree with Snoot, this version would return 0 and 1 twice less often as other values
Easy, you can do this
var random = new Random();
var myRandom = 1 - Math.Abs(random.NextDouble() - random.NextDouble());
update
Sorry, this won't generate normal distribution of results, where they will tend to be higher ones, close to 1.
I need to generate a number of float numbers with approximately normal distribution over a range from 0 to a specific ceiling.
I've searched on stack overflow and found similar questions for other languages, but none for .net core.
internal List<float> function(int ceiling, int repetitions)
{
List<float> list = new List<float>();
for (int i = 0; i<= repetitions;i++)
{
list.Add(Random.nextFloat() * ceiling);
}
return list;
}
I expect the function to return a list of random positive floatnumbers, in range from 0 to a given ceiling with at least approximately normal distribution.
If you're seeking something "at least approximately normal" with bounds at 0 and ceiling, summing three uniforms will yield a result which is symmetric, bell-shaped, and bounded, and can subsequently be rescaled to any range you wish. I'm not a C# programmer, but if you have a PRNG named prng:
(prng.NextDouble() + prng.NextDouble() + prng.NextDouble()) * ceiling / 3.0
will yield a result in the range [0, ceiling]. Here's what 100,000 observations look like with ceiling set to 3:
You can generalize this to sum k uniforms and replace the 3 by k in the divisor for the rescaling. The larger k is, the closer this will get to normality by the central limit theorem, but since you don't seem to be asking for actual normals (which don't have a bounded range anyway) that quickly gets into diminishing returns.
Note that while this approach uses multiple uniforms, it is computationally relatively efficient because it avoids transcendental functions.
Well, you could use truncated normal together with taking absolute values to make result positive.
Along the lines
double R = 10.0; // upper value of the truncated normal
var seed = 31234567;
Random rng = new Random( seed );
double u1 = rng.NextDouble();
double u2 = rng.NextDouble();
double phi = 2.0*Math.PI*u2;
double r = Math.Sqrt(-2.0*Math.Log(1.0 - u1*(1.0 - Math.Exp(-R*R/2.0))));
return new Tuple<double,double>(Math.Abs(r*Math.Cos(phi)), Math.Abs(r*Math.Sin(phi)));
Code above shall return couple of sampled values in the interval from 0 to R which looks like truncated gaussian. You could compare with Box-Muller for standard gaussain sampling
I have a trackbar associated with a picture box where I am drawing an image based on the selected zoom factor. The range is from 1% to 1,000% so the lower you slide it, the faster it appears to zoom out.
This is expected but not desired. Is there a way to scale interpret the slider values so that zooming appears more natural to the user, specially in the < 50% range.
This is easily done:
myTrackBar.Minimum = 0;
myTrackBar.Maximim = 3000;
...
public double RealValue
{
get
{
var trackPos = myTrackBar.Value;
return Math.Pow(10.0, trackPos / 1000.0);
}
set
{
var logValue = Math.Log10(value) * 1000;
myTrackBar.Value = (int) logValue;
}
}
To understand how this works, consider your range - 1 to 1000, or expressed as powers of 10 it is 1e0 to 1e3. Hence if we give the track bar a range from 0 to 3 and raise 10 to the value, we get a nice exponential set of values, just like you want.
But if we set the range to 0..3 we could only select from 4 different values: 0, 1, 2, 3 which would translate into 1, 10, 100 and 100 respectively.
To give us values inbetween, we simply multiply the range by a thousand, giving us 3001 different values that the track bar can keep track off, and then divide the trackbar's value by a thousand.
I am struck in a tricky situation where I need to calculate the number of combinations to form 100 based on different factors.
Those are
Number of combinations
Multiplication factor
distance
Sample input 1: (2-10-20)
It means
list the valid 2 way combination to form 100.
the distance between the combination should be less than or equal to 20.
And all of the resultant combination must be divisible by the given multiplication factor 10
Output will be
[40,60]
[50,50]
[60,40]
here [30,70],[20,60] are invalid because the distance is above 20.
Sample Input 2: [2-5-20]
[40,60]
[45,55]
[50,50]
[55,45]
[60,40]
I would really appreciate if you guided me to the right direction.
Cheers.
I hope it's not a homework problem!
def combinations(n: Int, step: Int, distance: Int, sum: Int = 100): List[List[Int]] =
if (n == 1)
List(List(sum))
else
for {
first <- (step until sum by step).toList
rest <- combinations(n - 1, step, distance, sum - first)
if rest forall (x => (first - x).abs <= distance)
} yield first :: rest
If you need to divide 100 over 2 with a maximum distance of N, the lowest value in the combination is
100 / 2 - N / 2
If you need to divide 100 over 3 values with a maximum distance of N, this becomes more tricky. The average of the 3 values will be 100/3, but if one of them is much lower than this average, than the other can only be slightly bigger than this average, meaning that the minimum value is not the average minus the maximum distance divided by two, but probably
100 / 3 - 2N / 3
In general with M values, this becomes
100 / M - (M-1)N / M
Which can be simplified to
(100 - (M-1)N) / M
Similarly we can calculate the highest possible value:
(100 + (M-1)N) / M
This gives you a range for first value of your combination.
To determine the range for the second value, you have to consider the following constraints:
the distance with the first value (should not be higher than your maximum distance)
can we still achieve the sum (100)
The first constraint is not a problem. The second is.
Suppose that we divide 100 over 3 with a maximum distance of 30 using multiples of 10
As calculated before, the minimum value is:
(100 - (3-1)30) / 3 --> 13 --> rounded to the next multiple of 10 --> 20
The maximum value is
(100 + (3-1)30) / 3 --> 53 --> rounded to the previous multiple of 10 --> 50
So for the first value we should iterate over 20, 30, 40 and 50.
Suppose we choose 20. This leaves 80 for the other 2 values.
Again we can distribute 80 over 2 values with a maximum distance of 30, this gives:
Minimum: (80 - (2-1)30) / 2 --> 25 --> rounded --> 30
Maximum: (80 + (2-1)30) / 2 --> 55 --> rounded --> 50
The second constraint is that we don't want a distance larger than 30 compared with our first value. This gives a minimum of -10 and a maximum of 50.
Now take the intersection between both domains --> 30 to 50 and for the second value iterate over 30, 40, 50.
Then repeat this for the next value.
EDIT:
I added the algorithm in pseudo-code to make it clearer:
calculateRange (vector, remainingsum, nofremainingvalues, multiple, maxdistance)
{
if (remaingsum==0)
{
// at this moment the nofremainingvalues should be zero as well
// found a solution
print vector
return;
}
minvalueaccordingdistribution = (remainingsum-(nofremainingvalues-1)*maxdistance)/nofremaingvalues;
maxvalueaccordingdistribution = (remainingsum+(nofremainingvalues-1)*maxdistance)/nofremaingvalues;
minvalueaccordingdistance = max(values in vector) - maxdistance;
maxvalueaccordingdistance = min(values in vector) + maxdistance;
minvalue = min (minvalueaccordingdistribution, minvalueaccordingdistance);
maxvalue = max (minvalueaccordingdistribution, minvalueaccordingdistance);
for (value=minvalue;value<=maxvalue;value+=multiple)
{
calculaterange (vector + value, remainingsum - value, nofremainingvalues-1, multiple, maxdistance);
}
}
main()
{
calculaterange (emptyvector, 100, 2, 20);
}
Why can't you use a brute force approach with few optimization? For example, say
N - Number of combinations
M - Multiples
D - Max possible Distance
So possible values in combinations can be M, 2M, 3M and so on. You need to generate this set and then start with first element from set and try to find out next two from choosing values from same set (provided that they should be less than D from first/second value).
So with i/p of 3-10-30 would
Create a set of 10, 20, 30, 40, 50, 60, 70, 80, 90 as a possible values
Start with 10, choice for second value has to be 20, 30, 40, 50 (D < 30)
Now choose second value from set of 20, 30, 40, 50 and try to get next value and so on
If you use a recursion then solution would become even simpler.
You have to find N values from a
list of possible values within MIN &
MAX index.
So try first value at
MIN index (to MAX index). Say we
have chosen value at X index.
For every first value, try to find
out N-1 values from the list where MIN =
X + 1 and MAX.
Worst performance will happen when M = 1 and N is sufficiently large.
Is the distance between all the additive factors, or between each of them? For example, with 3-10-20, is [20-40-60] a valid answer? I'll assume the latter, but the solution below can be modified pretty trivially to work for the former.
Anyway, the way to go is to start with the most extreme answer (of one sort) that you can manage, and then walk the answers along until you get to the other most extreme.
Let's try to place numbers as low as possible except for the last one, which will be as high as possible (given that the others are low). Let the common divisor be d and divide 100 by it, so we have S = 100/d. This quantizes our problem nicely. Now we have our constraint that spacing is at most s, except we will convert that to a number of quantized steps, n = s/d. Now assume we have M samples, i1...iM and write the constraints:
i1 + i2 + i3 + ... + iM = S
0 <= i1 <= n
0 <= i2 <= n
. . .
0 <= iM <= n
i1 <= i2
i2 <= i3
. . .
i(M-1) <= iM
We can solve the first equation to get iM given the others.
Now, if we make everything as similar as possible:
i1 = i2 = ... = iM = I
M*I = S
I = S/M
Very good--we've got our starting point! (If I is a fraction, make the first few I and the remainder I+1.) Now we just try to walk each variable down in turn:
for (i1 = I-1 by -1 until criteria fails)
sum needs to add to S-i1
i2 >= i1
i2 <= i1 +n
solve the same problem for M-1 numbers adding to S-i1
(while obeying the above constraint on i2)
Well, look here--we've got a recursive algorithm! We just walk through and read off the answers.
Of course, we could walk i1 up instead of down. If you need to print off the answers, may as well do that. If you just need to count them, note that counting up is symmetric, so just double the answer you get from counting down. (You'll also have a correction factor if not all values started the same--if some were I and some were I+1, you need to take that into account, which I won't do here.)
Edit: If the range is what every value has to fit within, instead of all the
0 <= i1 <= n
conditions, you have
max(i1,i2,...,iM) - min(i1,i2,...,iM) <= n
But this gives the same recursive condition, except that we pass along the max and min of those items we've already selected to throw into the mix, instead of adding a constraint on i2 (or whichever other variable's turn it is).
Input:
(2-10-20)
Divide the number by param 1
(50,50)
2 Check whether the difference rule allows this combination. If it hurts the rule, then STOP, if it allows, then add this and it's combinations to the result list
For example: abs(50-50)<20, so it is ok
3 Increase the the first value by param 2, decrease the second value by param 2
Go 2. point
I'm currently implementing a software that measures certain values over time. The user may choose to measure the value 100 times over a duration of 28 days. (Just to give an example)
Linear distribution is not a problem, but I am currently trying to get a logarithmical distribution of the points over the time span.
The straight-forward implementation would be to iterate over the points and thus I'll need an exponential function. (I've gotten this far!)
My current algorithm (C#) is as follows:
long tRelativeLocation = 0;
double tValue;
double tBase = PhaseTimeSpan.Ticks;
int tLastPointMinute = 0;
TimeSpan tSpan;
for (int i = 0; i < NumberOfPoints; i++)
{
tValue = Math.Log(i + 1, NumberOfPoints);
tValue = Math.Pow(tBase, tValue);
tRelativeLocation = (long)tValue;
tSpan = new TimeSpan(tRelativeLocation);
tCurrentPoint = new DefaultMeasuringPointTemplate(tRelativeLocation);
tPoints.Add(tCurrentPoint);
}
this gives me a rather "good" result for 28 days and 100 points.
The first 11 points are all at 0 seconds,
12th point at 1 sec,
20th at 50 sec,
50th at 390 min,
95th at 28605 mins
99 th at 37697 mins (which makes 43 hours to the last point)
My question is:
Does anybody out there have a good idea how to get the first 20-30 points further apart from each other, maybe getting the last 20-30 a bit closer together?
I understand that I will eventually have to add some algorithm that sets the first points apart by at least one minute or so, because I won't be able to get that kind of behaviour into a strictly mathematical algorithm.
Something like this:
if (((int)tSpan.TotalMinutes) <= tLastPointMinute)
{
tSpan = new TimeSpan((tLastPointMinute +1) * 600000000L);
tRelativeLocation = tSpan.Ticks;
tLastPointMinute = (int)tSpan.TotalMinutes;
}
However, I'd like to get a slightly better distribution overall.
Any cool ideas from you math cracks out there would be greatly appreciated!
From a practical point of view, the log function squeezes your time point near the origin already. A power function squeezes them even more. How about simple multiplication?
tValue = Math.Log(i + 1, NumberOfPoints);
tValue = tBase * tValue;
Another way to flatten the curve is start farther from the origin.
for (int i = 0; i < NumberOfPoints; i++)
{
tValue = Math.Log(i + 10, NumberOfPoints + 9);
The range of tvalue is still 0 to 1.
How about this to have a minimum space of 1 second at the beginning?
double nextTick = 0;
for (int i = 0; i < NumberOfPoints; i++)
{
tValue = Math.Log(i + 1, NumberOfPoints);
tValue = Math.Pow(tBase, tValue);
if (tValue < nextTick) tValue = nextTick;
nextTick++;
The distribution curve you choose depends on what you're measuring.
A straight line, a sine wave, a polynomial or exponential curve may individually be the best distribution curve for a given set of measurements.
Once you've decided on the distribution curve, you calculate the missing data points by calculating the y value for any given time value (x value), using the mathematical formula of the curve.
As an example, for a straight line, all you need is one data point and the slope of the line. Let's say at time 0 the measured value is 10, and the measurement goes up by 2 every minute. The formula would by y = 2 * x + 10. if we wanted to calculate the measurement when x = 5 (minutes), the formula gives us a measurement of 20.
For a logarithmic curve, you'd use a logarithm formula. For simplicity, let's say that the actual measurements give us a formula of y = 2 ** x + 12; You plug in the time values (x values) you want to calculate, and calculate the measurements (y values).
Realize that you are introducing calculation errors by calculating data points instead of measuring. You should mark the calculated data points in some manner to help the person reading your graph differentiate them from actual measurements.
I am not exactly sure what you are trying to do, your code does not seem to match your example (it could be that I am screwing up the arithmetic). If you want your samples to have a minimum separation of 1 sec, and each point at a location of x times the last point (except for the first) then you want to find x such that x^(n - 1) = span. This is just x = exp(log(span) / (n - 1)). Then your points would be at x^i for(i = 0; i < n; i++)