套路博弈背后的代码



对本文有任何问题,可加我的个人微信询问:kymjs666

前段时间在微博上看到一个有意思的视频,叫:【股市暴跌,为啥散户炒股票总赔钱】视频地址在这里:http://my.tv.sohu.com/us/209546370/101342155.shtml

文中讲了一个游戏。美女和男人每人拿一个硬币,一人把硬币出一面,你出一面我出一面,之后打开看一看,如果这两个硬币都是正面,那美女就给男人三块钱,男人的收益就是三块;如果都是反面 那就给一块,男人的收益就是一块钱;如果两个硬币一正一反,男人就给美女两块钱,所以男人的收益就是输两块。
于是美女问男人玩不玩,男人说他肯定玩对不对。为什么,首先因为你是个美女,跟美女玩会有什么,其次呢,是因为从概率上讲,两个正面的概率是1/4,两个反的概率也是1/4,玩四把,一次两正一次两反,还有两把是一正一反的,算下来不赚也不亏。这样一来的看起来是公平的。

但是玩了很长时间后,发现,男人总是在输钱,女人赚了很多钱。

解答

根据前面的描述,首先列出一个表格

女人↓男人→
3 -2
-2 1

假设男人出正面的概率是x,反面的概率就是1-x;
女人出正面的概率是y,反面的概率就是1-y;

那么男人赚钱的期望就是:

E = 3xy+1*(1-x)(1-y)-2*[x(1-y)+y(1-x)]
  = 8xy-3x-3y+1 

其中因为x和y都是概率值,所以取值范围在[0-1]。
如果女人想让男人一直输钱,那么也就是在这个式子中,E永远为负数。

所以解不等式0 < 8xy-3x-3y+1
最终结果y的取值范围在 (1/3,2/5)

也就是说,只要玩15把,女人出五到六次正,就可以一直赢钱。

如果你也听说

我知道,如果你也是第一次听说这个故事,一定想迫切的找个人试一试。
没错,我按照文中的说法试了这个游戏,很可惜,即便按照故事中女人的出法依然输的很惨。

不甘心的我拿代码模拟了一次这样的游戏,然后发现了其中的原因。

代码上的模拟

用GO语言实现这个游戏(语言无所谓,你也可以自己试试)

const MAX = 10000

func main() {
	profit := 0
	for i := 0; i < MAX; i++ {
		boyResult := boyPlan1()
		girlResult := girlPlan1()
		current := referee(boyResult, girlResult)
		profit += current
	}
	println(profit)
}

func boyPlan1() bool {
	num := getRandom() % 2
	if num == 0 {
		return true
	} else {
		return false
	}
}

func girlPlan1() bool {
	num := getRandom()
	const max = rondom_max * 2 / 5
	const min = rondom_max * 1 / 3
	if num < max && num > min {
		return true
	} else {
		return false
	}
}

func getRandom() int {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return r.Intn(rondom_max)
}

我把这段代码执行了一百遍,最后发现每次结果男人都会输4000元左右,也就是说,上文讲的思路与结果都是正确的,可是为什么我在和朋友玩的时候就不行了。

其实如果代码是你自己写的,在写的时候就能意识到了。在这段代码中,女人出硬币的策略是经过精细计算的,而男人的策略却只是简单的random,没有丝毫的策略可言。而在实际玩的时候,毕竟我的对手不是一个低智商的计算机random。可理论上来说尽管我的对手和random有区别,但最后我也不会输那么多。

其实再回想这个故事,男人要做的就是跟女人出的硬币一样就可以了,那么从概率上来讲就是,男人出正面的概率跟女人保持一致,就能达到收益最大化。
那最简单的就是,重复上一次女人的结果就能达到最大收益了。

用代码模拟一下

const MAX = 10000
var girlPreviousResult = getRandom()%2 == 0

func main() {
	profit := 0
	for i := 0; i < MAX; i++ {
		boyResult := boyPlan2()
		girlResult := girlPlan1()
		girlPreviousResult = girlResult
		current := referee(boyResult, girlResult)
		profit += current
	}
	println(profit)
}

func boyPlan2() bool {
	return girlPreviousResult
}

func girlPlan1() bool {
	num := getRandom()
	const max = rondom_max * 2 / 5
	const min = rondom_max * 1 / 3
	if num < max && num > min {
		return true
	} else {
		return false
	}
}

func getRandom() int {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return r.Intn(rondom_max)
}

结果

果然,男人最简单的策略,只要重复女人上一次的结果,就能赚到最高。
在上面的代码运行后,每次男人都能净赚6000以上。

所以在这个例子中,女人就像股市里的庄家一样,它可以拉升股价,也可以打压股价,对应着故事里面硬币的正和反。而男人就像散户一样,可以买多也可以买空。如果庄家拉升股价,而我们做多,就可以有很高的收益;如果庄家打压股价,我们做空也会有受益;但是如果庄家拉升股价,我们却做空,我们就输了。表面上看好像有涨有跌,我们是可以赚到的。但如果庄家通过一定策略,让你一直赚不到钱,你就要想办法了。