How to generate a number with a correlating chance

Hello, How would I go about making a function that generates a random number between like 1 and 1,000,000,000 but the higher the number the harder it is too roll. For example to roll 100 it would be a 1/100 chance or 1% or like 1,000 would be 0.1%. I hope that makes sense I wasn’t sure exactly how to word it.

3 Likes

Something like this …

function getRandomNumber()
	local maxChance = 1000000000
	local thresholds = {100, 1000, 10000}
	local number

	repeat
		number = math.random(1, maxChance)
		local probability = 1 / number

		for _, threshold in ipairs(thresholds) do
			if probability >= 1 / threshold then
				return number, probability
			end
		end
	until false
end


print(getRandomNumber())
3 Likes

Considering the range you gave, I believe you want a random integer. I don’t know of a way to efficiently choose a random integer that follows the given probability rule from such a big interval because a simple weighted random value code for a finite distribution won’t be efficient with such many possible values.

However, you could choose the random value using a continuous probability distribution and round it to get an integer. Because of the rounding, the values won’t exactly follow the desired distribution, but the more possible integers, the better they will follow the distribution (I think), so with a long interval like the one you mentioned, this should give results pretty close to the desired distribution.

I have written about continous probability distributions in an earlier post:

Let’s call the minimum integer imin and the maximum integer imax. These should be positive integers. In this case, the density function (let’s call it f) should be such a function that

  • f(x) = 0 when x < imin - 0.5 or when x > imax + 0.5
  • f(x) = a * 1 / x when imin - 0.5 <= x <= imax + 0.5 where a is chosen such that the area between the curve of f(x) and the x-axis is 1. The intervals in which f(x) is zero obviously don’t contribute to the area so it’s enough to solve a such that the definite interval of a * 1 / x in the interval [imin - 0.5, imax + 0.5] is 1.

By solving this equation, we get the function f. Then, using it, we get the accumulation function F.

p is the probability for a random number that respects the distribution to be less than or equal to x. We’ll choose p as a uniform random number. By solving x from p = F(x), we get two alternative expressions for x. We choose the one that gives positive values. g(p) in the image is the function that gives a random number given a uniform random probability p.

After that, all we need to do is round the result of g(p). Here’s the code. g(p) is called getRandomNumberWithDesiredDistribution.

local function getRandomNumberWithDesiredDistribution()
	local p = math.random()
	return .5 * math.abs((2 * iMax + 1) / (2 * iMin - 1))^p * (2 * iMin - 1)
end

local function getRandomIntegerWithApproximatelyDesiredDistribution()
	local continuousDistributionNumber = getRandomNumberWithDesiredDistribution()
	return math.round(continuousDistributionNumber)
end

Actually, if iMin is always 1, you can simplify

.5 * math.abs((2 * iMax + 1) / (2 * iMin - 1))^p * (2 * iMin - 1)

to

.5 * math.abs(2 * iMax + 1)^p
4 Likes

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