├── randomlua.love ├── LICENSE ├── demo.lua ├── README.md └── randomlua.lua /randomlua.love: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linux-man/randomlua/HEAD/randomlua.love -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 linux-man 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /demo.lua: -------------------------------------------------------------------------------- 1 | require('randomlua') 2 | 3 | l1 = lcg(0) -- Linear congruential generator (Ansi C params) 4 | l2 = lcg(0, 'nr') --Linear congruential generator (Numerical recipes params) 5 | c1 = mwc(0) -- Multiply-with-carry (Ansi C params) 6 | c2 = mwc(0, 'nr') -- Multiply-with-carry (Numerical recipes params) 7 | m = twister(0) -- Mersenne twister 8 | 9 | for n = 1, 10 do 10 | io.write(string.format("%8u%8u%8u%8u%16u\n", l1:random(0), l2:random(0), c1:random(0), c2:random(0), m:random(0))) 11 | end 12 | 13 | -- lets run them all a bunch of times and gather stats 14 | count = 10000 15 | names = {"l1", "l2", "c1", "c2", "m"} 16 | generators = {l1, l2, c1, c2, m} 17 | mins = {} 18 | maxs = {} 19 | bins = {} 20 | for i, gen in ipairs(generators) do 21 | bins[i] = {} 22 | for n = 1, count do 23 | x = gen:random() 24 | bin = math.floor(x*10) + 1 25 | bins[i][bin] = (bins[i][bin] or 0) + 100/count 26 | if mins[i] == nil or x < mins[i] then 27 | mins[i] = x 28 | end 29 | if maxs[i] == nil or x > maxs[i] then 30 | maxs[i] = x 31 | end 32 | end 33 | end 34 | 35 | print("name: min - max [histogram of each 10%]") 36 | for i = 1, #generators do 37 | for j = 1, #bins[i] do 38 | bins[i][j] = string.format("%2.0f%%", bins[i][j]) 39 | end 40 | distribution = table.concat(bins[i], " ") 41 | print(string.format("%2s: %f - %f [%s]", names[i], mins[i], maxs[i], distribution)) 42 | end 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # randomlua - Pure Lua Random Generator 2 | 3 | (c) 2017 Caldas Lopes 4 | 5 | Mersenne twister (http://en.wikipedia.org/wiki/Mersenne_twister), very good and slow 6 | Multiply-with-carry (http://en.wikipedia.org/wiki/Multiply-with-carry), probably the best option 7 | Linear congruential generator (http://en.wikipedia.org/wiki/Linear_congruential_generator), fast and, well, it works... kind of. 8 | 9 | MWC and LCG come with 3 pre-defined parameters. 10 | 11 | m = twister(seed) - Create a generator. 12 | 13 | Usually it's better to initialize the generators without params. m = twister() use os.time() for seed. 14 | A seed must be given when choosing the LCG params, as in l3 = lcg(0, 'mvc'). After that, call l3:randomseed() 15 | 16 | :random() returns a float between 0 and 1. 17 | :random(max) returns an integer between 1 and max 18 | :random(min, max) returns an integer between min and max 19 | :random(0) return the original 31 (twister) or 16 (lcg and mwc) bits integer. 20 | 21 | :randomseed(seed) to change seed. Again, randomseed() call os.time(). 22 | 23 | Code example: 24 | 25 | require('randomlua') 26 | 27 | l1 = lcg(0) -- Linear congruential generator (Ansi C params) 28 | l2 = lcg(0, 'nr') --Linear congruential generator (Numerical recipes params) 29 | l3 = lcg(0, 'mvc') -- Linear congruential generator (Microsoft Visual C params) 30 | c1 = mwc(0) -- Multiply-with-carry (Ansi C params) 31 | c2 = mwc(0, 'nr') -- Multiply-with-carry (Numerical recipes params) 32 | c3 = mwc(0, 'mvc') -- Multiply-with-carry (Microsoft Visual C params) 33 | m = twister(0) -- Mersenne twister 34 | 35 | for n = 1, 10 do 36 | io.write(string.format("%8d%8d%8d%8d%8d%8d%16u\n", l1:random(0), l2:random(0), l3:random(0), c1:random(0), c2:random(0), c3:random(0), m:random(0))) 37 | end 38 | -------------------------------------------------------------------------------- /randomlua.lua: -------------------------------------------------------------------------------- 1 | --[[------------------------------------ 2 | RandomLua v0.3.1 3 | Pure Lua Pseudo-Random Numbers Generator 4 | Under the MIT license. 5 | copyright(c) 2017 linux-man 6 | --]]------------------------------------ 7 | 8 | local math_floor = math.floor 9 | 10 | local function normalize(n) --keep numbers at (positive) 32 bits 11 | return n % 0x80000000 12 | end 13 | 14 | local function bit_and(a, b) 15 | local r = 0 16 | local m = 0 17 | for m = 0, 31 do 18 | if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end 19 | if a % 2 ~= 0 then a = a - 1 end 20 | if b % 2 ~= 0 then b = b - 1 end 21 | a = a / 2 b = b / 2 22 | end 23 | return normalize(r) 24 | end 25 | 26 | local function bit_or(a, b) 27 | local r = 0 28 | local m = 0 29 | for m = 0, 31 do 30 | if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end 31 | if a % 2 ~= 0 then a = a - 1 end 32 | if b % 2 ~= 0 then b = b - 1 end 33 | a = a / 2 b = b / 2 34 | end 35 | return normalize(r) 36 | end 37 | 38 | local function bit_xor(a, b) 39 | local r = 0 40 | local m = 0 41 | for m = 0, 31 do 42 | if a % 2 ~= b % 2 then r = r + 2^m end 43 | if a % 2 ~= 0 then a = a - 1 end 44 | if b % 2 ~= 0 then b = b - 1 end 45 | a = a / 2 b = b / 2 46 | end 47 | return normalize(r) 48 | end 49 | 50 | local function seed() 51 | --return normalize(tonumber(tostring(os.time()):reverse())) 52 | return normalize(os.time()) 53 | end 54 | 55 | --Mersenne twister 56 | mersenne_twister = {} 57 | mersenne_twister.__index = mersenne_twister 58 | 59 | function mersenne_twister:randomseed(s) 60 | if not s then s = seed() end 61 | self.mt[0] = normalize(s) 62 | for i = 1, 623 do 63 | self.mt[i] = normalize(0x6c078965 * bit_xor(self.mt[i-1], math_floor(self.mt[i-1] / 0x40000000)) + i) 64 | end 65 | end 66 | 67 | function mersenne_twister:random(a, b) 68 | local y 69 | if self.index == 0 then 70 | for i = 0, 623 do 71 | --y = bit_or(math_floor(self.mt[i] / 0x80000000) * 0x80000000, self.mt[(i + 1) % 624] % 0x80000000) 72 | y = self.mt[(i + 1) % 624] % 0x80000000 73 | self.mt[i] = bit_xor(self.mt[(i + 397) % 624], math_floor(y / 2)) 74 | if y % 2 ~= 0 then self.mt[i] = bit_xor(self.mt[i], 0x9908b0df) end 75 | end 76 | end 77 | y = self.mt[self.index] 78 | y = bit_xor(y, math_floor(y / 0x800)) 79 | y = bit_xor(y, bit_and(normalize(y * 0x80), 0x9d2c5680)) 80 | y = bit_xor(y, bit_and(normalize(y * 0x8000), 0xefc60000)) 81 | y = bit_xor(y, math_floor(y / 0x40000)) 82 | self.index = (self.index + 1) % 624 83 | if not a then return y / 0x80000000 84 | elseif not b then 85 | if a == 0 then return y 86 | else return 1 + (y % a) 87 | end 88 | else 89 | return a + (y % (b - a + 1)) 90 | end 91 | end 92 | 93 | function twister(s) 94 | local temp = {} 95 | setmetatable(temp, mersenne_twister) 96 | temp.mt = {} 97 | temp.index = 0 98 | temp:randomseed(s) 99 | return temp 100 | end 101 | 102 | --Linear Congruential Generator 103 | linear_congruential_generator = {} 104 | linear_congruential_generator.__index = linear_congruential_generator 105 | 106 | function linear_congruential_generator:random(a, b) 107 | local y = (self.a * self.x + self.c) % self.m 108 | self.x = y 109 | if not a then return y / 0x10000 110 | elseif not b then 111 | if a == 0 then return y 112 | else return 1 + (y % a) end 113 | else 114 | return a + (y % (b - a + 1)) 115 | end 116 | end 117 | 118 | function linear_congruential_generator:randomseed(s) 119 | if not s then s = seed() end 120 | self.x = normalize(s) 121 | end 122 | 123 | function lcg(s, r) 124 | local temp = {} 125 | setmetatable(temp, linear_congruential_generator) 126 | temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C 127 | if r then 128 | if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes. 129 | elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC 130 | end 131 | temp:randomseed(s) 132 | return temp 133 | end 134 | 135 | -- Multiply-with-carry 136 | multiply_with_carry = {} 137 | multiply_with_carry.__index = multiply_with_carry 138 | 139 | function multiply_with_carry:random(a, b) 140 | local m = self.m 141 | local t = self.a * self.x + self.c 142 | local y = t % m 143 | self.x = y 144 | self.c = math_floor(t / m) 145 | if not a then return y / 0x10000 146 | elseif not b then 147 | if a == 0 then return y 148 | else return 1 + (y % a) end 149 | else 150 | return a + (y % (b - a + 1)) 151 | end 152 | end 153 | 154 | function multiply_with_carry:randomseed(s) 155 | if not s then s = seed() end 156 | self.c = self.ic 157 | self.x = normalize(s) 158 | end 159 | 160 | function mwc(s, r) 161 | local temp = {} 162 | setmetatable(temp, multiply_with_carry) 163 | temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C 164 | if r then 165 | if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes. 166 | elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC 167 | end 168 | temp.ic = temp.c 169 | temp:randomseed(s) 170 | return temp 171 | end 172 | --------------------------------------------------------------------------------