Trying to figure out how to make a math percentage function

I am writing an addon that needs a percentage system and I am at a loss on how to program one. The reason I need it is so i can apply an argument to my table entries. Example code below (Pseudo Code):



local table = {
     {"ent_entname1", 5},
     {"ent_entname2", 50},
     {"ent_entname3", 99}
}


Basically ent_entname1 would have a 5% chance of being selected, ent_entname2 would have a 50% chance, ent_entname3 would have a 99% chance of being selected, etc. I again am at a loss on how to program such a thing. So does anyone have any source or suggestions they would like to share? Or if there is a easier way of accomplishing the same goal please suggest away:).

Thanks for all help in advanced.

I think what you could do is take advantage of the math.random function. Here’s an example.

[lua]function PickRandomEnt(tab)
//Shitty way of adding them up. There are much
//better ways of adding up your percentage value
//for tables of varying length/size. This is just
//for an example. Here, we add up the three numbers
//in your example (5, 50, 99).
local max = tab[1][2] + tab[2][2] + tab[3][2];

//Generate a number from 1 to the sum of those percentages.
local rand = math.random(max);

if (0 < rand) and (rand <= tab[1][2]) then
	//Do something with ent_entname1...
elseif (tab[1][2] < rand) and (rand <= tab[2][2]) then
	//Do something with ent_entname2...
elseif (tab[2][2] < rand) and (rand <= tab[3][2]) then
	//Do something with ent_entname3...
else
	//Throw an error, play a sound, spawn a control ent... Do whatever!
end

end

//Call PickRandomEnt with your table in the parameters.[/lua]

This is a pretty sloppy and slower/not as efficient way of doing it. If you put your imagination to it, you can probably make something better up. However, this is the general gist of how it can be done.

Fixed the syntax of double-comparisons.

Ill play around with this some. Thank you for the snippet :slight_smile:

“elseif tab[1][2] < rand” is unnecessary, by the time it reaches that elseif the condition is already true

[lua]-- if you want these to be exact percentages, tally them up to 100
– or don’t, it doesn’t really matter
local t = {
{“very_rare_entity”, 5},
{“rare_entity”, 10},
{“common_entity”, 25},
{“very_common_entity”, 60},
}

– helper function to sort by ascending probability
local function sortProb(a, b)
return a[1] > b[1]
end
local function pickWeighted(t)
table.sort(t, sortProb)

local range = 0
for i, tbl in ipairs(t) do
	range = range + tbl[2]
end

-- pick random number in the the range
-- this allows for probabilities that aren't integers e.g. 0.5%
local pick = math.Rand(1, range)

-- 1st item will be 0 + 5 = 5, so between 0 and 5 =&gt; 5%
-- 2nd item will be 5 + 10 = 15, so between 5 and 15 =&gt; 10%
-- 3rd item will be 15 + 25 = 40, so between 15 and 40 =&gt; 25%
-- 4th item will be 40 + 60 = 100, so between 40 and 100 =&gt; 60%
-- etc etc
local ct = 0
for i, tbl in ipairs(t) do
	if pick &lt; ct + tbl[2] then
		return tbl[1]
	end
	ct = ct + tbl[2]
end

end

print(pickWeighted(t))[/lua]

if the iterator part is confusing, consider the following:

note the numbers on the bottom: if you think of each item as a percentage chance out of some total (in this case, 100) those are the start and end of the ranges, so you can pick a random number between 0 and 100 and it’ll have about a 25% chance of landing between 15.0 and 40.0.

thus the “ct = ct + tbl[2]”, which gets you the end of the last range and the start of the next one.

Well this is actually really really helpful. Thank you!

So one last question directed at Luni’s code. Theoretically speaking, the higher the second argument the higher the “chance” of picking the item? that being said there is no limit to that number?

Yes. The ‘100%’ value is just the sum of the weights, so the table you pass to pickWeighted() can have any number of elements with completely-arbitrary weights (although adding more means every entry’s chance of appearing goes down in proportion to the new item’s weight – if you have weights of 1, 5 and 10 and you add an entry with a weight of 1000, guess which one’s always showing up).

Just don’t give any of them negative or zero weights. I haven’t tried that but I’m sure it would be bad.

Why are you sorting the table though? That doesn’t really seem necessary to me. Also shouldn’t math.Rand start from 0 instead of 1?