关于盲盒概率的计算问题

2021-01-11 14:42:11 +08:00
 songz

背景

问题

var artObjects = ["dbld", "db", "fdm", "hg", "hm", "le", "mef", "mg", "hlbt", "lp", "xtlx", "lw"]

var impossibleObjects = [
    ["fdm", "lw", "mef"],//第 1 个格子不会出现的物品
    ["fdm", "xtlx", "hm"],//第 2 个格子不会出现的物品
    ["dbld", "fdm", "hlbt"],//第 3 个格子不会出现的物品
    ["fdm", "db", "hg"],//...
    ["hlbt", "lp", "db"],//...
    ["lw", "lp", "mef"],
    ["xtlx", "db", "fdm"],
    ["mef", "hg", "hlbt"],
    ["hg", "le", "xtlx"],
    ["le", "lw", "lp"],
    ["mg", "hm", "mef"],
    ["lw", "mg", "le"]
]

报错

<--- Last few GCs --->

[2338:0x108008000]    30104 ms: Scavenge 4052.4 (4129.0) -> 4047.7 (4130.5) MB, 4.0 / 0.0 ms  (average mu = 0.210, current mu = 0.145) allocation failure 
[2338:0x108008000]    30111 ms: Scavenge 4054.0 (4130.5) -> 4049.1 (4131.8) MB, 3.7 / 0.0 ms  (average mu = 0.210, current mu = 0.145) allocation failure 
[2338:0x108008000]    30653 ms: Scavenge 4055.4 (4131.8) -> 4050.6 (4147.8) MB, 538.3 / 0.0 ms  (average mu = 0.210, current mu = 0.145) allocation failure 


<--- JS stacktrace --->

FATAL ERROR: MarkCompactCollector: young object promotion failed Allocation failed - JavaScript heap out of memory
var artObjects = ["女孩", "男孩", "乳牛", "博客", "甜点", "朋克"]

var impossibleObjects = [
    ["甜点", "朋克", "女孩"],
    ["乳牛", "甜点", "博客"],
    ["女孩", "乳牛", "甜点"],
    ["甜点", "乳牛", "男孩"],
    ["博客", "女孩", "男孩"],
    ["朋克", "男孩", "博客"]
]

结果

女孩:[0,41,0,41,0,17]
男孩:[35,29,35,0,0,0]
乳牛:[29,0,0,0,35,35]
博客:[35,0,35,29,0,0]
甜点:[0,0,0,0,52,47]
朋克:[0,29,29,29,11,0]

请教

在 node 环境下如何让脚本在样本等于 MN=12 时正常输出?

js 脚本

数组全排列用的这里 https://github.com/GDUFXRT/NOTES/tree/master/permutation

function permutation(a, m) {

    let result = [];
    let n = a.length;
    m = m || n;

    function recur(_a, tmpResult = []) {
        if (tmpResult.length === m) {

            result.push(tmpResult);

        } else {
            for (let i = 0; i < _a.length; i++) {

                let tmpA = _a.concat();

                let _tmpResult = tmpResult.concat();

                _tmpResult.push(tmpA[i]);

                tmpA.splice(i, 1);
                recur(tmpA, _tmpResult);
            }
        }
    }

    recur(a);
    return result;
}


//12 个物品放在 12 个格子
var artObjects = ["dbld", "db", "fdm", "hg", "hm", "le", "mef", "mg", "hlbt", "lp", "xtlx", "lw"]

var impossibleObjects = [
    ["fdm", "lw", "mef"],//第 1 个格子不会出现的物品
    ["fdm", "xtlx", "hm"],//第 2 个格子不会出现的物品
    ["dbld", "fdm", "hlbt"],//第 3 个格子不会出现的物品
    ["fdm", "db", "hg"],//...
    ["hlbt", "lp", "db"],//...
    ["lw", "lp", "mef"],
    ["xtlx", "db", "fdm"],
    ["mef", "hg", "hlbt"],
    ["hg", "le", "xtlx"],
    ["le", "lw", "lp"],
    ["mg", "hm", "mef"],
    ["lw", "mg", "le"]
]

var allArray = permutation(artObjects)

var arrayGroup = [allArray]

for (var i = 0; i < artObjects.length; i++) {

    arrayGroup[i + 1] = []

    for (var j = 0; j < arrayGroup[i].length; j++) {

        if ((arrayGroup[i][j][i] !== impossibleObjects[i][0]) &&
            (arrayGroup[i][j][i] !== impossibleObjects[i][1]) &&
            (arrayGroup[i][j][i] !== impossibleObjects[i][2]) &&
            (arrayGroup[i][j][i] !== (impossibleObjects[i][3] !== undefined ? impossibleObjects[i][3] : ""))) {

            arrayGroup[i + 1].push(arrayGroup[i][j])
        }
    }

    console.log(arrayGroup[i + 1].length)

}
var finalArray = arrayGroup[arrayGroup.length - 1]

var resultGroup = {}

for (var i = 0; i < artObjects.length; i++) {

    resultGroup[artObjects[i]] = []

    for (var a = 0; a < artObjects.length; a++) {

        resultGroup[artObjects[i]][a] = []
    }

    for (var f = 0; f < finalArray.length; f++) {

        for (var t = 0; t < artObjects.length; t++) {

            if (finalArray[f][t] == artObjects[i]) {

                resultGroup[artObjects[i]][t].push(finalArray[f])
            }
        }

    }
    console.log(artObjects[i] + ":" + JSON.stringify(resultGroup[artObjects[i]].map((count) => Math.floor(count.length / finalArray.length * 100))))

}
3422 次点击
所在节点    Node.js
11 条回复
misdake
2021-01-11 14:53:40 +08:00
上来就求全排列,12!=479001600 种情况,每一个都是 12 长度的字符串数组,每几十上百 GB 内存是不够的
shintendo
2021-01-11 14:56:06 +08:00
虽然你的提问很详细,排版干净令人赏心悦目,但既然是程序崩溃而不是结果错误,其实只要贴出代码然后问为什么崩溃就好,我还读了半天背景问题……话说你这个明显是递归过深爆栈了吧
banricho
2021-01-11 15:00:30 +08:00
能把求助贴发成这样也是套路很深
misdake
2021-01-11 15:06:43 +08:00
如果能每次求出一个排列就进行统计的话,内存就应该够了。而不是把指数级的的可能结果全部放到一个数组里再一个一个统计。
另外推荐仔细学习一下全排列的代码,把你这个 impossibleObjects 的逻辑放到全排列里进行剪枝,性能会好很多。
songz
2021-01-11 15:10:59 +08:00
@misdake 谢谢你的思路!
shintendo
2021-01-11 16:18:15 +08:00
alan0liang
2021-01-11 16:27:35 +08:00
@shintendo 不是爆栈了,爆栈了是这样的:RangeError: Maximum call stack size exceeded

如果只是想强行调大 node 内存上限的话可以用 node --max-old-space-size=4096 file.js 。
songz
2021-01-11 18:01:52 +08:00
@shintendo #6 感谢!代码优雅多了,也不报错!
dangyuluo
2021-01-11 18:07:33 +08:00
不是 stack,是 heap 爆了
krixaar
2021-01-12 11:15:14 +08:00
这个问题看起来简单实际上很炸裂啊……
这就是个 Constrained N-rooks problem,可以抽象成二分图( Bipartite graph ),求排列的个数等同于求二分图完美匹配( Perfect matching )的个数,等同于把矩阵列成 0 和 1,0 代表不可放置,1 代表可放置,然后求这个矩阵的积和式( Permanent )……
求积和式除了暴力之外,还有 Ryser formula,从 StackExchange 抄了个实现代码粗略改了下( Python,因为我懒):
https://gist.github.com/Raka-loah/d11e340998d76829e7b8f81a36846683

12x12 的情况似乎就非常趋近于概率均匀分布了。
no1xsyzy
2021-01-12 14:34:41 +08:00
能不能用 DP (不是分阶段 DP )
每次固定 M-1 个盒子的概率求解某一盒中的概率,直觉上是收敛的或者震荡的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/743836

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX