Give the keys of a non-numerical indexed table different chances of being picked

As the title suggests, I have, for example, this table: [lua]local mytbl = {
[“a”] = “test”,
[“b”] = function() print( “B” ) end,
[“c”] = { 1, 2, 3, 4 }
}[/lua]

The actual values don’t really matter, but basically I want to pick a **key[at random, with a way to make certain values have less/more chance of being picked.

The way I have it right now is pretty ugly, I’m wondering if there’s a simpler way to do it (Or at least if my code could be improved, I kinda rushed it)
[lua]
local mytbl = {
[“a”] = “test”,
[“b”] = function() print( “B” ) end,
[“c”] = { 1, 2, 3, 4 }
}

local chancesTbl = {
[“a”] = 100, – a has the normal chance, 33.33% (Because there are 3 objects in the table)
[“b”] = 50, – b has half the normal chance, 16.66% (Because there are 3 objects in the table)
[“c”] = 150 – a has 1.5 times the normal chance, 50% (Because there are 3 objects in the table)
}

local count, key = table.Count(mytbl), math.Rand(0, 100)
local chances, total, ref = {}, 0, 0
for k, v in pairs( mytbl ) do
total = total + (chancesTbl[k] / count)

chances[k] = (chancesTbl[k] / count) + ref -- Trust me
ref = chances[k]

end

local scale = 100/total – Get a scale, that once all the values in the chances table are multiplied by, they end at 100. (So a random number between 1 and 100 could always determine which object is chosen, even if the chanceTbl values don’t add up to a nice number)

local SortedTable = {} – This table will be numerically indexed, holding all chances from 0 to 100 in a sorted order
for k, v in pairs( chances ) do
chances[k] = v * scale
table.insert( SortedTable, chances[k] )
end
table.sort( SortedTable )

–[[
So, an example SortedTable would be:

1: 20
2: 30
3: 60
4: 100

This way, [1] has a 20% chance, [2] has a 10% chance, [3] has a 30% chance, and [4] has a 40% chance - of being chosen at random

]]

local index
for k, v in ipairs( SortedTable ) do
if v >= key and ( k == 1 or SortedTable[k-1] < key ) then – If our random number is between 2 values in the table
index = v – Store our chance into an index. To compare with the chances table
break
end
end

for k, v in pairs( chances ) do
if v == index then – If the value (chance) matches our index
key = k – Store the key
break
end
end

–[[
And we have our key stored in the ‘key’ table
Now finally, I can use key in ‘mytbl’
]]
[/lua]

There’s an answer here

[editline]15th December 2016[/editline]

Although values over 1 like 1.5 would get selected 100% of the time with that answer… damn

Edit: tested and fixed.
[lua]local chancesTbl = {
[“a”] = 100,
[“b”] = 50,
[“c”] = 150
}

local function GetWeightedRandomKey()
local sum = 0;
for _, chance in pairs(chancesTbl) do
sum = sum + chance
end

local rand = math.random(sum) – if you want to be able to use float values, use math.Rand(0, sum)
local winningKey
for key, chance in pairs(chancesTbl) do
winningKey = key
rand = rand - chance
if rand <= 0 then break end
end

return winningKey
end

for i = 1, 2000 do
print(GetWeightedRandomKey())
–print()
end[/lua]

Output confirmed via 2000 samples (above code at https://www.lua.org/cgi-bin/demo) and the following excel-produced check:


             a     b     c
count        642   335   1023
out of 300   96.3  50.25 153.45

It’s so simple… :frowning: