寻找更好的算法来解决这种概率/组合游戏问题。

3
假设我们有从1到10的10个整数。
我们还有一些玩家,每个玩家都会得到来自这个集合的不同随机数字。
现在,玩家们开始通过说:“我的数字在初始的1到10集合的一个子集中”来说出他或她的数字。例如,“我的数字是8、9或10”。
我们想要做出关于还没有说话的玩家数量的假设(当然,对于每个沉默的玩家,给出初始信息是相同的假设)。
假设我们有5个玩家,前3个玩家依次说:
1. 我的数字是8、9或10。 2. 我的数字是7或6。 3. 我的数字是7、6、9或10。
现在我们需要计算下一个玩家具有特定数字的可能性(概率),比如下一个玩家具有数字7的可能性是多少。
当然这只是一个例子,每个玩家可以用任何形式提供信息(例如“1或10”,“1到10”等)。
这是某种众所周知的问题吗?或者有人看到了一个好的方法吗? 我真的希望这个解决方案能够高效,所以暴力破解不是好的选择。我认为它可能直接与贝叶斯定理相关,但不能100%确定它可以应用于这里。
示例:
简单情况下有2个玩家和12345个数字。第一个玩家有4或5。 然后对于第二个玩家,他有25%的机会拥有1,但只有12.5%的机会拥有4,因为在第一个玩家说出他的手牌信息之后有2种可能的结果。
1234或1235,我们可以看到1是(1/4*2)/2=1/4,4是(1/4*1)/2= 1/8 这就是我所谓的暴力解决方案,通过分析每个组合来计算所有可能的组合并推导出数字概率。
更新:
Mr.Wizard提出的解决方案可行。
如果您想知道它的样子,这是代码:
class Program
    {
        static void Main()
        {
            int length = 5;
            double[][] matrix = new double[length][];
            for (int i = 0; i < length; i++) {
                matrix[i] = new double[length];
            }

            for (int i = 0; i < length; i++) {
                for (int j = 0; j < length; j++) {
                    matrix[i][j] = 1;
                }
            }

            matrix[0] = new double[] { 0, 0, 0, 1, 1 };
            matrix[1] = new double[] { 0, 0, 1, 1, 0 };
            matrix[2] = new double[] { 0, 0, 0, 0, 1 };

            DumpMatrix(matrix);
            while(true)
            {
                NormalizeColumns(matrix);
                DumpMatrix(matrix);
                NormalizeRows(matrix);
                DumpMatrix(matrix);
                Console.ReadLine();
            }
        }

        private static void NormalizeRows(double[][] matrix)
        {
            for (int i = 0; i < matrix.Length; i++)
            {
                double sum = matrix[i].Sum();
                for (int j = 0; j < matrix.Length; j++) {
                    matrix[i][j] = matrix[i][j] / sum;
                }
            }
        }

        private static void NormalizeColumns(double[][] matrix)
        {
            for (int j = 0; j < matrix.Length; j++)
            {
                double columnSum = 0;
                for (int i = 0; i < matrix.Length; i++)
                {
                    columnSum += matrix[i][j];
                }
                for (int i = 0; i < matrix.Length; i++) {
                    matrix[i][j] = matrix[i][j] / columnSum;
                }
            }
        }

        private static void DumpMatrix(double[][] matrix)
        {
            for (int i = 0; i < matrix.Length; i++) {
                for (int j = 0; j < matrix.Length; j++) {
                    Console.Write(matrix[i][j].ToString("0.#####").PadRight(8));
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }

    }

虽然从这个例子可以清楚地看出它并不是很快地接近最终结果。

在这里,玩家3有5点,玩家1和2可以分别有4或53或4,这意味着玩家1有4是因为玩家3得到了5,而玩家2有3是因为玩家2得到了4。但是,在许多迭代之后,我们在匹配列中为玩家1和2逼近1个值。

4个回答

4
尝试构建一个以玩家为一侧、数字为另一侧的图。如果根据他们所说的,玩家可能拥有该数字,则该玩家和数字之间存在一条边。对于每条边,您希望知道均匀随机完美匹配包含该边的概率。
不幸的是,如果这个问题有一个精确的多项式时间算法,那么#P,一个包含 NP(事实上包含整个多项式层次结构,由Toda's theorem得出)的类,将等于P。
理论上至少可以通过Jerrum, Sinclair 和 Vigoda的复杂算法来估计概率。但我不确定是否有人曾经实现过该算法。

我对这个答案印象深刻,但您能否详细阐述每一段的内容呢? - Valentin Kuzub
左边是玩家,右边是数字,其他部分不清楚。 - Valentin Kuzub

2
你应该建立一个概率树状图
对于你的例子:
__
  |___ 0.5 A=4 __
  |              |___ 0.25 B=1
  |              |___ 0.25 B=2
  |              |___ 0.25 B=3
  |              |___ 0.25 B=5
  |___ 0.5 A=5 __
                 |___ 0.25 B=1
                 |___ 0.25 B=2
                 |___ 0.25 B=3
                 |___ 0.25 B=4

树表示诸如p(B=1|A=4)=0.25之类的语句。
因此,

p(B=1|A=4或A=5)= p(B=1|A=4)+p(B=1|A=5)= 0.5*0.25 + 0.5*0.25= 0.25

p(B=4|A=4或A=5)= p(B=4|A=4)+p(B=4|A=5)= 0 + 0.5*0.25= 0.125

您可以在游戏的任何阶段动态扩展树,并相应地计算每个假设的概率。
我认为对于一般情况没有捷径。

这看起来像是我所描述的完全枚举解决方案/暴力破解。这正是我目前的做法,谢谢。但是想象一下,如果我们有1000个数字和10个玩家,我们最终会得到大量的组合。这是否意味着在这种情况下问题变得无法解决?基本上,我想重新表达我的问题:除了概率树之外,还有什么其他方法? - Valentin Kuzub

1

我可能有些偏离主题,但我认为对每行和每列进行重复归一化的过程将会收敛于正确的值。

如果您从一个n*n矩阵开始,其中零表示不能,而一表示可以,那么对于您的示例:

0   0   0   1   1
1   1   1   1   1
1   1   1   1   1
1   1   1   1   1
1   1   1   1   1

这意味着第一行代表玩家1,只能是4或5,其他情况未知。然后,如果我们将每一行归一化,使其总和为1,我们得到:

0.  0.  0.  0.5 0.5
0.2 0.2 0.2 0.2 0.2
0.2 0.2 0.2 0.2 0.2
0.2 0.2 0.2 0.2 0.2
0.2 0.2 0.2 0.2 0.2

然后,对于每一列:

0.      0.      0.      0.384615    0.384615
0.25    0.25    0.25    0.153846    0.153846
0.25    0.25    0.25    0.153846    0.153846
0.25    0.25    0.25    0.153846    0.153846
0.25    0.25    0.25    0.153846    0.153846

重复这个过程15次,我们得到:
0.      0.      0.      0.5     0.5
0.25    0.25    0.25    0.125   0.125
0.25    0.25    0.25    0.125   0.125
0.25    0.25    0.25    0.125   0.125
0.25    0.25    0.25    0.125   0.125

如果原始参数不是不可能的话,那么最终矩阵中的每一行和每一列的总和应该约等于1。
我没有证明这是正确的,但如果您已经有一个工作的暴力实现,检查结果的相关性应该很容易。

它可以工作,已确认。我添加了演示这种方法的代码。不确定它是否最佳,因为对于我的例子来说,需要太多次迭代才能最终到达目标。 - Valentin Kuzub
@Valentin 我看到你的测试用例中有很多零,这意味着它高度规定。在我的测试中,我使用了很少的零,这样收敛得更快。你在另一条评论中写道:“但是假设我们有1000个数字和10个玩家”,我认为这描述了一个稀疏矩阵(很少的零)。如果这更符合你实际的使用情况,那么这可能仍然有效。 - Mr.Wizard
@Valentin,虽然我会很高兴如果这被证明是您使用的解决方案,但没有必要急于接受答案。我很想知道在一个足够大而又可以用您的枚举方法计算的情况下,这个算法的表现如何,以及它是否更能代表您实际数据的情况。 - Mr.Wizard
我的情况无法通过枚举计算,对于我的大型案例,我没有能够工作的算法。就像我所说,想象一下有10个玩家和1000个数字,我认为我们无法在这里枚举所有可能性。而另一方面,这种概率方法容易操作并提供实际有用的结果! - Valentin Kuzub
@Valentin,我在math.SE上泛泛地询问了一下我的算法。根据你的数据,这可能会有所帮助。http://math.stackexchange.com/questions/80538/matrix-algorithm-convergence - Mr.Wizard
显示剩余2条评论

0

现在不确定确切的计算方法,但我认为你可以比使用完整的树更容易地完成它。

以简单的例子为例:有5个数字,你想知道B拥有3的概率:

  • 总共有4个可能的数字,因为其中一个已经被取走了
  • 无论A做什么,3都不能被取走,所以3始终是这4个数字之一。

由此我们可以直接得出概率为1/4 = 25%

对于1和2也是一样的,而对于4和5,你只有50%的机会使数字在池中,因此它减少到0.25*0.5 = 0.125


对于更大的例子: 1到10,像你上面说的那样有5个玩家。

现在假设你想知道掷出6的可能性。

两个没有说话的玩家有相同的概率。 一个人说他有25%的6,另一个人说他有50%的6。 我现在不确定具体是如何计算的,但是你可以计算“他们中的一个有6”的概率。因为其中一个有50%,另一个有25%,所以加起来应该是60%左右(不是简单地把它们加起来……两次50%是很大的机会,但不是确定的命中)。

让我们假设这个例子是60%。现在我们有10个数字,其中3个被选走了,剩下7个可供选择= 1/7〜14%。 因此,对于任何可用的数字,我们有14%的概率。但现在6只有40%的机会出现,所以我认为我们有0.14 * 0.4 = 0.056,也就是5.6%的机会得到6。

无论你拥有什么信息,你都可以计算出想要了解的数字被取走的概率,以及命中剩下X个数字中恰好一个的概率,并将它们相乘。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接