Jump to content
Torisu10

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

Recommended Posts

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...

Share this post


Link to post
Share on other sites

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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites

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;
}

 

Share this post


Link to post
Share on other sites

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, " "));
    }
}

 

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...