海爷和我share了一个很有意思的算法题:
FROM:http://ilewen.com/questions/1534
一个骰子,6面,1个面是 1, 2个面是2, 3个面是3, 问平均掷多少次能使1,2,3都至少出现一次!
首先要知道这个题求什么,最先直接没理解
这题可以翻译为,一个骰子,6面,1个面是 1, 2个面是2, 3个面是3,随机扔骰子,在第x次时3个数都出现,求这个x的期望(也就是扔无数次,x的平均值是多少)
思路:
第一二次肯定不可能出现这种情况
第x(x > 2)次三个都出现的情况分三种(x ^ y 表示 x 的 y 次方)
1:第x次出现 1,那么前面出现的必然是 2 和 3 ,且至少出现一次
出现1的概率为 1 / 6,前面x-1次不出现1的概率为(1 - 1 / 6) ^ (x - 1),
但是其中包含全是 2 和全是 3 的情况,去掉全 2 的概率 (1 / 2) ^ (x - 1),全部为3的概率(1 / 3) ^ (x - 1),
那么情况 1 的概率为 ((1 - 1 / 6) ^ (x - 1) - (1 / 2) ^ (x - 1) - (1 / 3) ^ (x - 1)) * (1 / 6)
2:第x次出现2,那么前面出现的必然是 1 和 3 ,且至少出现一次 同样,概率为 ((1 - 1 / 3) ^ (x - 1) - (1 / 2) ^ (x - 1) - (1 / 6) ^ (x - 1)) * (1 / 3)
3:第x次出现3,那么前面出现的必然是 1 和 2 ,且至少出现一次 同样,概率为 ((1 - 1 / 2) ^ (x - 1) - (1 / 3) ^ (x - 1) - (1 / 6) ^ (x - 1)) * (1 / 2)
p(x)就为上面三种情况的和
那么,根据期望公式,平均值就等于从x = 3 到 n(无穷)求(x * p(x))的和
利用错位相减法计算极限值,附代码
#include "stdio.h"
#include "math.h"
const int MAX_TRY = 10000;
double funBase(double x)
{
return x * x * (3 + x / (1 - x));
}
double funOne(double x)//每种情况的极限
{
x = 1 - x;
return funBase(x) - funBase(1 - x);
}
double funThree(double x1, double x2, double x3)
{
return funOne(x1) + funOne(x2) + funOne(x3);//三种情况的极限和
}
double funTimeBase(double x, int n)
{
return x * pow((1 - x), n) - (1 - x) * pow(x, n);
}
double funTimeOne(double x1, double x2, double x3, int n)
{
return funTimeBase(x1, n) + funTimeBase(x2, n) + funTimeBase(x3, n);
}
double funTimeAll(double x1, double x2, double x3, int start, int n)
{//简单的变化
double total = 0;
for (int i = start; i < n; ++i)
{
double look = funTimeOne(x1, x2, x3, i);
total += (i + 1)* look;
}
return total;
}
double funDirectOne(double x1, double x2, double x3, int n)
{
double ret = 0;
ret += x1 * (pow((1 - x1), n) - pow(x2, n) - pow(x3, n));
ret += x2 * (pow((1 - x2), n) - pow(x1, n) - pow(x3, n));
ret += x3 * (pow((1 - x3), n) - pow(x2, n) - pow(x1, n));
return ret;
}
double funDirectAll(double x1, double x2, double x3, int start, int n)
{//按各种情况直接求和
double total = 0;
for (int i = start; i < n; ++i)
{
double look = funDirectOne(x1, x2, x3, i);
total += (i + 1) * look;
}
return total;
}
int main()
{
//后面的start = 2表示第三次的概率
double ret = funTimeAll(1.0 / 6, 1.0 / 2, 1.0 / 3, 2, MAX_TRY);
printf("%f ", ret);
ret = funThree(1.0 / 6, 1.0 / 2, 1.0 / 3);
printf("%f ", ret);
ret = funDirectAll(1.0 / 6, 1.0 / 2, 1.0 / 3, 2, MAX_TRY);
printf("%f\n",ret);
return 0;
}
先把我的运行结果发出来:
错位相减法是一种常用的数列求和方法,应用于等比数列与等差数列相乘的形式。 形如An=BnCn,其中Bn为等差数列,Cn为等比数列;分别列出Sn,再把所有式子同时乘以等比数列的公比,即kSn;然后错一位,两式相减即可。
如果数列的各项是由一个等差数列和一个等比数列的对应项之积构成的,那么这个数列的前n项和可用此法来求,如等比数列的前n项和公式就是用此法推导的