Comparing Numbers, Version I


题目

Paula在两张纸条上各写一个整数,这两个整数只要求彼此不同,没有其他限制。

她把两张纸分别藏在两只手里。Victor随机选一只手,Paula打开这只手,让Victor看到其中一个数。

现在Victor必须判断:他看到的这个数是两数中较大的那个,还是较小的那个。若猜对,赢\(1\)美元;若猜错,输\(1\)美元。

显然,Victor至少可以做到不输不赢(例如抛硬币决定猜“大”或“小”)。问题是:在完全不了解Paula心理、也不知道她会选什么数的前提下,Victor是否有办法把胜率提高到严格大于\(\frac{1}{2}\)

分析

答案是:可以

核心思路是先随机生成一个“阈值”\(T\),再用它和看到的数比较。

先在游戏开始前,Victor从某个连续分布中随机取一个实数\(T\)(例如标准正态分布)。然后采用规则:

  • 若看到的数\(x>T\),就猜“这是较大的数”;
  • 若看到的数\(x<T\),就猜“这是较小的数”。

设Paula写的两个整数是\(a<b\)

  1. 若Victor看到的是\(a\)(概率\(\frac{1}{2}\)),他猜对当且仅当\(a\lt T\),概率为\(1-F(a)\),其中\(F\)\(T\)的分布函数。
  2. 若Victor看到的是\(b\)(概率\(\frac{1}{2}\)),他猜对当且仅当\(b\gt T\),概率为\(F(b)\)

所以总胜率

\[ P(\text{win})=\frac{1}{2}(1-F(a))+\frac{1}{2}F(b)=\frac{1}{2}+\frac{1}{2}(F(b)-F(a)).\]

因为\(a<b\)\(T\)来自连续分布,故\(F(b)>F(a)\),于是

\[ P(\text{win})>\frac{1}{2}.\]

这说明不论Paula选哪两个不同整数,Victor都能用这个策略把期望收益做到严格为正。

C++程序

下面这个程序用蒙特卡洛方法模拟这道题。

程序固定若干组\((a,b)\)(其中\(a<b\)),并比较三种策略的胜率:

  1. 阈值策略(本题解法):先采样\(T\sim N(0,\sigma^2)\),若\(x>T\)猜“大”,否则猜“小”;
  2. 纯随机策略:等概率猜“大/小”;
  3. 永远猜“大”(但抽到哪只手是随机的)。

如果你想直接下载源码,可以点击这里:comparing_numbers_i_sim.cpp.txt。由于GRAV通常不会直接提供.cpp文件,这里使用可下载的文本版本;下载后把文件名改回comparing_numbers_i_sim.cpp即可。

#include <cmath>
#include <iomanip>
#include <iostream>
#include <random>
#include <utility>
#include <vector>

using namespace std;

double normalCdf(double x)
{
    return 0.5 * erfc(-x / sqrt(2.0));
}

double simulateThresholdStrategy(int a, int b, long long trials, double sigma, mt19937_64& rng)
{
    uniform_int_distribution<int> revealDist(0, 1);
    normal_distribution<double> thresholdDist(0.0, sigma);

    long long wins = 0;
    for (long long i = 0; i < trials; ++i)
    {
        int revealed = (revealDist(rng) == 0) ? a : b;
        double t = thresholdDist(rng);

        bool guessLarger = (revealed > t);
        bool correct = (revealed == b && guessLarger) || (revealed == a && !guessLarger);
        if (correct)
        {
            ++wins;
        }
    }

    return static_cast<double>(wins) / static_cast<double>(trials);
}

double simulateRandomGuess(long long trials, mt19937_64& rng)
{
    uniform_int_distribution<int> coin(0, 1);
    long long wins = 0;
    for (long long i = 0; i < trials; ++i)
    {
        if (coin(rng) == 1)
        {
            ++wins;
        }
    }
    return static_cast<double>(wins) / static_cast<double>(trials);
}

double simulateAlwaysLarger(long long trials, mt19937_64& rng)
{
    uniform_int_distribution<int> revealDist(0, 1);
    long long wins = 0;
    for (long long i = 0; i < trials; ++i)
    {
        if (revealDist(rng) == 1)
        {
            ++wins;
        }
    }
    return static_cast<double>(wins) / static_cast<double>(trials);
}

int main()
{
    const long long trials = 2000000;
    const double sigma = 10.0;

    vector<pair<int, int>> tests = {
        {-3, 3},
        {0, 1},
        {-8, -2},
        {2, 9},
        {-1, 6},
        {1000, 1001}
    };

    random_device rd;
    mt19937_64 rng(rd());

    cout << fixed << setprecision(6);
    cout << "Trials per test = " << trials << "\\n";
    cout << "Threshold T ~ N(0, " << sigma * sigma << ")\\n\\n";

    for (const auto& [a, b] : tests)
    {
        double empirical = simulateThresholdStrategy(a, b, trials, sigma, rng);

        double fa = normalCdf(static_cast<double>(a) / sigma);
        double fb = normalCdf(static_cast<double>(b) / sigma);
        double theoretical = 0.5 + 0.5 * (fb - fa);

        double randomGuess = simulateRandomGuess(trials, rng);
        double alwaysLarger = simulateAlwaysLarger(trials, rng);

        cout << "a=" << a << ", b=" << b << "\\n";
        cout << "  threshold strategy (empirical): " << empirical << "\\n";
        cout << "  threshold strategy (theory)   : " << theoretical << "\\n";
        cout << "  random guess baseline         : " << randomGuess << "\\n";
        cout << "  always guess larger baseline  : " << alwaysLarger << "\\n\\n";
    }

    return 0;
}

你会看到阈值策略的胜率稳定高于\(0.5\),与前面的理论公式一致;而后两种基准策略都只能在\(0.5\)附近波动。

一个极端情况

补一个“优势极小但仍严格大于\(\frac{1}{2}\)”的例子:取\(a=1000,b=1001\),仍用上面程序中的\(\sigma=10\)

这时理论胜率仍是

\[ P(\text{win})=\frac{1}{2}+\frac{1}{2}\bigl(F(1001)-F(1000)\bigr)>\frac{1}{2},\]

但由于\(1000,1001\)都离分布中心\(0\)极远,\(F(1001)-F(1000)\)会非常非常小,模拟结果通常会显示成\(0.500xxx\)甚至四舍五入后几乎就是\(0.5\)

这正好说明:理论上“严格更优”与实践中“优势肉眼几乎看不见”可以同时成立。

Previous Next