Make random (short range) more randomized

I have levels where the math.random works between numbers 1-3, 1-5, and periodically (not always but in 25-30 % cases) the math.random creates a series of the same repeating numbers, five or even seven in a row at a time.

For me, this is a problem. Is there a way to somehow improve the randomizing mechanics?

For example, I don’t request idle 1, 2, 3, 1, 2, 3 random generating but I also don’t need 1, 1, 1, 1, 2, 1, 1 random, which periodically quite often happens.

I’ve been doing some C64 programming lately and this problem with random numbers is even more pronounced on that machine, can often get ‘stuck’ on a particular value for 10, 11, 12 iterations.

I did a couple of things to get round this:

Have a variable that increases every frame, as it’s 8-bit it loops round when it reaches 256. I add this to the ‘generated’ random number. This can work quite well as long as there’s no particular pattern to when you request numbers.

I also change this number based on user input - i.e. every time they move the joystick right, it increases by x, every time they press fire it decreases by y. 

Another option is to have a lookup table which repeats 1, 2, 3 say 32 times. Generate a random number between 1-96 and use this to get the value in the table. 

I have also used this random number generator when I needed seeded values that would be the same whether running on iOS, Android or Desktop. No idea whether it gives a better distribution than math.random but might be worth a go?

&nbsp; -- "GameRand.lua" -- -- This is an implementation of a md5 based -- pseudo random number generator which creates the same -- sequence of values for implementations on different -- platforms (Hans Raaf) &nbsp; -- copy what we need as local local floor = math.floor local byte = string.byte local sub = string.sub local random = math.random local tostring = tostring local assert = assert &nbsp; local crypto = require('crypto') &nbsp; local digest = crypto.digest local md5 = crypto.md5 &nbsp; -- make it into a module module(...) &nbsp; -- All functions are local and used only inside the objects I create &nbsp; local randInt = function(self, min, max) &nbsp; &nbsp; assert(self.pos \> 0) -- is 0 if neither seed nor randomize was called &nbsp; &nbsp; assert(max - min \< 256 and max - min \> 0) -- only possible values &nbsp; &nbsp; if self.pos \> 16 then &nbsp; &nbsp; &nbsp; &nbsp; self.digest = digest(md5, self.digest, true) &nbsp; &nbsp; &nbsp; &nbsp; self.pos = 1 &nbsp; &nbsp; end &nbsp; &nbsp; local x = floor(byte(sub(self.digest, self.pos, self.pos)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \* (max - min + 1) / 256) + min &nbsp; &nbsp; self.pos = self.pos + 1 &nbsp; &nbsp;\_step =\_step + 1 &nbsp; &nbsp; return x end &nbsp; local seed = function(self, s) &nbsp; &nbsp;\_step = 0 &nbsp; &nbsp;\_seed = s &nbsp; &nbsp; self.digest = digest(md5,\_seed, true) &nbsp; &nbsp; self.pos = 1 end &nbsp; local randomize = function(self) &nbsp; &nbsp; self:seed(tostring(random())) end &nbsp; local step = function(self, step) &nbsp; &nbsp; assert(self.pos \> 0) -- is 0 if neither seed nor randomize was called &nbsp; &nbsp; -- fast forward to a position &nbsp; &nbsp; local i &nbsp; &nbsp; -- shortcut for full 16 steps &nbsp; &nbsp; for i = 1, floor(step / 16) do &nbsp; &nbsp; &nbsp; &nbsp; self.digest = digest(md5, self.digest, true) &nbsp; &nbsp; &nbsp; &nbsp; self.pos = 1 &nbsp; &nbsp; &nbsp; &nbsp;\_step =\_step + 16 &nbsp; &nbsp; end &nbsp; &nbsp; -- set the offsets to the right position &nbsp; &nbsp; self.pos = self.pos + step % 16 &nbsp; &nbsp;\_step =\_step + step % 16 end &nbsp; &nbsp; return function() -- create a new Object :) &nbsp; &nbsp; return { &nbsp; &nbsp; &nbsp; &nbsp; my\_step = 0, &nbsp; &nbsp; &nbsp; &nbsp; my\_seed = nil, &nbsp; &nbsp; &nbsp; &nbsp; digest = nil, &nbsp; &nbsp; &nbsp; &nbsp; pos = 0, &nbsp; &nbsp; &nbsp; &nbsp; randInt = randInt, &nbsp; &nbsp; &nbsp; &nbsp; seed = seed, &nbsp; &nbsp; &nbsp; &nbsp; randomize = randomize, &nbsp; &nbsp; &nbsp; &nbsp; step = step &nbsp; &nbsp; } end &nbsp; &nbsp;

Hi @stalxerhd,


math.randomseed( os.time() ) local prev\_rand\_index = 0 for i=1, 10 do repeat rand\_index = math.random(3)&nbsp; until rand\_index ~= prev\_rand\_index&nbsp; prev\_rand\_index = rand\_index print( rand\_index ) end&nbsp;

Have a nice day:)


if you force that two numbers next to each other cant be the same, then the outcome is much less random. In @idurniat’s code, if you had 3 numbers long sequence, then only the first number has 1/3 chance of being 1, 1/3 of being 2 and 1/3 of being 3. Afterwards the chances are only 1/2 and 1/2, so randomness clearly suffers.

For better random, get better seed. All computer random numbers are pseudo random, ie not random. But if you restrict them, then it gets much worse.

You can also use a shuffle bag.

local bag = 1,2,3,4,5 ) print( bag:get() ) -- guaranteed 1..5 print( bag:get() ) -- guaranteed 1..5; non-repeating print( bag:get() ) -- guaranteed 1..5; non-repeating print( bag:get() ) -- guaranteed 1..5; non-repeating print( bag:get() ) -- guaranteed 1..5; non-repeating print( bag:get() ) -- guaranteed 1..5; may repeat last

@Idurniat’s suggestion is good if all you want is to avoid having the same number twice in a row. However, as the original question was “how to make a sequence of numbers more randomised”, this function makes the sequence deterministic instead of random.

The equation for possible outcomes these simple sequences would be N^M, where N is the possible outcomes for each number and M is the amount of numbers in a given sequence. For instance, if you have five numbers in a sequence between the values of 1 and 3, then N = 3 and M = 5, for 3^5 = 243.

If you insist that two consecutive numbers can’t be the same, then the equation changes to N x (N-1)^(M-1). For the same five numbers in a sequence between the values of 1 and 3, the result becomes 3 x (3-1)^(5-1) = 48.

So, if you wish to prevent the same number from occurring twice in a row, then in the above example you can only have 48 unique sequences instead to 243 unique sequences, i.e. significantly less random.

Exactly! Thanks for the real maths!

Guys, thank you all for the advice. This was an interesting discussion, but I did not speak of literally two or three identical values ​​in a row.

The problem for me is 4-5 or more identical values ​​in a row, which happens quite often on random short ranges.

XeduR @Spyric this example is not correct, because it does not describe the probability. In your example, there are 243 search options, three of which will always have five identical numbers in a row. You can also count four identical numbers in a row from a five-element string; three and two identical numbers; but it’s all combinatorics.

This does not mean at all that if we in lua generate 243 times a five-element array, then there will always be 3 arrays, all contain the same values. I have checked, random will give out from 0 to 8 arrays with the same values ​​(any). So this 0 to 8 is our probability. We can, of course, repeat the procedure 5,000 times, then yes, average will be 3 arrays with the same values, but this is already the law of large numbers.

I’m working with random () on-demand, and if I record, for example, an array of 1000 random values, there I could found 8-12 rows of five identical values ​​(any). And four is around 20-25. If unpack() and watch on it, you could found sections with a good random (when the values ​​regularly change or are repeated no more than 2 times) and bad (values ​​go one after another 4-8 times).

Realization of probability could be various, it is another question. Most programming languages ​​use a uniform probability distribution, which means that the generated random number can reach any point in the range with equal probability. However, in lua all this works well only on wide ranges (0-100) and large samples (more than 1000).

My task is exactly the opposite - a short-range and a permanent stream of random (), so I don’t have a sample. I generate 1-2 objects per second, and on the screen another 6-8 objects from previous generations. And from time to time it is clearly visible that the probability is either uniform or fixated on one value.

Just as an example, let’s say I’m playing poker and receive cards from an endless deck, open them, and all are jokers. From the point of view of probability/LLN theory, everything is correct, since the deck is infinite, then an infinite number of standard 54-card decks are shuffled in it and jokers percentage in the entire deck strictly 0.037%.

But the very quality of this shuffle is very, very bad since the player regularly encounters only jokers, aces or jacks, several aces or jacks of the same suit, and so on.

And I asked for some simple way to improve the operation of the random mechanism in the sense of its “shuffling”. But the discussion showed that I need to create some kind of partially randomized, partially determined algorithm.

@stalxerhd , the example is still correct, you are talking about a different matter.

Shuffling, as already talked about, can be a valid approach. Shuffling a predetermined table is a good means for controlling probabilities. Many games that I know of use this method to control drop rates.

For instance, if you had a game where the player has 1% chance to get a legendary item, 5% for epic, 10% for rare, 20% for uncommon and 64% for common items. If a designer wants to ensure that a player will get exactly 1% chance for a said legendary item, then they’d create an array with 100 entries and they’d shuffle it. Every time the game reaches the end of said array, it is simply reshuffled once more. This would ensure that the developer intended probabilities would remain constant and there would be no “lucky breaks” or “dice hell”.

if math.random(1, 10) is too repetitive then try math.floor(math.random(10, 100) / 10) or evenmath.floor(math.random(100, 1000) / 100)