一道概率论的面试题 求大牛解答

zilaishuichina 2013-08-06 05:09:08
找不到算法区,就发在这了


一个长N的数组(比如长度是50)
每个数在0-100闭区间以均等的概率随机

求这个数组(50个数)之和大于某个值(比如3000)的概率是多少?
应该怎么算?
...全文
1119 40 打赏 收藏 转发到动态 举报
写回复
用AI写文章
40 条回复
切换为时间正序
请发表友善的回复…
发表回复
FancyMouse 2013-08-13
  • 打赏
  • 举报
回复
还有比动态规划更快的办法。用生成函数。一次试验的生成函数就是f(x)=(1+x+...+x^100)/101。50次试验就是f(x)^50,这个函数的x^k系数就是总和为k的概率。 而这个多项式乘法可以用FFT做。假设一次试验的取值范围有n,总共m次试验。动态规划的方法等价于f(x)自乘乘m-1次,复杂度O((nm)^2),而FFT的话只要O(nm*log(nm))。
gdsafewfew 2013-08-13
  • 打赏
  • 举报
回复
从0-100 取 50个数而这50个数又可以重复出现 这个组合是 101^50 (第一个数有101个可能,第二个也有101个可能,。。。第50个也有101个可能)是非常大的 然后就用for loop 由50个0。。。0开始 loop 到 50 个100 如果大于3000 的就 count++ 就可以了 概率就是 count/(101^50) a[N]={0,0,....,0} float fun(int N, int sum) //N=50,sum=3000 { int aSum=0; int count=0; int position = 0; while(a[0]<=100 && a[N-1]<=100 && position<N) { aSum=0; for(int i=0;i<N;i++) { aSum+=a[i]; } if(aSum>sum) count++; if(a[position]<100) a[positon]++; else position++; } return count/(101.0^N); }
lpcads 2013-08-12
  • 打赏
  • 举报
回复
引用 37 楼 No__stop 的回复:
这题不是应该考虑dp么?应该是个非常简单的背包问题吧?dp[i][j]表示已经用了i个数,组和出的和为j的概率,j的范围0-5000,i从1到50。递推算完之后,将3001到5000的值累加起来不就可以了?
j的范围2000-2500就行了
No__stop 2013-08-12
  • 打赏
  • 举报
回复
这题不是应该考虑dp么?应该是个非常简单的背包问题吧?dp[i][j]表示已经用了i个数,组和出的和为j的概率,j的范围0-5000,i从1到50。递推算完之后,将3001到5000的值累加起来不就可以了?
ri_aje 2013-08-10
  • 打赏
  • 举报
回复
引用 30 楼 zilaishuichina 的回复:
[quote=引用 29 楼 ri_aje 的回复:]
我觉得的这题少给了一个重要条件,这些 [0,100] 的数字,是实数还是整数?


。。。整数。。。必须是整数[/quote]
那就好办多了。下面先上数学,然后再上代码,顺便吐槽一下 csdn 的编辑器不支持 latex,写点儿公式真费劲,只好抓图了。
最重要的就是这个 (1) 号公式,显示了递归结构。

然后是一些解释,为什么概率可以这么算。主要就是 a[i] 的不同取值是不相关事件,所以总概率等于各子事件概率的和。

再然后是,递归终止条件,当序列只剩一个数字的时候,概率就很好算了,就是最简单的均匀分布。

最后是两个边界条件,对于一些明显有问题的值,可以直接快速计算概率的。

下面是个简单的程序,实现了上面的公式。这个递归可以用动态规划做,这样当子问题重复的时候,直接查找上次计算结果即可。

#include <cassert>
#include <cmath>
#include <map>
#include <iostream>
#include <vector>

struct problem_t
{
double operator () (size_t const i, int const n) const
{
return f(i,n);
}

double f (size_t const i, int const n) const
{
assert(i < N);

if (n < 0) { return 1; }
if (n > int(K*(N-i))) { return 0; }

if (i+1 == N) // single integr probability.
{
return (K-n)/(K+1.);
}
else
{
double p = 0;
for (size_t k = 0; k <= K; ++k)
{
p += pf(i+1,n-k);
}
return p/(K+1.);
}
}

double pf (size_t const i, int const n) const
{
static std::vector<std::map<int,double>> probabilities(N);
auto& probs = probabilities[i];

auto it = probs.find(n);
if (probs.end() == it)
{
auto const ans = probs.insert({n,f(i,n)}); assert(ans.second);
it = ans.first;
}
assert(it->second >= 0);
return it->second;
}

size_t const N; // # of elements in sequence.
size_t const K; // upper limit, element value in [0,K].
};

int main ()
{
size_t const n_elements = 50;
size_t const upper_limit = 100;
size_t const starting_index = 0;
size_t const target_threshold = 3000;
std::cout << problem_t{n_elements,upper_limit}(starting_index,target_threshold) << std::endl;
}
lpcads 2013-08-10
  • 打赏
  • 举报
回复
引用 34 楼 HH_YT 的回复:
最后求得前3000的概率之和x,用1 - x即为大于3000的概率
求得前2000的概率之和x,用x即为大于3000的概率
水平不流 2013-08-10
  • 打赏
  • 举报
回复
算法导论里有过这个导论.好像是
HH_YT 2013-08-10
  • 打赏
  • 举报
回复
最后求得前3000的概率之和x,用1 - x即为大于3000的概率
HH_YT 2013-08-10
  • 打赏
  • 举报
回复
简单的动态规划,dp【51】【3000】,dp【i】【j】表示前i个数和为j的概率 那么dp【i+1】【j+k】(0<=k<=100) += dp【i】【j】 * 1 / 101; 这就是状态方程
ri_aje 2013-08-09
  • 打赏
  • 举报
回复
我觉得的这题少给了一个重要条件,这些 [0,100] 的数字,是实数还是整数?
zilaishuichina 2013-08-09
  • 打赏
  • 举报
回复
引用 27 楼 ananluowei 的回复:
[quote=引用 26 楼 lpcads 的回复:] [quote=引用 22 楼 zilaishuichina 的回复:] [quote=引用 21 楼 lpcads 的回复:] 如果用蒙特卡洛法,基于对称性可将子空间分量之和划为0-2000,2000-2500,2500-3000,3000-5000 四个范围,这样可以提高效率和准确度。
好高端的样子 求解释[/quote] 其实一点都不高端。这里说的是0到100取数连续的情况,50个数值和(记为Z)最大为5000,最小为0,显然Z=0和Z=5000处的概率密度是一样的,不仅如此,事实上Z的概率密度函数f关于Z=2500对称(更精确的,f趋近高斯函数,前面的中心极限定理)。 如果模拟次数少,蒙特卡洛法的结果可能不太准确。可以在每次模拟时对四个区间计数(而不是仅对3000-5000)。设模拟结束后,计数值分别为n1,n2,n3,n4,记 m=n1+n2+n3+n4, 则所求概率为 n1/m , n4/m ,0.5-n2/m 或 0.5-n3/m ,可求平均值,或是求方差看看模拟误差有多大。[/quote] 完全不懂 话说,这面试题是考编程还是考数学啊。[/quote] 显然靠的数学 我觉得 数学老师死的早啊
zilaishuichina 2013-08-09
  • 打赏
  • 举报
回复
引用 29 楼 ri_aje 的回复:
我觉得的这题少给了一个重要条件,这些 [0,100] 的数字,是实数还是整数?
。。。整数。。。必须是整数
赵4老师 2013-08-08
  • 打赏
  • 举报
回复
说到均等的随机,参考洗牌算法:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int d[6];
int i,n,a,b,t;
int c,j;
void main() {
    srand(time(NULL));
    printf("shuffle 0..n-1 demo\n");
    for (n=1;n<=5;n++) {/* 测试1~5个元素 */
        printf("_____n=%d_____\n",n);
        j=1;
        for (c=1;c<=n;c++) j=j*c;/* j为n! */
        j*=n*2;
        for (c=1;c<=j;c++) {/* 测试n*2*n!次 */
            for (i=0;i<n;i++) d[i]=i;/* 填写0~n-1 */
            for (i=n;i>0;i--) {/* 打乱0~n-1 */
                a=i-1;b=rand()%i;
                if (a!=b) {t=d[a];d[a]=d[b];d[b]=t;}
            }
            printf("%04d:",c);
            for (i=0;i<n;i++) printf("%d",d[i]);
            printf("\n");
        }
    }
    printf("shuffle 1..n demo\n");
    for (n=1;n<=5;n++) {/* 测试1~5个元素 */
        printf("_____n=%d_____\n",n);
        j=1;
        for (c=1;c<=n;c++) j=j*c;/* j为n! */
        j*=n*2;
        for (c=1;c<=j;c++) {/* 测试n*2*n!次 */
            for (i=1;i<=n;i++) d[i]=i;/* 填写1~n */
            for (i=n;i>1;i--) {/* 打乱1~n */
                a=i;b=rand()%i+1;
                if (a!=b) {t=d[a];d[a]=d[b];d[b]=t;}
            }
            printf("%04d:",c);
            for (i=1;i<=n;i++) printf("%d",d[i]);
            printf("\n");
        }
    }
}
zilaishuichina 2013-08-08
  • 打赏
  • 举报
回复
引用 17 楼 zhaowech 的回复:
这道题不是要求程序开发者给出精确的值,而是让你用一个程序去模拟,如何验证理论上给出的结果,所以要实现的东西包括,如何概率均等地产生一个0到100的随机数,然后根据给定的数组长度n去产生n个随机数,求和后判断是否大于3000,如果满足则j++,j初始化0,重复m次试验,将模拟得到的概率j/m,输出。理论上的概率是当n<30的时候,概率为0;当n>=30时,这个嘛,不会了!!!
。。。
lpcads 2013-08-08
  • 打赏
  • 举报
回复
引用 27 楼 ananluowei 的回复:
[quote=引用 26 楼 lpcads 的回复:] [quote=引用 22 楼 zilaishuichina 的回复:] [quote=引用 21 楼 lpcads 的回复:] 如果用蒙特卡洛法,基于对称性可将子空间分量之和划为0-2000,2000-2500,2500-3000,3000-5000 四个范围,这样可以提高效率和准确度。
好高端的样子 求解释[/quote] 其实一点都不高端。这里说的是0到100取数连续的情况,50个数值和(记为Z)最大为5000,最小为0,显然Z=0和Z=5000处的概率密度是一样的,不仅如此,事实上Z的概率密度函数f关于Z=2500对称(更精确的,f趋近高斯函数,前面的中心极限定理)。 如果模拟次数少,蒙特卡洛法的结果可能不太准确。可以在每次模拟时对四个区间计数(而不是仅对3000-5000)。设模拟结束后,计数值分别为n1,n2,n3,n4,记 m=n1+n2+n3+n4, 则所求概率为 n1/m , n4/m ,0.5-n2/m 或 0.5-n3/m ,可求平均值,或是求方差看看模拟误差有多大。[/quote] 完全不懂 话说,这面试题是考编程还是考数学啊。[/quote] 不是和你6楼的做法一样嘛,只不过如果只记大于3000的次数,样本数量可能太少。反正都是一趟,干脆把小于3000的情况也分分清楚,利用对称性使蒙特卡洛结果更准确一点
大尾巴猫 2013-08-08
  • 打赏
  • 举报
回复
引用 26 楼 lpcads 的回复:
[quote=引用 22 楼 zilaishuichina 的回复:] [quote=引用 21 楼 lpcads 的回复:] 如果用蒙特卡洛法,基于对称性可将子空间分量之和划为0-2000,2000-2500,2500-3000,3000-5000 四个范围,这样可以提高效率和准确度。
好高端的样子 求解释[/quote] 其实一点都不高端。这里说的是0到100取数连续的情况,50个数值和(记为Z)最大为5000,最小为0,显然Z=0和Z=5000处的概率密度是一样的,不仅如此,事实上Z的概率密度函数f关于Z=2500对称(更精确的,f趋近高斯函数,前面的中心极限定理)。 如果模拟次数少,蒙特卡洛法的结果可能不太准确。可以在每次模拟时对四个区间计数(而不是仅对3000-5000)。设模拟结束后,计数值分别为n1,n2,n3,n4,记 m=n1+n2+n3+n4, 则所求概率为 n1/m , n4/m ,0.5-n2/m 或 0.5-n3/m ,可求平均值,或是求方差看看模拟误差有多大。[/quote] 完全不懂 话说,这面试题是考编程还是考数学啊。
lpcads 2013-08-08
  • 打赏
  • 举报
回复
引用 22 楼 zilaishuichina 的回复:
[quote=引用 21 楼 lpcads 的回复:] 如果用蒙特卡洛法,基于对称性可将子空间分量之和划为0-2000,2000-2500,2500-3000,3000-5000 四个范围,这样可以提高效率和准确度。
好高端的样子 求解释[/quote] 其实一点都不高端。这里说的是0到100取数连续的情况,50个数值和(记为Z)最大为5000,最小为0,显然Z=0和Z=5000处的概率密度是一样的,不仅如此,事实上Z的概率密度函数f关于Z=2500对称(更精确的,f趋近高斯函数,前面的中心极限定理)。 如果模拟次数少,蒙特卡洛法的结果可能不太准确。可以在每次模拟时对四个区间计数(而不是仅对3000-5000)。设模拟结束后,计数值分别为n1,n2,n3,n4,记 m=n1+n2+n3+n4, 则所求概率为 n1/m , n4/m ,0.5-n2/m 或 0.5-n3/m ,可求平均值,或是求方差看看模拟误差有多大。
zhctj159 2013-08-08
  • 打赏
  • 举报
回复
引用 20 楼 lpcads 的回复:
[quote=引用 16 楼 zhctj159 的回复:] [quote=引用 15 楼 gpshq 的回复:] 这么高端 [quote=引用 13 楼 zhctj159 的回复:] [quote=引用 9 楼 zilaishuichina 的回复:] [quote=引用 7 楼 zhctj159 的回复:] 该使用 独立同分布的中心极限定理
另外这个是求出近似值 还是精确值?[/quote]应该是近似值,如截图所示,我们假设N=50已经足够大,实际上也确实够了、、欲求西格玛Xk大于3000的概率,可以先算西格玛Xk小于3000的概率,即"(西格玛Xk-nu)/o根号n" 小于等于 "(3000-nu)/o根号n" 的概率.后者可以算出,然后查表可以得到这个概率,最后用1减去这个概率就可以了[/quote][/quote]大概还有捷径、、、需要大家发挥聪明才智了[/quote] 如果觉得 N=50 不够大,就只能用类似线性规划的方法了,在50维的空间中求出分量和大于3000的子空间的体积。蛋疼的是,这时又觉得 N=50 太大了。 碰到这种题,我觉得还是用蒙特卡洛法吧,这样连续和离散的情况都能处理。[/quote]50够大了,,以前算的时候独立同分布大多是几十个就可以近似成正态了
zilaishuichina 2013-08-08
  • 打赏
  • 举报
回复
引用 23 楼 congya001 的回复:
数组有50个数,每个数有100中取值,且等概率,而且每个数的取值不会影响其他数取值,总共有5000中取法。 这50个数的和的范围为50-5000,就是说有一部分的取值他们的值是相同的,3000将(50,5000)分成两个区间2950和2000,由于每个数取值的概率都相同,所以这两个区间和相同的取法的数目也相同,因此大于3000的概率为2000/5000=0.4
总共有 101的50次方种取法。。。。
文修 2013-08-08
  • 打赏
  • 举报
回复
数组有50个数,每个数有100中取值,且等概率,而且每个数的取值不会影响其他数取值,总共有5000中取法。 这50个数的和的范围为50-5000,就是说有一部分的取值他们的值是相同的,3000将(50,5000)分成两个区间2950和2000,由于每个数取值的概率都相同,所以这两个区间和相同的取法的数目也相同,因此大于3000的概率为2000/5000=0.4
加载更多回复(20)

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧