• 游戏里面的容斥原理


    我们处理游戏Boss掉落时经常碰到一个问题,假设这个BOSS会掉3个部位的装备,

    武器:20% 衣服:30% 头盔:50%

    那么求,期望次数多少,可以集齐这三件装备

    作为程序员,我们先来一段暴力破解,循环1000000万次,也便于我们验证解果

    math.randomseed(tostring(os.time()):reverse():sub(1, 6))
    
    local counts = {}
    
    local p1, p2, p3 = 0.2, 0.3, 0.5
    
    for j=1, 10000 do
        local a1, a2, a3 = 0, 0, 0
    
        for i=1,1000000 do
            local r = math.random()
            if r > p3 then
                a3 = a3 + 1
            elseif r > p2 then
                a2 = a2 + 1
            else
            --elseif r > p1 then
                a1 = a1 + 1
            end
    
            if a1 > 0 and a2 > 0 and a3 > 0 then
                table.insert(counts, i)
                break
            end
        end
    end
    
    print(avg_count/#counts) --打印结果,当然你不一定是这个值:6.6702

    但是这出来也只是个近似值,得到一个靠谱值,这个时候容斥原理就出来了,以下解释得真好:

    如果被计数的事物有A、B、C三类,那么,A类和B类和C类元素个数总和= A类元素个数+ B类元素个数+C类元素个数—既是A类又是B类的元素个数—既是A类又是C类的元素个数—既是B类又是C类的元素个数+既是A类又是B类而且是C类的元素个数。(A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C)

    再放一下图:

    所以:算法变成这样:

    print(1/p1+1/p2+1/p3 - 1/(p1+p2) - 1/(p2+p3) - 1/(p1+p3) + 1/(p1+p2+p3))

    那如果部位多,那也真够累的,写个通用算法:

    --传入数组,取里面的几个元素进行组合
    local function combine(arr, n, m, b, result)
        n = n or #arr
    --print(' combine', n, m, arr[n])
        assert(n >= m)
        for i=m, n, 1 do
            b[m] = i
            if m > 1 then
                combine(arr, i - 1, m - 1, b, result)
            else
                --用于打印
                local s = {}
                table.foreach(b,function(_,v)
                    table.insert(s,arr[v])
                end)
                table.insert(result, s)
            end
        end
    end
    
    --combine({'a','b','c', 'd'},nil,2,{})
    
    --将数组相加
    local function add(arr)
        assert(type(arr)=='table')
        local r = 0
        for i,v in pairs(arr) do
            r = r + v
        end
        return r
    end
    
    --容斥原理
    function InclusionExclusion (ppp)
        local result = 0
        for i,v in ipairs(ppp) do
            result = result + 1/v
        end
    
    
        local count = #ppp
        for i=2,count - 1 do
            local c = {}
            combine(ppp, nil, i, {}, c)
            --遍历所有的组合
            table.foreach(c, function(_,v)
                --计算组合里面所有的数
                result = result - 1/add(v)
            end)
        end
    
        result = result + 1/add(ppp)
    
        return result
    end
    
    print(InclusionExclusion({p1,p2,p3}) --打印值:6.6547619047619
  • 相关阅读:
    AndroidStudio build.gradle 报错
    点九图片的显示内容区域应作何理解
    【Android Studio快捷键】之导入相应包声明(import packages)
    ListView之EmptyView
    Activity 设置切换动画
    android 中 系统日期时间的获取
    解决Activity启动黑屏及设置android:windowIsTranslucent不兼容activity切换动画问题
    android选择图片或拍照图片上传到服务器(包括上传参数)
    Spring 4 官方文档学习(十)数据访问之OXM
    Spring 4 官方文档学习(十)数据访问之ORM
  • 原文地址:https://www.cnblogs.com/linbc/p/7171699.html
Copyright © 2020-2023  润新知