Search code examples
randomluaprng

Lua 4.0 random number generator


I don't have access to random() in Lua 4.0 (DON'T ASK), so I need to roll my own random number generator. Or rather I have to roll another one since the one I implemented several years ago is failing me now. I.e. I am noticing repeating values which is bad.

Any suggestions or examples written in Lua that I can use? FYI here's the one I've been using up until now:

seedobja = 1103515245
seedobjc = 12345
seedobjm = 4294967295 --0x100000000

function srandom(seedobj, fVal1, fVal2)
    seedobj[1] = mod(seedobj[1] * seedobja + seedobjc, seedobjm)
    local temp_rand = seedobj[1] / (seedobjm - 1)
    if (fVal2) then
        return floor(fVal1 + 0.5 + temp_rand * (fVal2 - fVal1))
    elseif (fVal1) then
        return floor(temp_rand * fVal1) + 1
    else
        return temp_rand
    end
end

[edit]

Later edit deleted.


Solution

  • Here is another attempt (always Lua 5.1 code), using an adaptation from C of a subtractive generator by Knuth (not linear congruential then). According to Knuth it should work with FP arithmetic (even single precision).

    local mod = math.fmod
    local floor = math.floor
    local abs = math.abs
    
    local B =  4000000
    
    -- rough adaptation of Knuth float generator
    function srandom( seedobj, fVal1, fVal2 )
        local ma = seedobj.ma
        local seed = seedobj.seed
        local mj, mk
        if seed < 0 or not ma then
            ma = {}
            seedobj.ma = ma
            mj = abs( 1618033 - abs( seed ) )
            mj = mod( mj, B )
            ma[55] = mj
            mk = 1
            for i = 1, 54 do
                local ii = mod( 21 * i,  55 )
                ma[ii] = mk
                mk = mj - mk
                if mk < 0 then mk = mk + B end
                mj = ma[ii]
            end
            for k = 1, 4 do
                for i = 1, 55 do
                    ma[i] = ma[i] - ma[ 1 + mod( i + 30,  55) ]
                    if ma[i] < 0 then ma[i] = ma[i] + B end
                end
            end
            seedobj.inext = 0
            seedobj.inextp = 31
            seedobj.seed = 1
        end -- if
        local inext = seedobj.inext
        local inextp = seedobj.inextp
        inext = inext + 1
        if inext == 56 then inext = 1 end
        seedobj.inext = inext
        inextp = inextp + 1
        if inextp == 56 then inextp = 1 end
        seedobj.inextp = inextp
        mj = ma[ inext ] - ma[ inextp ]
        if mj < 0 then mj = mj + B end
        ma[ inext ] = mj
        local temp_rand = mj / B
        if fVal2 then
            return floor( fVal1 + 0.5 + temp_rand * ( fVal2 - fVal1 ) )
        elseif fVal1 then
            return floor( temp_rand * fVal1 ) + 1
        else
            return temp_rand
        end
    end
    
    -- test
    
    -- Note: seedobj must be a table with a field named `seed`;
    -- this field must be negative; after the first number has
    -- been generated, the seedobj table will be populated with
    -- additional state needed to generate numbers; changing its
    -- `seed` field to a negative number will reinitialize the
    -- generator and start a new pseudorandom sequence.
    local seedobj = { seed = -232343 }
    for i = 1, 100 do
        print( srandom( seedobj, 100, 1000 ) )
    end