为何两段R代码计算21点天然牌概率结果存在差异?
我用R语言模拟牌组研究概率时发现:当牌组用数字字符串(如"10"、"8")构建时,计算21点天然牌的概率,和用纯字符字符串(如"Ten"、"Eight")构建的结果不一样。
加载library(gtools)后,第一段代码如下:
suits <- c("D", "C", "H", "S") numbers <- c("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A") deck <- expand.grid(number = numbers, suit = suits) deck <- paste(deck$number, deck$suit) aces <- paste("A", suits) facecard <- c("K", "Q", "J", "10") facecard <- expand.grid(number=facecard, suit=suits) facecard <- paste(facecard$number, facecard$suit) hands <- combinations(52, 2, v = deck) mean(hands[,1] %in% aces & hands[,2] %in% facecard)
运行结果:
> mean(hands[,1] %in% aces & hands[,2] %in% facecard) [1] 0.0361991
换成纯字符字符串定义numbers后,代码如下:
suits <- c("D", "C", "H", "S") numbers <- c("Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "J", "Q", "K", "A") deck <- expand.grid(number = numbers, suit = suits) deck <- paste(deck$number, deck$suit) aces <- paste("A", suits) facecard <- c("K", "Q", "J", "Ten") facecard <- expand.grid(number=facecard, suit=suits) facecard <- paste(facecard$number, facecard$suit) hands <- combinations(52, 2, v = deck) mean(hands[,1] %in% aces & hands[,2] %in% facecard)
运行结果:
> mean(hands[,1] %in% aces & hands[,2] %in% facecard) [1] 0.04826546
问题
为什么只是把numbers向量换成纯字符字符串,概率计算结果就出现差异?是因为"A"和其他数字的组合导致花牌计数减少吗?
解答
差异完全源于combinations()函数的排序特性:
gtools包中的combinations()函数生成组合前,会先对输入的向量v按字符串顺序排序,再生成有序的组合对——每个无序牌组只会出现一次,且组合内元素按排序后的顺序排列(靠前的元素在第一列,靠后的在第二列)。
两种场景的具体差异:
数字字符串场景:
字符串排序时,"10"的首字符"1"ASCII码小于"A"的"A",所以"10 D"这类牌会排在"A D"前面;而"J"、"Q"、"K"的ASCII码大于"A",会排在"A"后面。
因此:- A和"10"类花牌的组合会以
花牌在前、A在后的形式出现(比如("10 D", "A D")),不会被你的hands[,1] %in% aces条件命中; - A和J/Q/K类花牌的组合会以
A在前、花牌在后的形式出现,会被统计到。
最终有效统计数是4张A × 12张J/Q/K花牌 = 48,除以总组合数C(52,2)=1326,得到≈0.0361991。
- A和"10"类花牌的组合会以
纯字符字符串场景:
"Ten"的首字符"T"ASCII码大于"A",所以所有花牌(Ten、J、Q、K)都排在A牌后面。
所有A和花牌的组合都会以A在前、花牌在后的形式出现,全部被你的条件统计到,有效统计数是4×16=64,除以1326得到≈0.04826546。
补充:正确的天然牌概率计算
21点天然牌包含A+花牌和花牌+A两种无序组合,但combinations()只会生成一次每个无序对。如果要统计完整的天然牌概率,应该同时覆盖两种排列形式的组合:
mean((hands[,1] %in% aces & hands[,2] %in% facecard) | (hands[,1] %in% facecard & hands[,2] %in% aces))
此时两种场景下的结果会一致,均为天然牌的真实概率≈0.04826546。
内容的提问来源于stack exchange,提问作者nick

