Page 2 of 2

Re: Another Question About 0.9's RNG

Posted: Fri Dec 20, 2013 6:31 pm
by bartbes
If determinism is important, using random number generator objects, you can have multiple pieces of codes working with their own seeds, and their own generators. You could even do awesome things like forking and then choosing one of the forks. In general though, there's occasions where it's useful to have one generator not affect the other (because they're different parts of the code, for example), and this API allows for that.

Re: Another Question About 0.9's RNG

Posted: Fri Dec 20, 2013 7:25 pm
by Helvecta
Aha! Gotcha. So for most purposes love.math.random will suffice.
Helvecta wrote:Maybe it's because of the way I'm testing it, but I don't think that should matter.
I was wrong; it was the way I was testing it. Maybe forcing the program to assert a pseudo-random number immediately didn't give the generator proper time to engage. I figure this because actually setting up an app that displayed random numbers worked just fine!

Image


Thanks for all the help, everyone. It's much appreciated! :awesome:

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 1:53 am
by Hexenhammer
Try this:

Code: Select all

    rng = love.math.newRandomGenerator()
    rng:setSeed(tonumber(tostring(os.time()):reverse():sub(1,6)) )
    rng:random();rng:random();rng:random();
    randomNumber = rng:random(1,100)
 
There are two problems with your original code:

1. The seed value. os.time() is almost identical between calls, and unfortunately the most significant bits are identical . The strange vodoo done to the os.time() return value basically makes the bits which change more often the most significant ones.

2. As bartbes already explained, the first numbers returned are not very "random" (I know that is pedantically incorrect phrasing).

So there is your solution, massage the os.time() return value and throw the first few numbers away. But here comes the kicker...

.. you don't need to do any of that now that LÖVE uses LuaJIT. This

Code: Select all

    math.randomseed(os.time())
    randomNumber = math.random(1,100)
..will work just fine with LuaJIT's RNG. The new LÖVE RNG is pretty pointless now given that LuaJIT already addresses all common issues of the vanilla Lua one.

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 2:01 am
by slime
Hexenhammer wrote: .. you don't need to do any of that now that LÖVE uses LuaJIT.
[..]
The new LÖVE RNG is pretty pointless now given that LuaJIT already addresses all common issues of the vanilla Lua one.
LÖVE can use Lua 5.1 or Lua 5.2-compatibility or LuaJIT 2.0 or LuaJIT 2.1 - it's just distributed with LuaJIT 2.0 by default. If you make a LÖVE game and expect other people running their own copies of LÖVE to be able to run it with the results you expect, you might not want to rely on a particular implementation of the Lua math.random, especially since you don't need to.

Not only that, but you can have completely separate RNGs using different seeds at the same time with LÖVE's RandomGenerator objects, which is something LuaJIT doesn't provide at all without completely separate Lua states.

love.math.getRandomSeed and RNG:getSeed are useful as well.

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 6:26 am
by Helvecta
bartbes wrote:You could even do awesome things like forking and then choosing one of the forks.
Since this thread is still kicking, would you mind giving an example of what you mean by this? Or a resource that explains what this means in greater detail? I think Jasoco mentioned something that sounds similar, but I'd like to make sure I know what you mean.
Hexenhammer wrote:

Code: Select all

    math.randomseed(os.time())
    randomNumber = math.random(1,100)
..will work just fine with LuaJIT's RNG. The new LÖVE RNG is pretty pointless now given that LuaJIT already addresses all common issues of the vanilla Lua one.
I know Slime just replied about this but I thought it's worth pointing out that he also stated that "The love.math.random function exists partly because math.random's implementation is different on different operating systems. love.math.random will give the same results regardless of OS, given the same inputs (if the random seed/state is the same), but math.random will not."

Somebody else on the forums (I can't find what the exact response was) noted the implications of having RNGs that are dependent on OS by giving an example of generating a map on multiple OSs, and noting that - even though the same function is used to generate random data - the maps would end up different for people on different OSs because of math.random. Avoiding that seems to be the primary reason for making love.math.random.

This is coming from someone that barely knows how to use an RNG though, so take my rambling with a grain of salt.
Hexenhammer wrote:1. The seed value. os.time() is almost identical between calls, and unfortunately the most significant bits are identical . The strange vodoo done to the os.time() return value basically makes the bits which change more often the most significant ones.
So, hypothetically speaking, let's say I use a RNG to fetch 3 random numbers, with the following seeds: 1, 2, and 3. For the sake of the example, the returned numbers will be 3, 6, and 1. Then, I try to use the same RNG to fetch 3 random numbers again, with the seeds being 2, 3, and 4. Would I get completely different random numbers? Or would I get 6, 1, and a new random number?

I ask this because one of Plu's posts makes it seem like the seed is the insertion point in a list of numbers, from which a number in this list is returned based on the seed. If that's the case, I could see why it's important to make the seed as random as the numbers returned from said list, and almost explains why you want to reverse os.time.

Why do I say almost? Because now I'm confused about what Robin posted a while ago. In Robin's post, gen:setSeed(os.time()) is placed outside of any function, yet the numbers are still always different! Since os.time is defined at the beginning, shouldn't it be a static value at that point (whatever os.time is when :setSeed is called)? And even if os.time as the argument is somehow dynamic (seriously, if you guys say that os.time is a dynamic argument I'm going to dissolve into the floor), shouldn't the returned numbers be the same "random" number returned over and over until os.time changes (that is, until a second has passed)?

Robin even specifically stated that I shouldn't be setting the seed inside of the function (and testing it myself has confirmed it):
Robin wrote:A function that returns or uses a pseudo-random number should not mess with the seed.
If the seed is an insertion point like I figure it is, why is it bad to change it wherever you want?

MY MIND IS IMPLODING. :death:

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 10:21 am
by bartbes
Helvecta wrote:
bartbes wrote:You could even do awesome things like forking and then choosing one of the forks.
would you mind giving an example of what you mean by this?
If you have a game where moving through time is an important mechanic, it might be interesting to store the random seed at any point you can (or do) diverge, that way you can faithfully rewind and start again. You could also do some weird magic with statistics-based games where you calculate two options using two diverging branches of choices, and then with that result, you can pick either and they both would have been possible from the point you started calculating them. So basically, you can do some cool, weird shit with "replay" mechanics, while still keeping it deterministic.
Helvecta wrote: So, hypothetically speaking, let's say I use a RNG to fetch 3 random numbers, with the following seeds: 1, 2, and 3. For the sake of the example, the returned numbers will be 3, 6, and 1. Then, I try to use the same RNG to fetch 3 random numbers again, with the seeds being 2, 3, and 4. Would I get completely different random numbers? Or would I get 6, 1, and a new random number?
The latter, see below.
Helvecta wrote:it seem[s] like the seed is the insertion point in a list of numbers
[...]
Robin even specifically stated that I shouldn't be setting the seed inside of the function (and testing it myself has confirmed it):
[...]
If the seed is an insertion point like I figure it is, why is it bad to change it wherever you want?
Because you're resetting the position! It's like you're rewinding your movie, and complaining you always see the same scene.
Imagine the random numbers being a cycle like this:

Code: Select all

seed
|
\/
a -----> b
^        |
|        |
|        \/
d<------ c
This is a reasonable approximation of how it really works (except the cycle is so long, it's practically infinite). You ask for a number with the seed producing this cycle, and you get 'a', then you ask for one again, get 'b', then 'c', then 'd' and then 'a' again. What you're doing is you're getting 'a', then telling it to use the same seed (start at the same point), and then you get 'a' again.

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 11:10 am
by Robin
An important detail I think you're missing, Helvecta, is that asking for a random number changes the seed (which is why you don't mess with the seed). The PRNG is written such that the next seed will produce an unpredictable number next.

A PRNG generally works like this:

Code: Select all

local hiddenSeed = 0

function seed(seednr)
    hiddenSeed = seednr
end

function getrandomnumber()
    local nr = makeNumberBasedOnSeed(hiddenSeed) -- depends on specific PRNG
    hiddenSeed = makeNewSeedBasedOnSeed(hiddenSeed) -- depends on specific PRNG
    return nr
end
With a good PRNG, the function makeNewSeedBasedOnSeed makes sure that the sequence of seeds produced (and thus of the numbers produced) is unpredictable. But if you keep seeding, you never let it finish the sequence, like bartbes showed in his ASCII diagram.

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 4:53 pm
by Helvecta
bartbes wrote:it might be interesting to store the random seed at any point you can (or do) diverge, that way you can faithfully rewind and start again.
So, like if your character were to die, you could reload from the beginning and use that seed to regenerate the world as it was when you saved (assuming that the world was originally generated using the same seed)? Or is replicable randomness not possible this way?
bartbes wrote:Because you're resetting the position! It's like you're rewinding your movie, and complaining you always see the same scene.
Robin wrote:An important detail I think you're missing, Helvecta, is that asking for a random number changes the seed (which is why you don't mess with the seed). The PRNG is written such that the next seed will produce an unpredictable number next.
OH. So it's not my responsibility to change the seed; LOVE chooses a random one for me. I think realizing this brings everything full circle, many thanks! :nyu:

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 5:51 pm
by bartbes
Helvecta wrote:is replicable randomness not possible this way?
That's exactly what it's about.

Re: Another Question About 0.9's RNG

Posted: Sat Dec 21, 2013 11:20 pm
by Helvecta
I guess the concept goes over my head; oh well :oops: