Keeping a random % chance to items/drops/spawnrates consistent when chances don't add up to 100

Something basic as:

local chances = { -- %
	["Common"] 		= 50,
	["Rare"] 		= 30,
	["Epic"] 		= 15,
	["Legendary"] 	= 5,
}

Is easy to work with, as these numbers add up to 100 (%), which would make ‘50’ also 50% or 1/2.

Do all ‘big games’ use something like this, or is there a better system to accomodate many more additional chances (only listing three here)

local chances = {
	["Cat"] 		= 50,
	["Dog"] 		= 50,
	["Super Epic Dragon"] 		= 0.5,
}

Would you manually change the odds of Cat and Dog to 49.75 respectively so the sum still equals 100?
Or is there a better way to do this, as manually trying to figure out what the introduction of a new item with a percentage means for a whole list of … maybe 20 other items seems like an unnecessary burden.

A solution that seemed right at first was to change the Random.new():NextNumber(0, 100) to the sum of these odds (in this case 50 + 50 + 0.5 = NextNumber(0, 100.5) )
But what does does is slightly skew the odds.
The 50% chance isn’t exactly 50 anymore, but rather ( 50 / 100.5 * 100 ) = 49.75124…

You might argue that yes, that’s pretty much 50% but when another item is introduced

local ItemChances = {
	["Cat"] 					= 50,
	["Dog"] 					= 50,
	["Golden Monkey"]			= 20,
	["Super Epic Dragon"] 		= 0.5,
}

It be a Golden Monkey, the relative weight of these ( X on 120.5 ) is fine, but when trying to format them as a percentage it’s even more skewed (50% gets ~ 41,5%).

So what do you do?

  • Do you lie and display the odds as 50% of getting a Cat ?

  • Do you display a less pretty number and label the chance of a cat as 42% ?

  • Do you mention the odds as 1 in 2.5 ? (120.5/50=2,41)

Or is there a way to actually keep the odds at 50% by changing this total sum to an adjusted number of weight somehow?

  • Display a less pretty number and label the chance of a cat as 42% OR
  • Mention the odds as 1 in 2.5 ? (120.5/50=2,41)
    You can’t lie.
    I wouldn’t really care which one it was if I was a player, maybe you could have an option to toggle it?

I think what you may be looking for is called a “Weighted Chance System”, where you would gather up the total odds and do some fancy math work.

There are a few really good posts here already that helped me when I was doing a rng game, I’ll link them below, and hope they can help you also.

3 Likes

Not exactly sure what you’re trying to say but percentages have to add up to 100. If you want to go past that then you’d be using a weight system. If you want to find the % chance using a weight system, it’s weight / total weight * 100

you can use a weighted chance system Explained there :arrow_up:

now their chances are proportional, so their sum doesn’t have to be 100

A proper % only works when the sum of these odds is 100 and done as Random.new():NextNumber(0, 100), but that doesn’t allow for the sum to be higher than 100 or else items past the counted 100 aren’t included and won’t ever be picked

The for loop will go over these items, but after 100 (cat, dog, --) it stops taking into account any others because they come later alphabetically.

for item, chance in pairs(ItemChances) do
	counter += chance
	if counter >= pickedNumber then
		pickedData = item
		break
	end
end

That’s why yes, a weighted system where Random.new():NextNumber(0, TOTAL_CHANCE_SUM) is the maximum would be better, as it ensures all items are included.
The issue with that however is that the chance of 50 isn’t actuallt 50%, but rather 50 in 120.5 (which was that ~42%)
Looking for a way to keep this 50 chance stay as a 50/100 (or proper 50%), whilst still allowing for the items to exceed a value of 100.

local ItemChances = { -- %
   ["Nothing"] 					= 100,
   ["BigPrize"] 					= 1,
}

Using a weighed system you’d have a 1/101*100 = 0.99% chance of getting the prize.
But the idea is to have this value of 100 not be a weight, but rather a proper percentage.
So for this example it’d mean that you technically never get that prize as it’s 100% chance of nothing (instead of a 99% when using weighted odds)


Having sum of the percentages be over 100 turns it into a weight.
But as soon as they’re a weight, the written percentage no longer applies

So can this be achieved; where dog appears 50% of the times and not ~41.5%?

idk if this is efficient but what I do is I create a table like

local table = {
Item1 = 10,
Item2 = 15,
Item 3 = 10}
with all the weights then I create a new table that

local function pickRandomItem()
local weightTable = {}
for i,v in pairs(itemWeight) do
for _ = 0, v do
table.insert(weightTable, i)
end
end
local randomIndex = math.random(1, #weightTable)
return weightTable[randomIndex]
end

If you were to do it using the second table it would be impossible for it to actually be a 50% chance since it’s just not how percentage works. So basically, the big games just lie