-
美女和硬币的博弈问题是一个经典的博弈论课题。
-
美女和倒霉蛋每人持有一枚硬币,各自决定自己的硬币是正面还是反面,然后一起进行比对。
-
如果均为正面,美女给倒霉蛋3元,如果均为反面,美女给倒霉蛋1元;如果一正一反,倒霉蛋给美女2元。
-
表面上看,每一种可能性都是1/4,所以每个人的数学期望都是0.也就是不输也不会赢。
-
但是,双方均想赢了对方,所以会对自己的出币行为应用一定的策略。下面我们就要对这种策略进行研究。
-
美女出正面的概率是m,倒霉蛋出正面的概率是d,那么,倒霉蛋(换成美女也行)的收益的数学期望是:
E = (mxdx3)+((1-m)x(1-d)x1)+((1-m)xdx-2) + (mx(1-3)x-2)
-
美女要想赢,就得让倒霉蛋输,也就是他的数学期望应该小于0才对。
E<0 => 8md-3m-3d+1 < 0
也就是:
m(8d-3)<3d-1
初中数学哈,把 8d-3 右移,不等式的话,就涉及到这个8d-3是正数,还是负数,还是0了。
毋庸置疑,如果d = 3/8,不等式肯定成立,不用考虑m的取值。反之也是一样,因为m和d是对等的,上面的不等式也可以是:
d(8m-3)<3m-1
也就是说,无论是美女选择固定的以3/8的概率出正面(不关心倒霉蛋怎么出),还是倒霉蛋选择以3/8的概率出正面(不关心美女怎么出),上面的不等式都是成立的,也就是倒霉蛋是赔钱的。那么,就需要考虑剩下的两种情况,8d-3大于0,以及8d-3小于0两种情况。
①. 8d-3 > 0, 则 d>3/8 , 则 m < (3d-1)/(8d-3) ,当d的取值范围是:(3/8,1]的时候,m 是一个随d变化的减函数,d 趋近于3/8时,右侧表达式趋近于无穷大,d等于1的时候,右侧等于2/5。也就是 m < 2/5。 也就是说,m的取值,最大可以是2/5(不含),可以让不等式成立。
②. 8d-3 < 0, 则 d<3/8, 则 m > (3d-1)/(8d-3) ,当d的取值是[0,3/8)时,m是一个随d变化的减函数,当d趋近于3/8时,右侧趋近于无穷小,当d等于0时,m > 1/3 ,也就是说,m最小也可以是1/3,可以保证不等式成立。综上所述,当 m 的取值范围是 (1/3,2/5)时,无论d的取值范围怎样变化,不等式都是始终成立的。也就是倒霉蛋恒亏钱。
-
好的,不知道解释明白了没有。
-
为了验证美女和硬币的问题,我写了一段代码,来帮助大家理解。先上代码,大家凑合看一下(jdk17.0)
/**
* 出手策略,开始投注,需要依据一定的策略,进行投注,是否正面和背面。
**/
public abstract class BetPolicy {
// 默认的概率模型是50%,正反面。
private double _low = 0.0;
private double _high = 0.5;
private boolean _lowInclude = false;
private boolean _highInclude = false;
// 初始金额 0
protected long money = 0l;
protected long wonCnt = 0;
protected long lostCnt = 0;
// 当前的硬币是正面吗?
private boolean myFace = false;
// 胜利条件的对应关系
protected Map<String, Integer> _mpWinMap = new HashMap<>();
// 赢得比赛的条件。
public abstract void initPolicy();
BetPolicy() {
initPolicy();
}
// 设置概率上下限
public void setProbBound(double low, double high, boolean lowInclude, boolean highInclude) {
_low = low;
_high = high;
_lowInclude = lowInclude;
_highInclude = highInclude;
}
// 概率边界
public String getBounds() {
String start = _lowInclude ? "[" : "(";
String end = _highInclude ? "]" : ")";
return start + _low + " , " + _high + end;
}
// 是否正面
public boolean coinFace() {
double one = Math.random();
myFace = false;
// 符合指定概率模型的时候,返回正面。默认情况,按照50%的概率选择正反面
if ((_lowInclude ? one >= _low : one > _low) && (_highInclude ? one <= _high : one < _high)) {
myFace = true;
}
return myFace;
}
// 赢了,钱增加,赢的次数增加。输了,钱减少,输的次数增加。
public void bet(boolean target) {
var ret = _mpWinMap.get("" + myFace + "_" + target);
if (ret > 0) {
wonCnt++;
} else {
lostCnt++;
}
money += ret;
}
/// 说出结果
public void say() {
System.out.println("我赢了" + wonCnt + "次,现在共有:" + money + "元钱,赢的概率是:" + (wonCnt * 1.0 / (wonCnt + lostCnt)) + " 平均每次赢 " + (money * 1.0 / (wonCnt + lostCnt)) + "元");
}
}
/**
* 美女的打赌策略
*/
public class Beauty extends BetPolicy {
@Override
public void initPolicy() {
// 美女的正面策略
setProbBound(0, 3 / 8.0,true,true);
_mpWinMap.clear();
_mpWinMap.put(true + "_" + true, -3);
_mpWinMap.put(true + "_" + false, 2);
_mpWinMap.put(false + "_" + true, 2);
_mpWinMap.put(false + "_" + false, -1);
}
}
/**
* 倒霉蛋的打赌策略
* */
public class Victim extends BetPolicy {
@Override
public void initPolicy() {
_mpWinMap.clear();
_mpWinMap.put(true + "_" + true, 3);
_mpWinMap.put(true + "_" + false, -2);
_mpWinMap.put(false + "_" + true, -2);
_mpWinMap.put(false + "_" + false, 1);
}
}
// 美女和硬币,运行100次,以0.01的步进,检索一下倒霉蛋有没有破局的可能性。当然,各位可以发挥一下,看看什么情况下倒霉蛋赔钱最多,什么情况下美女赢钱最少。
public class BeautyAndCoin {
// 总共运行的次数
static final long totalCnt = 10000000;
// 程序入口
public static void main(String[] args) {
for (double bm = 0; bm < 1; bm += 0.01) {
oneBet(0, bm);
}
System.out.println("运行结束。");
}
// 来一局
public static void oneBet(double low, double high) {
var beauty = new Beauty();
var victim = new Victim();
victim.setProbBound(low, high, true, true);
for (long i = 0; i < totalCnt; i++) {
var victimRet = victim.coinFace();
var beautyRet = beauty.coinFace();
beauty.bet(victimRet);
victim.bet(beautyRet);
}
System.out.println("美女的策略是:" + beauty.getBounds());
System.out.println("倒霉蛋的策略是:" + victim.getBounds());
System.out.println("美女说:");
beauty.say();
}
}
评论区