[Help] probability with weighting

Hi guys,

maybe you can help me with this function, I am not able to think anymore…

I want to find a function, that gaves me a random index of space, but (!) with weighting.
I thought, it would be a good idea to half the probability every time I’m going through the loop.
This works fine, but I don’t like it really…

The main idea is, to create a function, that returns a value (for example a weapon), that have a possibility to spawn.
For example:
RPG have a possibility to spawn of 10%
PISTOL - 75%
RIFLE - 40%
GRANADE - 60%
[…]

Now I have this:

local space = {
   {"A", "VALUE"}
   {"A", "VALUE"}
   {"A", "VALUE"}
   {"A", "VALUE"}
}

function dwRand(space)
    local rn = math.random(1, 100)
    local p = 100
    local sum = 0
    
    for i, j in ipairs(space) do
        p = p / 2
        sum = sum + p
        
        if ( math.floor(sum,3) >= math.floor(rn,3) ) then
            return i
        end
        
    end
    
    return 1
    
end

Do you think it is a good idea to give every index a weighting?
like this:

local space = {
    {"sabregt", 0.05},
    {"fbi", 0.10},
    {"cheetah", 0.25},
    {"emperor", 0.33},
    {"banshee", 0.50},
    {"taxi", 0.70},
    {"bfinjection", 0.75},
    {"blista", 0.75},
    {"sentinel", 0.80},
}

I tried this, and the result was not what I was expecting…
because:

  1. rand = 0.55 > weight = 0.05 :white_check_mark:
  2. rand = 0.55 > weight = 0.10 :white_check_mark:
  3. rand = 0.55 > weight = 0.33 :white_check_mark:
  4. rand = 0.55 > weight = 0.50 :negative_squared_cross_mark: - spawn banshee
    okay so far…
    but if I am getting 0.75 or greater, for the random number, the blista is never spawning. But I still want to keep the possibility…

tl;tr: I am trying to find a function, that allows me to get a random array index, in combination of a weightbased possibility.

I hope this thread is not going too long; had a looooong night :sleeping:

Try tinkering with this, I havent tested it, just adapted this code which “should” give you want you need. Read the link above for more of an explanation of how this works. And let me know if it does alright for you :slight_smile: (again you may want to look over it some before testing, I’m extremely tired and didn’t test it :stuck_out_tongue:)

local space = {
    {"sabregt", 5},
    {"fbi", 10},
    {"cheetah", 25},
    {"emperor", 33},
    {"banshee", 50},
    {"taxi", 70},
    {"bfinjection", 75},
    {"blista", 75},
    {"sentinel", 80},
}

function chooseWithChance(args)
     local argCount = #args
     local sumOfChances = 0

     for i = 1, argCount do
         sumOfChances = sumOfChances + args[i]
     end

     local randomDouble = math.random(sumOfChances)

     while (sumOfChances > randomDouble) do
         sumOfChances = sumOfChances - args[argCount]
         argCount = argCount - 1
     end

     return (argCount)
 end

function drawRand()
    local probabilities = {}

    for key,value in pairs(space) do
       probabilites.Add(value)
    end

    return space[chooseWithChance(probabilites)]
end

Can’t really think now either, but I guess something along these lines might work.

spawnables = {
    {"A", 1},
    {"B", 3},
    {"C", 0.5}
}

-- And then later on --

fullChance = 0
for _, item in pairs(spawnables) do
    fullChance = fullChance + item[2]
end

chance = math.random(fullChance)

addedUp = 0
for _, item in pairs(spawnables) do
    addedUp = addedUp + item[2]
    if (chance <= addedUp)
        -- Aaaand you got your winner!
    end
end

I don’t believe this approach would work considering when checking chance <= item[2] this is true for multiple items and youd need to specifically order your list so that this would start with lowest weight first. This would also evaluate to true for multiple items that could have the same weight (chance), which would then mean youd have to check for any other item with this same weight, then after that pick randomly between one of those

Fixed the chance thingy. Don’t know how to do the sort thingy (and can’t look it up currently), so I guess you’ll have to do that too @schneehaze.

Thank you both.

-- [2] is probability
local weapons = {
    {"WEAPON_HEAVYSNIPER",      15, "PICKUP_WEAPON_HEAVYSNIPER"},
    {"WEAPON_BULLPUPSHOTGUN",   30, "PICKUP_WEAPON_PUMPSHOTGUN"},
    {"WEAPON_STICKYBOMB",       30, "PICKUP_WEAPON_STICKYBOMB"},
    {"WEAPON_KNIFE",            35, "PICKUP_WEAPON_KNIFE"},
    {"WEAPON_CARBINERIFLE",     40, "PICKUP_WEAPON_CARBINERIFLE"},
    {"WEAPON_COMBATPISTOL",     50, "PICKUP_WEAPON_COMBATPISTOL"},
    {"WEAPON_PISTOL",           50, "PICKUP_WEAPON_PISTOL"}
}

function chooseWithChance(args) 
    local argCount = #args
    local sumOfChances = 0

    for i = 1, argCount do
        sumOfChances = sumOfChances + args[i][2]
    end
    
    local randomDouble = math.random(sumOfChances)
    
    while sumOfChances > randomDouble do
        sumOfChances = sumOfChances - args[argCount][2]
        argCount = argCount - 1
    end
    
    --f**king 0
    if( argCount == 0 ) then
        argCount = #args --get the last element
    end
    
    local gotP = args[argCount][2]
    --check if another object has the same probability
    local sameP = {}
    local j = 1
    for i = 1, argCount do
         if(gotP == args[i][2]) then
            sameP[j] = i
            j = j + 1
        end
    end
    if( #sameP > 1 ) then
        --Chat("Weapon: "..args[argCount][1])
        argCount = sameP[math.random(#sameP)]
        --Chat("RandomWeapon: "..args[argCount][1])
    end
    
    return argCount
end

Yessssss

Image is showing the randomness between weapons, that has the same probability.

1 Like