27

I am working on programming a Markov chain in Lua, and one element of this requires me to uniformly generate random numbers. Here is a simplified example to illustrate my question:

example = function(x)
    local r = math.random(1,10)
    print(r)
    return x[r]
end

exampleArray = {"a","b","c","d","e","f","g","h","i","j"}

print(example(exampleArray))

My issue is that when I re-run this program multiple times (mash F5) the exact same random number is generated resulting in the example function selecting the exact same array element. However, if I include many calls to the example function within the single program by repeating the print line at the end many times I get suitable random results.

This is not my intention as a proper Markov pseudo-random text generator should be able to run the same program with the same inputs multiple times and output different pseudo-random text every time. I have tried resetting the seed using math.randomseed(os.time()) and this makes it so the random number distribution is no longer uniform. My goal is to be able to re-run the above program and receive a randomly selected number every time.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Starfish_Prime
  • 287
  • 1
  • 3
  • 5
  • 2
    Seed the generator *once*. – lhf Nov 22 '13 at 21:45
  • 1
    Thanks so much for the help. Weird thing is after seeding the generator (once!) the first call to math.random always returns the same value. After that though all subsequent calls return the randomness I was looking for. I just have one "junk" call before I start using the numbers returned by math.random. Anyways, thanks again. – Starfish_Prime Nov 22 '13 at 22:07
  • That's expected output for the Mersenne twister implemented in some C standard library implementations: the first value will drift slightly depending on the difference of the seed, then the second value will drift more, and so on. – Stuart P. Bentley Nov 23 '13 at 01:12
  • 2
    possible duplicate of [Why is the first random number always the same on some platforms in lua?](http://stackoverflow.com/questions/461978/why-is-the-first-random-number-always-the-same-on-some-platforms-in-lua) – Tom Blodget Nov 24 '13 at 05:03

4 Answers4

28

You need to run math.randomseed() once before using math.random(), like this:

math.randomseed(os.time())

From your comment that you saw the first number is still the same. This is caused by the implementation of random generator in some platforms.

The solution is to pop some random numbers before using them for real:

math.randomseed(os.time())
math.random(); math.random(); math.random()

Note that the standard C library random() is usually not so uniformly random, a better solution is to use a better random generator if your platform provides one.

Reference: Lua Math Library

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
13

Standard C random numbers generator used in Lua isn't guananteed to be good for simulation. The words "Markov chain" suggest that you may need a better one. Here's a generator widely used for Monte-Carlo calculations:

local A1, A2 = 727595, 798405  -- 5^17=D20*A1+A2
local D20, D40 = 1048576, 1099511627776  -- 2^20, 2^40
local X1, X2 = 0, 1
function rand()
    local U = X2*A2
    local V = (X1*A2 + X2*A1) % D20
    V = (V*D20 + U) % D40
    X1 = math.floor(V/D20)
    X2 = V - X1*D20
    return V/D40
end

It generates a number between 0 and 1, so r = math.floor(rand()*10) + 1 would go into your example. (That's multiplicative random number generator with period 2^38, multiplier 5^17 and modulo 2^40, original Pascal code by http://osmf.sscc.ru/~smp/)

GrayFace
  • 1,224
  • 10
  • 13
  • 1
    This will, however, always produce the same sequence of numbers. It isn't clear how one would "seed" the procedure (by changing the initial value of `X1` and `X2` for example) without the degradation of the period. – Witiko May 19 '16 at 12:19
  • To have a different sequence you need to set X1 and X2 to different numbers. 0 <= X1 <= 2^20-1, 1 <= X2 <= 2^20-1 (must be odd!) Also, the algorithm can be optimized a lot, especially with 64-bit unsigned integers. – GrayFace Sep 05 '16 at 09:26
  • Basically, in LuaJIT rand() boils down to this: X1 = X1*762939453125ull; return tonumber(X1)*(2.0^-64). Initial X1 is 0ull + (Seed*2 + 1) * 2^24, where 0 <= Seed <= 2^39-1. – GrayFace Sep 05 '16 at 09:35
12
math.randomseed(os.clock()*100000000000)
for i=1,3 do
    math.random(10000, 65000)
end

Always results in new random numbers. Changing the seed value will ensure randomness. Don't follow os.time() because it is the epoch time and changes after one second but os.clock() won't have the same value at any close instance.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
daemonsl
  • 442
  • 4
  • 6
2

There's the Luaossl library solution: (https://github.com/wahern/luaossl)

local rand = require "openssl.rand"
local randominteger
if rand.ready() then -- rand has been properly seeded
    -- Returns a cryptographically strong uniform random integer in the interval [0, n−1].
    randominteger = rand.uniform(99) + 1 -- randomizes an integer from range 1 to 100
end

http://25thandclement.com/~william/projects/luaossl.pdf