@native function ExponentialBiased(minNumber: number, maxNumber: number, lambda: number): number
assert(lambda > 0, "Lambda must be a positive number.")
local u = Random.new():NextNumber()
-- This is the correct formula for inverse transform sampling on a
-- truncated exponential distribution.
-- It avoids the "clamping" problem and preserves the distribution's shape.
local term1 = math.exp(-lambda * minNumber)
local term2 = math.exp(-lambda * maxNumber)
local inner_log = term1 - u * (term1 - term2)
local finalValue = (-1 / lambda) * math.log(inner_log)
return finalValue
end
function calculateMinMaxMean(data: { number }): (number, number, number)
if #data == 0 then return nil, nil, nil end
local min = data[1]
local max = data[1]
local sum = 0
for _, value in ipairs(data) do
if value < min then min = value end
if value > max then max = value end
sum = sum + value
end
local mean = sum / #data
return min, max, mean
end
function calculateMedian(data: { number }): number
if #data == 0 then return nil end
local sortedData = {}
for _, v in ipairs(data) do
table.insert(sortedData, v)
end
table.sort(sortedData)
local n = #sortedData
if n % 2 == 1 then
return sortedData[math.floor(n / 2) + 1]
end
local mid1 = sortedData[n / 2]
local mid2 = sortedData[n / 2 + 1]
return (mid1 + mid2) / 2
end
function calculateMode(data: { number }): number
if #data == 0 then return nil end
local counts = {}
for _, value in ipairs(data) do
counts[value] = (counts[value] or 0) + 1
end
local mode = nil
local maxCount = 0
for value, count in pairs(counts) do
if count > maxCount then
maxCount = count
mode = value
end
end
return mode
end
local chances = {}
local TestArray = {}
local TryAmount = 1e3
for i = 1,TryAmount do
local size = ExponentialBiased(1, 1e5, 0.05) -- higher lambda = more rarer, like 0.1
TestArray[math.floor(size)] = (TestArray[math.floor(size)] or 0) + 1
end
local str = ""
for _, value in TestArray do
str ..= #str > 0 and `, {value}` or value
end
print(`range: {str}`)
for i, v in pairs(TestArray) do
table.insert(chances, (v / TryAmount) * 1e2)
end
local minChance, maxChance, meanChance = calculateMinMaxMean(chances)
local medianChance = calculateMedian(chances)
local modeChance = calculateMode(chances)
print(string.format("Minimum Chance: %.4f%%", minChance))
print(string.format("Maximum Chance: %.4f%%", maxChance))
print(string.format("Mean (Average) Chance: %.4f%%", meanChance))
print(string.format("Median Chance: %.4f%%", medianChance))
print(string.format("Mode Chance: %.4f%%", modeChance))
--[[
output for 0.05 lambda:
16:03:58.254 range: 48, 46, 42, 45, 41, 43, 31, 34, 41, 31, 34, 20, 24, 18, 24, 13, 20, 28, 20, 29, 16, 17, 11, 13, 18, 15, 15, 10, 19, 13, 11, 12, 12, 3, 8, 5, 7, 10, 8, 7, 7, 4, 3, 6, 5, 7, 6, 3, 8, 2, 5, 6, 4, 2, 5, 1, 3, 3, 2, 2, 3, 4, 2, 2, 1, 5, 2, 3, 1, 2, 1, 2, 2, 1, 2, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - Server - Script:81
16:03:58.254 Minimum Chance: 0.1000% - Server - Script:91
16:03:58.254 Maximum Chance: 4.8000% - Server - Script:92
16:03:58.254 Mean (Average) Chance: 1.0870% - Server - Script:93
16:03:58.254 Median Chance: 0.5000% - Server - Script:94
16:03:58.254 Mode Chance: 0.1000% - Server - Script:95
]]

this ExponentialBiased function, by increasing the number value of “lambda” will make higher potential to return the smaller numbers over bigger numbers, but decreasing the lambda will make higher potential to return bigger numbers over smaller numbers.