当前位置:   article > 正文

码蹄集 - MT2024 · 赌石(这题答案是不是有精度误差?)

码蹄集 - MT2024 · 赌石(这题答案是不是有精度误差?)


前言

这篇博客将会给出能通过这道题的代码,但笔者并不能100%理解这道题目。

下文将会详细说明对这道题的理解。

对真理的探索是没错的,以下内容只是我个人对这道题目的看法,没有任何恶意。若个人理解不正确,还请大佬包涵和指正。

赌石

时间限制:1秒
空间限制:64M


题目描述

富饶的璃月街道上有一家石料店,店主小码哥是个精明的商人,为了使他的赌石生意更加红火,他根据赌徒的心理设计了一个有趣的买卖规则:他在店铺的两边放了个小桶,一个桶里有n个红球,另一个有n个蓝球。每一批2n个璞石与这些球一一对应,对每个来买璞石的客户石头都会让他们在原地闭眼旋转数圈后走向一个小桶,若拿到蓝球则可免费获得一块石头,但若拿到红球则需要付出两倍的价钱。

假设每个人每次拿到蓝球和红球的概率相同,现在请你求出一个桶里没球而另一个桶里还剩两个球的概率,精确到小数点后四位。


输入描述

输入一个正整数代表这批璞石的个数


输出描述

输出一个四位小数代表所求答案


样例一

输入
256
  • 1
输出
0.9500
  • 1

题目分析

这题似乎说了一堆没什么用的话,大概意思就是:

两个桶,一个桶里有 n n n个蓝色石头,一个桶里有 n n n个红色石头。

每次随机从两个桶里取出一个石头,问取出 2 n − 2 2n-2 2n2个石头后,剩下的 2 2 2块石头在同一个桶里的概率。

好吧,一道纯数学题。但是每个桶里都有 128 128 128个石头( n = 128 , 2 n = 256 n=128,2n=256 n=128,2n=256)的时候,答案给出的 “取出 254 254 254个石头后剩下的 2 2 2个石头在同一个桶里的概率” 是 0.95 0.95 0.95!!!

emm,这怎么也不符合常识好吧~_~

等概率取出石头,要是说最后两个桶都剩下 1 1 1个石头的概率很大我还相信,但最终一个桶剩下俩石头一个桶空了的概率怎么也不会这么接近 100 % 100\% 100%

@Crazy_seven_sky大佬和我有着类似的看法,他认为:

这道题公式是 2 C 2 n − 2 n − 2 2 C 2 n − 2 n − 2 + C 2 n − 2 n − 1 \frac{2C^{n-2}_{2n-2}}{2C_{2n-2}^{n-2}+C_{2n-2}^{n-1}} 2C2n2n2+C2n2n12C2n2n2,也就是说最终 3 3 3种情况,分别是

  • 1 1
  • 2 0
  • 0 2

但是一个桶取空后就不能再取了,所以不应该是 95 % 95\% 95%

于是我写了个程序来模拟验证:

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;

const int simulationTimes = 10000000;  // 模拟1000万次
const int n = 128;  // 2 * n 一共256个球
int bin1, bin2;  // 桶1中剩下的球,桶2中剩下的球

/* 初始化一次模拟,把两个桶中每个桶都装入n个球 */
void init() {
    bin1 = bin2 = n;
}

/* 模拟取球的过程,知道总共剩下两个球为止 */
void simulate() {
    assert(bin1 + bin2 >= 2);  // 初始至少2个球
    while (bin1 + bin2 != 2) {
        bool firstBin = rand() % 2;
        if (firstBin) {
            if (bin1 > 0)
                bin1--;
        }
        else {
            if (bin2 > 0) {
                bin2--;
            }
        }
    }
}

/* 判断最终还剩2个球时,是否两个桶里剩下的球的个数是0和2 */
bool ifIs02() {
    return (bin1 == 2 && bin2 == 0) || (bin1 == 0 && bin2 == 2);
}

int main() {
    srand(time(0));
    int is02times = 0;  // 最终结果是02的次数
    for (int i = 0; i < simulationTimes; i++) {
        init();
        simulate();
        is02times += ifIs02();
    }
    double probability = 100. * is02times / simulationTimes;
    cout << "A total of " << simulationTimes << " simulations had been conducted." << endl;
    cout << "The number of times that 2 balls remain in one bucket and 0 balls remain in the other bucket is " << is02times << ", with a probability of " << probability <<"%" << endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

运行结果

A total of 10000000 simulations had been conducted.
The number of times that 2 balls remain in one bucket and 0 balls remain in the other bucket is 9514170, with a probability of 95.1417%
  • 1
  • 2

一千万次的模拟结果是 95.142 % 95.142\% 95.142%,和答案的 95 % 95\% 95%很接近。经过修改模拟次数、不同时间、不同平台运行次程序,结果大约都是 95.141 x % 95.141x\% 95.141x%,其中 x x x是一个不精确的数。

也许题目答案有精度误差?

显然模拟结果让我很惊讶,这和我预想的结果相差很远,而和给定答案相差很近。

仔细想想,似乎确实正好两个桶中各剩一个的概率比较小呢。

总之,还是这道题的答案与现实情况更为接近,但是答案中似乎也存在着精度误差。

能AC这道题但不能保证在现实生活中正确的代码

截止至2022年5月11,下面的代码交上去就能过。

如果把long double换成double则将结果错误,但long double也不能保证精度一定足够。

也许答案仍存在精度误差,但笔者实力有限,暂无法得知

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;

int main() {
    int n;
    cin >> n;
    long double result = 1;
    int p = n - 2;
    n = n / 2;
    for (int i = 0; i < n - 1; i++) {
        ll x = 2 * n - 2 - i;
        result = result * x / (i + 1);
    }
    for (int i = 0; i < p; i++) {
        result = result / 2;
    }
    result = 1 - result;
    printf("%.4Lf\n", result);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

每周提前更新菁英班周赛题解,点关注,不迷路

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/124725146

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/392610
推荐阅读
相关标签
  

闽ICP备14008679号