Article on random number generation is dated

I’m referring to the following article:

math.random doesn’t generate the same sequence of numbers anymore (i.e. you don’t have to call math.randomseed it upon starting the game), which renders part of the article irrelevant. There has also been a new datatype introduced, Random, which should be used over math.random in most (if not all?) cases since it has its own internal state and has a cleaner API for generating numbers in a range.

4 Likes

Actually, it is still recommend to use math.random if you want to micro-optimize as much as possible (aka needing random numbers multiple times a second) as proven by @Ugh_Lily’s testing:

The syntax is super nice for the Random object but it being slower can be a dealbreaker in some use cases.

But I do agree that, in this beginner tutorial, Random should be used over math.random.

1 Like

Actually, it is still recommend

No, that is not recommended.

What is recommended is that you choose the API that works best for what you are doing, then identify slowdowns based on informed profiling and experimentation.

The RNG is the least of your worries. The linked advice is dubious at best.

6 Likes

Although I fully agree with your advice, the rest of the sentence stated:

Not,

Although, I should have said “if you want to micro-optimize as much as possible”. Going to edit that in now. Sorry for not adding that in earlier.

Both randoms can accomplish the same things, except that one requires a little more math experience to accomplish certain tasks. Again, I cannot say what the best API for everybody is, but I just wanted to bring out the rare problem of speed. For some people, they prefer optimization over readability; those were the people who I were addressing in this post.

I admit defeat in that this is the wrong place to address this matter (as OP is in regards to a beginner tutorial) but this matter should not be skimmed over.


Also, the post I linked is by a person famous for their micro-optimization research, so I highly doubt the research is misleading.


Thank you for your time and thank you OP for finding this!

1 Like

While I understand that math.random is faster than using Random’s :NextNumber() (if only marginally – I’m talking about a 0.005 second difference for 1,000,000 random numbers on my crummy desktop. In all likelihood, whatever you’re going to use these random numbers for will take much longer than generating the numbers itself.), you’re missing out on a lot of its advantages:

  1. Like you said, its syntax is much neater. :NextNumber(min, max) is much easier to read that min + math.random() * (max-min). Having easy to read code is important since it makes code easy to debug and step through mentally. There’s a reason why programmers moved away from programming in assembly.

  2. It errors you when max is less than min. It’s important to fail loudly since it indicates that there might be a logical error that you would otherwise miss if you stuck to the math.random() solution I mentioned above.

  3. It has its own internal state. What I mean by this is that calling :NextNumber on a different instance of Random somewhere else in your code won’t affect the results of the RNG you’re currently using. Consider the following example.

With Random:

-- foo is a function that needs a seeded RNG
function foo(seed)
	local r = Random.new(seed)
	-- Do stuff with RNG here...
end

function bar()
	local r = Random.new(1)
	--
	-- imagine that I'm using the RNG here
	--
	foo(seed)
	-- This will always be the same number.
	print(r:NextNumber())
end

With math.random:

-- foo is a function that needs a seeded RNG
function foo(seed)
	math.randomseed(seed)
	-- Do stuff with RNG here...
end

function bar(seed)
	math.randomseed(1)
	--
	-- imagine that I'm using the RNG here
	--
	foo(seed)
	-- As a side-effect of calling foo, the state of the RNG used by math.random() has changed.
	-- The following will print something different if I pass a different number to this function, which
	-- is something I may not want!
	print(math.random())
end
2 Likes

Thank you for your well thought out reply!


In the world of micro-optimizations, every micromicromicrosecond is everything. Again, I know I said optimize instead of micro-optimiize in my OP; sorry about that and I have since fixed it.

  1. For mildly advanced programmers, they will realize this is a range function. Also, comments exist for a reason. But I do agree :NextNumber(min, max) is much more readable.
  2. Actually, I view this as a negative. There are times (none I can think of right now; sorry) that negative ranges are useful. min + math.random() * (max-min) will work perfectly with negative ranges. Although I am obligated to say that this could save a newbie from an hour of debugging.
  3. This is your best point. I honestly can’t argue it. Great job for noticing this; this is a major stumbling block that math.random has.

Again, I thank you for finding this problem in this beginner tutorial.
Also, I thank you for treating me like a human and not some inexperienced delinquent; I really appreciate it, thank you.

Edit: Was trying to test on some online lua compiler how much faster min + math.random() * (max-min) is compared to Random:NextNumber(min, max) but I realized the Random object is Roblox exclusive. I’ll try to test tomorrow or, if you want, here is the code I was going to test with:

Bad but usable comparison code
local Rand = Random.new(3463);
local timesToRepeat = 1;

local mins = {5, 2, 8, 6, 2, 8, 6, 4, 1, 4};
local maxs = {8, 3, 9, 10, 5, 9, 7, 10, 10, 23};

local tik = tick();
for z = 1, timesToRepeat do
    for i = 1, #mins do
        local test = mins[i] + math.random() * (maxs[i]-mins[i]);
    end
end
print(tick() - tik);

local tik = tick();
for z = 1, timesToRepeat do
    for i = 1, #mins do
        local test = Rand:NextNumber(mins[i], maxs[i]);
    end
end
print(tick() - tik);
1 Like

In your pursuit of microoptimization, you shouldn’t touch Vector3, you should inline every function, and you should unroll every for-loop you encounter.

If you find that your code is hard to read, remember that you can always comment it ;^)


In all seriousness, please stop micro-optimizing your code. Unless, for whatever reason, a benchmark reveals that generating the random numbers takes like 10% of your computing time, you should value your code’s readability and maintainability among all else.

3 Likes

:clap: :clap: :clap: :clap: :clap:


I admit defeat. But I want to clarify a few things:

  1. min + math.random() * (max-min) is not a spawn of the devil and can be deciphered if you look at it for a couple of seconds.
  2. math.random must be used to utilize negative ranges. Nevermind, you can just use min + Random:NextNumber() * (max-min)

Also, I actually don’t micro-optimize a lot of things. I do sin and use for loops but I tend to use them in a smart way (for i = 1, #table do instead of the ipairs equivalent). I was under the impression that math.random is 0.1 seconds faster than the Random object, not 0.005. Nevertheless, I have been beaten and tried to argue a point that was already lost. Thank you for the enjoyable debate. Have a great day/evening/night.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.