与概率相关的算法题C++解法(附证明过程)
来源:互联网 发布:安知玉如意txt下载久久 编辑:程序博客网 时间:2024/05/17 09:03
一、常考题型
1、客观题(选择题);
2、古典概率、期望的计算,不涉及高等概率和微积分;
3、利用随机来改进著名的算法(快速排序);
4、随机数发生器(根据给定的随机数发生器构造另一个)。
二、练习题
1、有2k只球队,有k-1个强队,其余都是弱队,随机把它们分成k组比赛,每组两个队,问两强相遇的概率是多大?结果化成最简分数。
解法:该题的难点有两点:
①总组队方法数的计算。用C(2k,2)C(2(k-1),2)……C(2,2)计算是错误的,因为这样把选取其中任意两队为一组重复计数了k次,例如4支球队A,B,C,D,选AB,则CD自然为一组,若用C(4,2)计算,它把先选AB和先选CD算成了两种情况,实际上是同一种情况。所以应该是C(2k,2)C(2(k-1),2)……C(2,2)/k*(k-1)……2*1。
②最大公约数的计算。
class
Championship {
public
:
int
getGcd(
int
a,
int
b) {
// 辗转相除法计算最大公约数
if
(a ==
0
)
return
b;
if
(b ==
0
)
return
a;
int
temp;
while
(b !=
0
) {
temp = a % b;
a = b;
b = temp;
}
return
a;
}
vector<
int
> calc(
int
k) {
int
n1 =
1
,n2 =
1
;
vector<
int
> res;
for
(
int
i =
2
*k-
1
;i >=
3
;i = i-
2
)
n1 *= i;
n2 = (k+
1
)*k/
2
;
for
(
int
j = k-
1
;j >=
1
;j--)
n2 *= j;
n2 = n1-n2;
int
gcd = getGcd(n1,n2);
res.push_back(n2/gcd);
res.push_back(n1/gcd);
return
res;
}
};
注:最大公约数的另一种计算方法(移位)
int getGcd(int a,int b) { if (a == 0)
return b;
if (b == 0)
return a;
if (!(a & 1) && !(b & 1))
return getGcd(a>>1, b>>1) << 1;
else if (!(b & 1))
return getGcd(a, b>>1);
else if (!(a & 1))
return getGcd(a>>1, b);
else
return getGcd(abs(a - b), min(a, b));
}
解法:所有情况有2的n次方种,不碰撞只有两种,即所有蚂蚁均顺时针或均逆时针。碰撞概率为(pow(2,n)-2)/pow(2,n)=(pow(2,n-1)-1)/pow(2,-1n)。
解法一:由于是2的幂次,可以直接移位来解决。又由于分子是2的幂次减1,分母是2的幂次,必然不可再约分。所以无需快速幂算法和移位求gcd算法。
class
Ants {
public
:
vector<
int
> collision(
int
n) {
int
C,D;
C =
1
; D =
1
<< (n -
1
);
vector<
int
> res;
res.push_back(D - C); res.push_back(D);
return
res;
}
};
解法二:按照常规的步骤,用快速幂算法求幂次,用移位算法求gcd。
class
Ants {
public
:
int
quickPower(
int
a,
int
n) {// 快速幂算法
long
res =
1
;
long
base = a;
while
(n) {
if
(n&
1
==
1
)
res *= base;
base *= base;
n >>=
1
;
}
return
(
int
)res;
}
int
getGcd(
int
a,
int
b) {// 求gcd的移位算法
if
(a ==
0
)
return
b;
if
(b ==
0
)
return
a;
if
(!(a&
1
) && !(b&
1
))
return
getGcd(a>>
1
,b>>
1
) <<
1
;
else
if
(!(a&
1
))
return
getGcd(a>>
1
,b);
else
if
(!(b&
1
))
return
getGcd(a,b>>
1
);
else
return
getGcd(abs(a-b),min(a,b));
}
vector<
int
> collision(
int
n) {
vector<
int
> res(
2
,
0
);
int
n1 = quickPower(
2
,n);
int
n2 = n1-
2
;
int
gcd = getGcd(n1,n2);
res[
0
] = n2/gcd;
res[
1
] = n1/gcd;
return
res;
}
};
解法:此题没做过的确很难。推导过程如下。
class
Random7 {
public
:
int
rand5() {
return
Random5::randomNumber();
}
int
randomNumber() {
int
temp;
do
{
temp =
5
*(rand5()-
1
)+(rand5()-
1
);
}
while
(temp >
20
);
return
(temp%
7
)+
1
;
}
};
4、给定一个以p概率产生0,以1-p概率产生1的随机函数RandomP::f(),不使用任何额外的随机机制,实现等概率随机产生0和1的随机函数。
解法:巧用独立重复试验。连续调用两次f()函数,产生01序列和10序列的概率都是p(1-p)。产生01返回0,产生10返回1,产生00和11继续产生,即可。
class Random01 {public:
int random01() {
int n1,n2;
do {
n1 = RandomP::f();
n2 = RandomP::f();
}while (n1 == n2);
return n1;
}
};
解法:巧用独立重复试验。连续调用f()函数k次,记录下k次的最大值,将这个最大值返回即可。因为这个最大值落入[0,x)区间的情况只有一种,那就是k次返回值都落入[0,x)区间,落入[0,x)记为1,否则记为0,这就将问题转化为k次独立重复试验,求k次结果均为1的概率,为x的k次方。
class
RandomSeg {
public
:
double
f() {
return
rand() *
1.0
/ RAND_MAX;
}
double
random(
int
k,
double
x) {
double
res=-
1
;
for
(
int
i=
0
;i<k;i++){
res=max(res,f());
}
return
res;
}
};
解法:此题与抽签原理类似。相当于M个人从N根签中每人抽一根,纵然抽签顺序有先后,但是每个人抽到某根签的概率是一样的,都是1/N。因此只需要一个循环,每次随机读取数组中的一个元素,然后把这个元素剔除,下一次循环再随机地从剩下的元素中读取一个,直到读取完M个为止。
class RandomPrint {public:
vector<int> print(vector<int> arr, int N, int M) {
vector<int> res;
int t;
for (int i = 0;i < M;i++) {
t = rand()%(N-i); // 这里的随机数产生方法采用的是不指定种子seed的方法
res.push_back(arr[t]);
swap(arr[t],arr[N-i-1]);
}
return res;
}
};
有一个只能装下10个球的袋子,当吐出100个球时,袋子里有10 球,并且1~100号中的每一个球被选中的概率都是10/100。然后继续吐球,当吐出1000个球时,袋子里有 10 个球,并且1~1000号中的每一个球被选中的概率都是10/1000。继续吐球,当吐出i个球时,袋子里有10个球,并且1~i号中的每一个球被选中的概率都是10/i。也就是随着N的变化,1~N号球被选中的概率动态变化成k/N。设计一个选择函数,输入球的编号N和袋子容量k,返回吐出第N号球后,袋子中k个球的编号。
解法:①显然,吐出前k个球时,所有球都要装入袋子;②当吐出第k+1个球时,由于这k+1个球中每个球被选中的概率都是k/k+1,所以要以k/k+1的概率决定第k+1个球是否被选中;③如果第k+1个球被选中,那么以1/k的概率丢掉袋子中的一个球,再用第k+1个球替换它。
class Bag {public:
vector<int> ret;
// 每次拿一个球都会调用这个函数,N表示第i次调用
vector<int> carryBalls(int N, int k) {
if (ret.size() < k)
ret.push_back(N);
else {
if (rand()%N < k)
ret[rand()%k] = N;
}
return ret;
}
};
推荐博文:
二叉树相关练习题(C++)
经典排序算法的C++实现
与字符串有关的一些典型问题的C++解法
一些可以用动态规划(DP)算法解决的问题(C++)
排列组合相关笔试面试题(C++)
二分查找的巧妙运用(C++)
位运算在算法题中的使用(C++)
链表相关练习题(C++)
用实例讲解栈和队列(C++)
一些智力题的C++解法
1 0
- 与概率相关的算法题C++解法(附证明过程)
- 概率相关的证明
- 求解概率相关的题目的解法
- KMP算法的相关证明
- 欧几里德 与 扩展欧几里德算法 的证明过程
- LeetCode中3Sum(a+b+c=0)的解法与证明
- 【滤波算法】【C语言】(二)标量Kalman滤波的过程分析和证明及C实现
- RSA算法证明-证明过程
- 平方探测法的证明《数据结构与算法分析(C语言描述)第二版》P119
- 扩展欧几里德算法(附证明)
- 与概率相关的题目
- 一道经典概率题的终极解法——后验事实与先验概率的关系
- .NET与C#(附VS2015安装过程)
- 最大工作量问题新的解法(不会证明)
- b->c->a 相关的概率
- 递归算法的证明与设计
- 面试相关的概率题
- 欧几里得&扩展欧几里得算法及相关的数学证明
- Java 中的简单分割
- 在C++中调用DLL中的函数
- Hadoop配置文件主要作用详解,即hadoop+hbase+zookeep配置方法(5台)
- iOS调试技巧之LLDB
- Android之abstract和interface介绍
- 与概率相关的算法题C++解法(附证明过程)
- db2 查询重复数据只显示一条
- Spring切点获取实体类数据
- Android 上千实例源码分析以及开源分析
- android 自定义控件pickview
- c++ typedefy用法
- Obeject Tracking Reference
- WEB状态管理机制剖析(cookie、session)
- Intent 使用总结