Модель вероятностей по слотам с вычеркиванием

IgorStepanov · updated July 03, 2023
WEIGHTS = {5, 3, 2}
CASES = 100000
SLOTS = 2

function sumOfWeights(weights)
    local sum = 0
    for element, weight in pairs(weights) do
        sum = sum + weight
    end
    return sum
end

function getRandomElement(weights)
    local sum = 0
    local rnd = sumOfWeights(weights) * math.random()
    local randomElement = nil
    for element, weight in pairs(weights) do
        if rnd >= sum then
            randomElement = element
        else
            return randomElement
        end
        sum = sum + weight
    end
    return randomElement
end

function getPropabilities(weights, slots, cases)
    local buckets = {}
    for i = 1, slots, 1 do
        buckets[i] = {}
        for element, weight in pairs(weights) do
            buckets[i][element] = 0
        end
    end
    for i = 1, cases, 1 do
        local tempWeights = {table.unpack(weights)}
        for i = 1, slots, 1 do
            local randomElement = getRandomElement(tempWeights)
            if randomElement ~= nil then
                buckets[i][randomElement] = buckets[i][randomElement] + 1
                tempWeights[randomElement] = nil
            end
        end
    end
    local propabilities = {}
    local quantities = {}
    local sumPropabilities = {}
    for i, slot in pairs(buckets) do
        for element, quantity in pairs(slot) do
            if propabilities[i] == nil then
                propabilities[i] = {}
            end
            propabilities[i][element] = string.format("%.2f", 100 * quantity / cases) .. "%"
            if quantities[element] == nil then
                quantities[element] = 0
            end
            quantities[element] = quantities[element] + quantity
        end
    end
    for element, quantity in pairs(quantities) do
        sumPropabilities[element] = string.format("%.2f", 100 * quantity / cases) .. "%"
    end
    return propabilities, sumPropabilities
end

function print_table(node)
    local cache, stack, output = {},{},{}
    local depth = 1
    local output_str = "{\n"
    while true do
        local size = 0
        for k,v in pairs(node) do
            size = size + 1
        end
        local cur_index = 1
        for k,v in pairs(node) do
            if (cache[node] == nil) or (cur_index >= cache[node]) then

                if (string.find(output_str,"}",output_str:len())) then
                    output_str = output_str .. ",\n"
                elseif not (string.find(output_str,"\n",output_str:len())) then
                    output_str = output_str .. "\n"
                end
                -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
                table.insert(output,output_str)
                output_str = ""

                local key
                if (type(k) == "number" or type(k) == "boolean") then
                    key = "["..tostring(k).."]"
                else
                    key = "['"..tostring(k).."']"
                end
                if (type(v) == "number" or type(v) == "boolean") then
                    output_str = output_str .. string.rep('\t',depth) .. key .. " = "..tostring(v)
                elseif (type(v) == "table") then
                    output_str = output_str .. string.rep('\t',depth) .. key .. " = {\n"
                    table.insert(stack,node)
                    table.insert(stack,v)
                    cache[node] = cur_index+1
                    break
                else
                    output_str = output_str .. string.rep('\t',depth) .. key .. " = '"..tostring(v).."'"
                end
                if (cur_index == size) then
                    output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
                else
                    output_str = output_str .. ","
                end
            else
                -- close the table
                if (cur_index == size) then
                    output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
                end
            end
            cur_index = cur_index + 1
        end
        if (size == 0) then
            output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
        end
        if (#stack > 0) then
            node = stack[#stack]
            stack[#stack] = nil
            depth = cache[node] == nil and depth + 1 or depth - 1
        else
            break
        end
    end
    -- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
    table.insert(output,output_str)
    output_str = table.concat(output)
    print(output_str)
end

print("Сумма весов: " .. sumOfWeights(WEIGHTS))
print("Шансы по слотам:")
local propabilities, sumPropabilities = getPropabilities(WEIGHTS, SLOTS, CASES)
print_table(propabilities)
print("Суммарные шансы:")
print_table(sumPropabilities)
Output

Comments

Please sign up or log in to contribute to the discussion.