Jump to content

Is there a way to generate a random number within a range of negative to positive integers?


You are about to reply to a thread that has been inactive for 1578 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Posted

for example, i want to generate a random float between -2 and 2. it could be -1.7634 or 1.2341 after generation for example as long as its random. I can only seem to find documentation online that allows for positive range random float outputs...

Posted

a tiny thing to consider when doing negative randoms

in the example llFrand(4.0) the range is 0.0 to 3.99...

when we deduct 2.0 the range is -2.00 to +1.99.. So we can get -2.0 and we can't get +2.0

the generating of numbers is biased toward negative numbers when 0 is counted as neither positive or negative

example showing bias. In the 0 < 4 - 2 integer case then the set is [-2,-1,0,+1]

Posted (edited)
2 hours ago, Mollymews said:

the generating of numbers is biased toward negative numbers when 0 is counted as neither positive or negative

While this is true, zero is considered positive by the compiler. So unless you specifically go out of your way to ignore a randomly generated zero-result, it creates no bias.

2 hours ago, Mollymews said:

example showing bias. In the 0 < 4 - 2 integer case then the set is [-2,-1,0,+1]

In this example (which is about integers, not what the topic is actually about), there are an equal chance of positive or negative results.

The same applies to floats, in [-2.0, 1.99999] the "missing 0.00001" is the zero. an exact range of -2.0 to 2.0 would have a bias for positives.

But if you actually need the full range from -2.0 to 2.0 (for example, to use as input), that's a different problem. Even so, the chance of getting an exact min/max or even zero is very small.

Edited by Wulfie Reanimator
Posted (edited)

i had a play with llFrand(). Seems it never returns an absolute 0.0 in LSL Mono. Something I never knew before

so llFrand(4.0) can never be 0.0 or 4.0

so llFrand(4.0) - 2.0 can never be -2.0

which without counting every possible value would indicate that there are an equal number of positive and negative values on each side of the 0..0 result after subtracting 2.0

might need more testing to be sure, but I ran llFrand() for some quite few million times and never got an absolute 0.0

edit add. if llFrand() never produces absolute 0.0 then 4.0 - 2.0 gives an equal number of minus and positive values on each side of 0.0 because IEEE745 specifies -0.0 and +0.00 which can be used in LSL. However when we use them in a calculation then they both resolve to 0.0, giving us an odd count of accessible values. Which would indicate that the result 0.0 occurs twice for each one time of every other value

Edited by Mollymews
Posted (edited)
13 hours ago, Mollymews said:

i had a play with llFrand(). Seems it never returns an absolute 0.0 in LSL Mono. Something I never knew before

You were never wrong.

default
{
    state_entry()
    {
        float val;
        float min = 0.5;
        float max = 0.0;

        integer loop;
        while(++loop)
        {
            val = llFrand(0.5);

            if (val > max) max = val;
            if (val < min) min = val;
            if (loop >= 1000000) // million iterations
            {
                llOwnerSay((string)[min, ", ", max]);
                if (min == 0.0) llOwnerSay("Zero!");
                if (max == 0.5) llOwnerSay("Hero!");
                loop = 0;
            }
        }
    }
}
llFrand: 0.000000, 0.500000
llFrand: 0.000000, 0.500000
llFrand: 0.000000, 0.500000
llFrand: 0.000000, 0.500000
llFrand: 0.000000, 0.500000
llFrand: 0.000000, 0.500000
llFrand: 0.000000, 0.500000
llFrand: Zero!

It took 7 million iterations on a much smaller range, but it does return zero as according to the wiki. Same code with (0.0, 4.0] range:

llFrand: 0.000023, 3.999999
llFrand: 0.000002, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: 0.000000, 3.999999
llFrand: Zero!

Got zero within 10 million iterations. If we add a bit more code, we can see a (very rough) approximation of what value the min actually has. Sometimes the semi-random sequence just doesn't catch a zero for a long while. For example, it took 73 million iterations to get an exact zero (within the 0.0 to 0.5 range) at one point.

Edited by Wulfie Reanimator
  • Thanks 1
Posted

A SL float has a precision of - lets say - 7 digits. So you can expect about 40 million possible results in a [0,4] range. The implementation of the random number generator may or may not lower that number but it's still high. So what to expect how often a single specific result will appear?

You can lower the precision to - for example - 3 digits by rounding. In a range of 0.00 to 4.00 there are 400 possible results and the chance to see a specific one will be much higher.

  • Like 1
Posted (edited)
5 hours ago, Nova Convair said:

A SL float has a precision of - lets say - 7 digits. So you can expect about 40 million possible results in a [0,4] range. The implementation of the random number generator may or may not lower that number but it's still high. So what to expect how often a single specific result will appear?

You can lower the precision to - for example - 3 digits by rounding. In a range of 0.00 to 4.00 there are 400 possible results and the chance to see a specific one will be much higher.

It's a bit higher than that, but that was the point I was trying to make. Floats aren't based on the digits we use, but the exponent/mantissa representation.

Between 0.0 and 0.5, there are 1'056'964'608 valid floats. That's over one Billion.

From 0.5 to 1.0, you only have 8'388'608 more, so about 1.065 billion values between 0.0 and 1.0.

From 1.0 to 4.0, you only have 16'777'215 more, so about 1.082 billion values between 0.0 and 4.0.

You can refer to this and check the numbers yourself:
https://www.h-schmidt.net/FloatConverter/IEEE754.html

But despite this discrepancy in where the representable values "congregate," llFrand return an even distribution of expected values. 

Edited by Wulfie Reanimator
Posted

i looked up the source code for the Mono pseudo-random number generator. Out-of-the box it is a Complementary Multiply With Carry generator designed by George Marsaglia who is/was a guru of these things. Not sure if Linden use this or have their made their own tho

source is here: https://github.com/mono/mono/blob/master/mcs/class/Mono.C5/C5/Random.cs

info about CMWC is here:  https://en.wikipedia.org/wiki/Multiply-with-carry_pseudorandom_number_generator

 

Posted

I don't know the quality of the LSL random number generator - never tested that.

But for one case I was absolutely not satisfied with the result. Not enough "jumpyness".

So I made this one for random integers. I don't know how the quality really is nor do I care. The goal was that it looks random.

string seed = "he65FGt2";
integer irnd(integer max) {
	++max;
	seed = llMD5String(seed,0);
	return (integer)("0x"+llGetSubString(seed,0,6))%max;
}

 

Posted

a number of research examinations of PRNG show that the typical implementation (float)rand() / (float)(MAX_RAND+1) when nearer to 0.0 returns a wider distribution of values (up to 32 bits), and when nearer to 1.0 returns a narrower distribution of values (up to 24 bits) due to rounding.  Not sure if this method is used by LSL (it is tho used in the standard Mono library)

the total count of values returned below and above 0.5 is the same. Is just that there are more returnable individual values below 0.5 than above 0.5 overall

as a exercise we can ameliorate this a bit by confining the full range [0.0 < 1.0] to 24 bits

a example of doing this with a lfsr algorithm:

list counts;

integer seed = 987769331; // some large integer value != 0. preferably prime with some
                          // equal-ish number of 0 and 1 bits in some uneven distribution
init_frand()
{   // change out 987769331 to some large number as above as it suits your app
    seed = 987769331 ^ ((integer)llFrand(65536.0) << 16 | (integer)llFrand(65536.0));
    if (!seed) seed = 987769331;    
}

float frand(float mag)
{
    seed = (seed >> 1) ^ (-(seed & 1) & 3489660929);
    return (float)(seed & 16777215) / 16777216.0 * mag;
}

default
{
    state_entry()
    {
        init_frand();
        integer c;
        do
        {
            float r = frand(1.0);
            integer i = (integer)(r * 10);
            counts = llListReplaceList(counts, [llList2Integer(counts, i)+1], i, i);
        }
        while (++c < 10000);
        llOwnerSay(llDumpList2String(counts, " "));
    }
}

 

  • 2 weeks later...
Posted (edited)
On 7/29/2020 at 2:28 AM, Torisu10 said:

for example, i want to generate a random float between -2 and 2. it could be -1.7634 or 1.2341 after generation for example as long as its random. I can only seem to find documentation online that allows for positive range random float outputs...

Why not do:

llFrand(2.0) * llList2Integer(llListRandomize([1,-1], 1), 0);

Nevermind.. that only works when to range is equidistant from zero. Can I delete this?

Edited by DoteDote Edison
Posted
4 hours ago, DoteDote Edison said:

Can I delete this?

can edit and leave it blank

can also edit and just type in Nevermind!  but then somebody might come along and post a Nevermind! script as a answer. peeples have been known to do that on here 😸

You are about to reply to a thread that has been inactive for 1578 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...