这是我在Glassdoor上发现的一个问题:如何使用具有的硬币以相等的概率生成7个整数?
基本上,您有一个硬币,可能是公平的,也可能是不公平的,这是唯一的随机数生成过程,因此想出一个随机数生成器,它输出从1到7的整数,其中获得每个整数的概率是1/7。
数据生成过程的效率至关重要。
这是我在Glassdoor上发现的一个问题:如何使用具有的硬币以相等的概率生成7个整数?
基本上,您有一个硬币,可能是公平的,也可能是不公平的,这是唯一的随机数生成过程,因此想出一个随机数生成器,它输出从1到7的整数,其中获得每个整数的概率是1/7。
数据生成过程的效率至关重要。
Answers:
翻转硬币两次。如果它降落HH
或TT
,请忽略它,然后再次翻转两次。
现在,硬币有相等的概率出现HT
或TH
。如果出现HT
,叫这个H1
。如果出现TH
,叫这个T1
。
继续获取H1
或T1
直到连续获取三个。这三个结果为您提供了基于下表的数字:
H1 H1 H1 -> 1
H1 H1 T1 -> 2
H1 T1 H1 -> 3
H1 T1 T1 -> 4
T1 H1 H1 -> 5
T1 H1 T1 -> 6
T1 T1 H1 -> 7
T1 T1 T1 -> [Throw out all results so far and repeat]
我认为这可以很好地工作,尽管您会在过程中浪费很多!
假设。
步骤1 :。抛硬币5次。
如果结果是
1,返回并停止。
2,返回并停止。
3,返回并停止。
4返回并停止。
5,返回并停止。
6,返回并停止。
7,返回并停止。
第2步:。如果结果不是以上任何一项,请重复步骤1。
请注意,无论的值如何,上面列出的七个结果中的每一个都有的概率,并且预期抛硬币的数量为。抛掷器不需要知道的值(和除外);可以保证实验终止时,这七个整数同样有可能被返回(并保证以结束)。q = p 3(1 - p )2 5 pp≠0p≠11
其他答案中描述的某些方法使用的是您抛出序列的方案在“转弯”中硬币并根据结果选择1或7之间的数字或放弃转弯并再次投掷。
诀窍是在可能性的扩展中找到7个结果的倍数,它们具有相同的概率并使它们彼此匹配。
因为结果总数不是7的倍数,所以我们有一些我们无法分配给结果的结果,并且有一定的概率需要丢弃结果并重新开始。
直觉上我们可以说掷骰子七次会非常有趣。因为我们只需要把中的扔掉可能性。即7次正面和0次正面。
对于所有其他可能性,总有7个盒数相同的磁头。即1头7例,2头21例,3头35例,4头35例,5头21例,6头7例。
因此,如果您计算数字(丢弃0个头和7个头)
如果使用 Bernoulli分布变量(值0或1),则X模7是一个具有七个可能结果的统一变量。
问题仍然是每转的最佳滚动数是多少。每转掷更多骰子会花费更多,但会降低不得不再次掷骰的可能性。
下图显示了手动计算每转硬币前几次翻转的次数。(可能有一种分析解决方案,但是我可以肯定地说,具有7个硬币翻转的系统提供了关于必要数量硬币翻转的期望值的最佳方法)
# plot an empty canvas
plot(-100,-100,
xlab="flips per turn",
ylab="E(total flips)",
ylim=c(7,400),xlim=c(0,20),log="y")
title("expectation value for total number of coin flips
(number of turns times flips per turn)")
# loop 1
# different values p from fair to very unfair
# since this is symmetric only from 0 to 0.5 is necessary
# loop 2
# different values for number of flips per turn
# we can only use a multiple of 7 to assign
# so the modulus will have to be discarded
# from this we can calculate the probability that the turn succeeds
# the expected number of flips is
# the flips per turn
# divided by
# the probability for the turn to succeed
for (p in c(0.5,0.2,0.1,0.05)) {
Ecoins <- rep(0,16)
for (dr in (5:20)){
Pdiscards = 0
for (i in c(0:dr)) {
Pdiscards = Pdiscards + p^(i)*(1-p)^(dr-i) * (choose(dr,i) %% 7)
}
Ecoins[dr-4] = dr/(1-Pdiscards)
}
lines(5:20, Ecoins)
points(5:20, Ecoins, pch=21, col="black", bg="white", cex=0.5)
text(5, Ecoins[1], paste0("p = ",p), pos=2)
}
注意:以下计算是针对翻转次数的期望值,对于公平的硬币,对于不同的做到这一点将变得一团糟,但是原理仍然相同(尽管不同的簿记方式案件是必要的)
我们应该可以选择情况(而不是的公式),以便我们可以更早停止。
通过五次硬币翻转,我们可以得到六种可能的不同的无序的首尾集:
1 + 5 + 10 + 10 + 5 + 1有序集
我们可以使用十种情况的组(即具有2个头的组或具有2个尾部的组)来选择(等概率)一个数字。在2 ^ 5 = 32例中有14例发生这种情况。这给我们留下了:
1 + 5 + 3 + 3 + 5 + 1有序集
额外掷出(第6)我们可以得到七种可能的无序的组:
1 + 6 + 8 + 6 + 8 + 6 + 1有序集
我们可以使用八种情况的组(即具有3个头的组或具有3个尾部的组)来选择(等概率)一个数字。2 *(2 ^ 5-14)= 36例中有14例发生这种情况。这给我们留下了:
1 + 6 + 1 + 6 + 1 + 6 + 1有序集
通过另一次(第7次)额外的硬币翻转,我们可以得到八种可能的无序的正面和反面组:
1 + 7 + 7 + 7 + 7 + 7 + 7 + 1有序集
我们可以使用具有七个案例(所有尾巴和所有头案例除外)的组来选择(均等概率)一个数字。在44例病例中有42例发生这种情况。这给我们留下了:
1 + 0 + 0 + 0 + 0 + 0 + 0 + 1有序集
(我们可以继续执行此操作,但只有在第49步中才能使我们受益)
所以选择一个数字的可能性
这使一轮翻转次数的期望值为条件,条件是成功且p = 0.5:
在p = 0.5的条件下,翻转总数的期望值(直到成功):
NcAdams的答案使用了该停止规则策略的一种变体(每次都提供两个新的硬币翻转),但并不是最佳地选择所有翻转。
Clid的答案也可能相似,尽管可能会有不规则的选择规则,即每两个硬币翻转可能会选择一个数字,但不一定具有相等的概率(差异会在以后的硬币翻转中得到修复)
使用类似原理的其他方法是NcAdams和AdamO的方法。
原理是:在确定一定数量的首尾之后,才决定1到7之间的数字。在翻过次后,对于得出每个决策,都有一个类似的,同样可能的决策导致(相同的前额和尾数,但顺序不同)。某些前后关系可能导致重新开始的决定。
对于这种类型的方法,此处放置的方法是最有效的,因为它会尽早做出决定(一旦有第7个相等的概率的前后序列,在第翻转后,我们可以使用他们决定一个数字,如果遇到其中一种情况,我们就无需进一步处理了)。
下面的图像和仿真对此进行了演示:
#### mathematical part #####
set.seed(1)
#plotting this method
p <- seq(0.001,0.999,0.001)
tot <- (5*7*(p^2*(1-p)^3+p^3*(1-p)^2)+
6*7*(p^2*(1-p)^4+p^4*(1-p)^2)+
7*7*(p^1*(1-p)^6+p^2*(1-p)^5+p^3*(1-p)^4+p^4*(1-p)^3+p^5*(1-p)^2+p^6*(1-p)^1)+
7*1*(0+p^7+(1-p)^7) )/
(1-p^7-(1-p)^7)
plot(p,tot,type="l",log="y",
xlab="p",
ylab="expactation value number of flips"
)
#plotting method by AdamO
tot <- (7*(p^20-20*p^19+189*p^18-1121*p^17+4674*p^16-14536*p^15+34900*p^14-66014*p^13+99426*p^12-119573*p^11+114257*p^10-85514*p^9+48750*p^8-20100*p^7+5400*p^6-720*p^5)+6*
(-7*p^21+140*p^20-1323*p^19+7847*p^18-32718*p^17+101752*p^16-244307*p^15+462196*p^14-696612*p^13+839468*p^12-806260*p^11+610617*p^10-357343*p^9+156100*p^8-47950*p^7+9240*p^6-840*p^5)+5*
(21*p^22-420*p^21+3969*p^20-23541*p^19+98154*p^18-305277*p^17+733257*p^16-1389066*p^15+2100987*p^14-2552529*p^13+2493624*p^12-1952475*p^11+1215900*p^10-594216*p^9+222600*p^8-61068*p^7+11088*p^6-1008*p^5)+4*(-
35*p^23+700*p^22-6615*p^21+39235*p^20-163625*p^19+509425*p^18-1227345*p^17+2341955*p^16-3595725*p^15+4493195*p^14-4609675*p^13+3907820*p^12-2745610*p^11+1592640*p^10-750855*p^9+278250*p^8-76335*p^7+13860*p^6-
1260*p^5)+3*(35*p^24-700*p^23+6615*p^22-39270*p^21+164325*p^20-515935*p^19+1264725*p^18-2490320*p^17+4027555*p^16-5447470*p^15+6245645*p^14-6113275*p^13+5102720*p^12-3597370*p^11+2105880*p^10-999180*p^9+371000
*p^8-101780*p^7+18480*p^6-1680*p^5)+2*(-21*p^25+420*p^24-3990*p^23+24024*p^22-103362*p^21+340221*p^20-896679*p^19+1954827*p^18-3604755*p^17+5695179*p^16-7742301*p^15+9038379*p^14-9009357*p^13+7608720*p^12-
5390385*p^11+3158820*p^10-1498770*p^9+556500*p^8-152670*p^7+27720*p^6-2520*p^5))/(7*p^27-147*p^26+1505*p^25-10073*p^24+49777*p^23-193781*p^22+616532*p^21-1636082*p^20+3660762*p^19-6946380*p^18+11213888*p^17-
15426950*p^16+18087244*p^15-18037012*p^14+15224160*p^13-10781610*p^12+6317640*p^11-2997540*p^10+1113000*p^9-305340*p^8+55440*p^7-5040*p^6)
lines(p,tot,col=2,lty=2)
#plotting method by NcAdam
lines(p,3*8/7/(p*(1-p)),col=3,lty=2)
legend(0.2,500,
c("this method calculation","AdamO","NcAdams","this method simulation"),
lty=c(1,2,2,0),pch=c(NA,NA,NA,1),col=c(1,2,3,1))
##### simulation part ######
#creating decision table
mat<-matrix(as.numeric(intToBits(c(0:(2^5-1)))),2^5,byrow=1)[,c(1:12)]
colnames(mat) <- c("b1","b2","b3","b4","b5","b6","b7","sum5","sum6","sum7","decision","exit")
# first 5 rolls
mat[,8] <- sapply(c(1:2^5), FUN = function(x) {sum(mat[x,1:5])})
mat[which((mat[,8]==2)&(mat[,11]==0))[1:7],12] = rep(5,7) # we can stop for 7 cases with 2 heads
mat[which((mat[,8]==2)&(mat[,11]==0))[1:7],11] = c(1:7)
mat[which((mat[,8]==3)&(mat[,11]==0))[1:7],12] = rep(5,7) # we can stop for 7 cases with 3 heads
mat[which((mat[,8]==3)&(mat[,11]==0))[1:7],11] = c(1:7)
# extra 6th roll
mat <- rbind(mat,mat)
mat[c(33:64),6] <- rep(1,32)
mat[,9] <- sapply(c(1:2^6), FUN = function(x) {sum(mat[x,1:6])})
mat[which((mat[,9]==2)&(mat[,11]==0))[1:7],12] = rep(6,7) # we can stop for 7 cases with 2 heads
mat[which((mat[,9]==2)&(mat[,11]==0))[1:7],11] = c(1:7)
mat[which((mat[,9]==4)&(mat[,11]==0))[1:7],12] = rep(6,7) # we can stop for 7 cases with 4 heads
mat[which((mat[,9]==4)&(mat[,11]==0))[1:7],11] = c(1:7)
# extra 7th roll
mat <- rbind(mat,mat)
mat[c(65:128),7] <- rep(1,64)
mat[,10] <- sapply(c(1:2^7), FUN = function(x) {sum(mat[x,1:7])})
for (i in 1:6) {
mat[which((mat[,10]==i)&(mat[,11]==0))[1:7],12] = rep(7,7) # we can stop for 7 cases with i heads
mat[which((mat[,10]==i)&(mat[,11]==0))[1:7],11] = c(1:7)
}
mat[1,12] = 7 # when we did not have succes we still need to count the 7 coin tosses
mat[2^7,12] = 7
draws = rep(0,100)
num = rep(0,100)
# plotting simulation
for (p in seq(0.05,0.95,0.05)) {
n <- rep(0,1000)
for (i in 1:1000) {
coinflips <- rbinom(7,1,p) # draw seven numbers
I <- mat[,1:7]-matrix(rep(coinflips,2^7),2^7,byrow=1) == rep(0,7) # compare with the table
Imatch = I[,1]*I[,2]*I[,3]*I[,4]*I[,5]*I[,6]*I[,7] # compare with the table
draws[i] <- mat[which(Imatch==1),11] # result which number
num[i] <- mat[which(Imatch==1),12] # result how long it took
}
Nturn <- mean(num) #how many flips we made
Sturn <- (1000-sum(draws==0))/1000 #how many numbers we got (relatively)
points(p,Nturn/Sturn)
}
为了更好的比较,用缩放的另一个图像:
放大本文和评论中描述的比较方法
“第7步有条件跳过”是可以在早期停止规则上进行的轻微改进。在这种情况下,在第6次翻转之后,您不会选择概率相同的组。您有6个具有相等概率的组,而1个具有稍微不同的概率的组(对于最后一组,当您有6个正面或反面时,您需要再翻转一次额外的时间,并且由于您丢弃了7个正面或7个反面,您将结束毕竟具有相同的概率)
将一个框划分为七个等面积区域,每个区域均用整数标记。将硬币投掷到盒子中的方式使其在每个区域具有相等的着陆概率。
这只是开玩笑的一半-基本上与使用物理蒙特卡洛过程估算过程相同,例如将米粒滴到纸上并画上一个圆圈。
这是在或的情况下唯一可行的答案之一。p = 0
0
,如果第二次掷得更远,则为1
编辑:基于其他人的反馈。
这是一个有趣的想法:
设置{1,2,3,4,5,6,7}的列表。依次为列表中的每个元素投掷硬币。如果将其正面朝上放到特定元素上,请从列表中删除该数字。如果删除了列表的特定迭代中的所有数字,请重复采样。这样做直到只剩下一个数字。
drop.one <- function(x, p) {
drop <- runif(length(x)) < p
if (all(drop))
return(x)
return(x[!drop])
}
sample.recur <- function(x, p) {
if (length(x) > 1)
return(sample.recur(drop.one(x, p), p))
return(x)
}
# x <- c(1:7,7:1)
x <- 1:7
p <- 0.01
out <- replicate(1e5, sample.recur(x, p))
round(prop.table(table(out)), 2)
给我大致均匀的分布
> round(prop.table(table(out)), 2)
out
1 2 3 4 5 6 7
0.14 0.14 0.15 0.14 0.14 0.14 0.14
M: matrix(
[(1-p)^7, 0, 0,0,0,0,1,1],
[7* p*(1-p)^6, (1-p)^6, 0,0,0,0,0,0],
[21*p^2*(1-p)^5, 6*p*(1-p)^5, (1-p)^5,0,0,0,0,0],
[35*p^3*(1-p)^4, 15*p^2*(1-p)^4, 5*p*(1-p)^4,(1-p)^4,0,0,0,0],
[35*p^4*(1-p)^3, 20*p^3*(1-p)^3, 10*p^2*(1-p)^3,4*p*(1-p)^3,(1-p)^3,0,0,0],
[21*p^5*(1-p)^2, 15*p^4*(1-p)^2, 10*p^3*(1-p)^2,6*p^2*(1-p)^2,3*p*(1-p)^2,(1-p)^2,0,0],
[7* p^6*(1-p)^1, 6*p^5*(1-p), 5*p^4*(1-p),4*p^3*(1-p),3*p^2*(1-p),2*(1-p)*p,0,0],
[p^7, p^6, p^5,p^4,p^3,p^2,0,0]
);
z: nullspace(M-diagmatrix(8,1));
x : apply (addcol, args (z));
t : [7,6,5,4,3,2,0,0];
plot2d(t.x/x[7],[p,0,1],logy);
# plotting empty canvas
plot(-100,-100,
xlab="p",
ylab="E(total flips)",
ylim=c(10,1000),xlim=c(0,1),log="y")
# plotting simulation
for (p in seq(0.1,0.9,0.05)) {
n <- rep(0,10000)
for (i in 1:10000) {
success = 0
tests = c(1,1,1,1,1,1,1) # start with seven numbers in the set
count = 0
while(success==0) {
for (j in 1:7) {
if (tests[j]==1) {
count = count + 1
if (rbinom(1,1,p) == 1) {
tests[j] <- 0 # elliminate number when we draw heads
}
}
}
if (sum(tests)==1) {
n[i] = count
success = 1 # end when 1 is left over
}
if (sum(tests)==0) {
tests = c(1,1,1,1,1,1,1) # restart when 0 are left over
}
}
}
points(p,mean(n))
}
# plotting formula
p <- seq(0.001,0.999,0.001)
tot <- (7*(p^20-20*p^19+189*p^18-1121*p^17+4674*p^16-14536*p^15+34900*p^14-66014*p^13+99426*p^12-119573*p^11+114257*p^10-85514*p^9+48750*p^8-20100*p^7+5400*p^6-720*p^5)+6*
(-7*p^21+140*p^20-1323*p^19+7847*p^18-32718*p^17+101752*p^16-244307*p^15+462196*p^14-696612*p^13+839468*p^12-806260*p^11+610617*p^10-357343*p^9+156100*p^8-47950*p^7+9240*p^6-840*p^5)+5*
(21*p^22-420*p^21+3969*p^20-23541*p^19+98154*p^18-305277*p^17+733257*p^16-1389066*p^15+2100987*p^14-2552529*p^13+2493624*p^12-1952475*p^11+1215900*p^10-594216*p^9+222600*p^8-61068*p^7+11088*p^6-1008*p^5)+4*(-
35*p^23+700*p^22-6615*p^21+39235*p^20-163625*p^19+509425*p^18-1227345*p^17+2341955*p^16-3595725*p^15+4493195*p^14-4609675*p^13+3907820*p^12-2745610*p^11+1592640*p^10-750855*p^9+278250*p^8-76335*p^7+13860*p^6-
1260*p^5)+3*(35*p^24-700*p^23+6615*p^22-39270*p^21+164325*p^20-515935*p^19+1264725*p^18-2490320*p^17+4027555*p^16-5447470*p^15+6245645*p^14-6113275*p^13+5102720*p^12-3597370*p^11+2105880*p^10-999180*p^9+371000
*p^8-101780*p^7+18480*p^6-1680*p^5)+2*(-21*p^25+420*p^24-3990*p^23+24024*p^22-103362*p^21+340221*p^20-896679*p^19+1954827*p^18-3604755*p^17+5695179*p^16-7742301*p^15+9038379*p^14-9009357*p^13+7608720*p^12-
5390385*p^11+3158820*p^10-1498770*p^9+556500*p^8-152670*p^7+27720*p^6-2520*p^5))/(7*p^27-147*p^26+1505*p^25-10073*p^24+49777*p^23-193781*p^22+616532*p^21-1636082*p^20+3660762*p^19-6946380*p^18+11213888*p^17-
15426950*p^16+18087244*p^15-18037012*p^14+15224160*p^13-10781610*p^12+6317640*p^11-2997540*p^10+1113000*p^9-305340*p^8+55440*p^7-5040*p^6)
lines(p,tot)
#plotting comparison with alternative method
lines(p,3*8/7/(p*(1-p)),lty=2)
legend(0.2,500,
c("simulation","calculation","comparison"),
lty=c(0,1,2),pch=c(1,NA,NA))
p <- 0.99
我会得到输出0.89 0.02 0.02 0.02 0.02 0.02 0.02
问题有点模棱两可,是问“以相等的概率生成等于或小于7的随机整数”,还是问“以相等的概率生成7个等于或小于7的随机整数?” -但是整数的空间是多少?!?
我假设是前者,但是一旦解决了这个问题,我正在应用的相同逻辑也可以扩展到后者。
使用有偏向的硬币,您可以按照以下步骤生产公平的硬币:https : //en.wikipedia.org/wiki/Fair_coin#Fair_results_from_a_biased_coin
可以将7或更少的数字写成三位{0,1}数字。因此,所有需要做的就是按照上述步骤进行三遍,并将生成的二进制数转换回十进制。
永远不会浪费翻转的解决方案,这对于非常偏向的硬币很有帮助。
这种算法的缺点(至少是书面形式)是使用任意精度算法。实际上,您可能要使用它直到整数溢出,然后才将其丢弃并重新开始。
另外,你需要知道什么偏见是 ......你可能不哪,也就是说,如果它随温度变化,最喜欢的物理现象。
假设有正面的机会是30%。
[1, 8)
。[1, 3.1)
。否则,请使用正确的70%,因此您的新范围是[3.1, 8)
。完整代码:
#!/usr/bin/env python3
from fractions import Fraction
from collections import Counter
from random import randrange
BIAS = Fraction(3, 10)
STAT_COUNT = 100000
calls = 0
def biased_rand():
global calls
calls += 1
return randrange(BIAS.denominator) < BIAS.numerator
def can_generate_multiple(start, stop):
if stop.denominator == 1:
# half-open range
stop = stop.numerator - 1
else:
stop = int(stop)
start = int(start)
return start != stop
def unbiased_rand(start, stop):
if start < 0:
# negative numbers round wrong
return start + unbiased_rand(0, stop - start)
assert isinstance(start, int) and start >= 0
assert isinstance(stop, int) and stop >= start
start = Fraction(start)
stop = Fraction(stop)
while can_generate_multiple(start, stop):
if biased_rand():
old_diff = stop - start
diff = old_diff * BIAS
stop = start + diff
else:
old_diff = stop - start
diff = old_diff * (1 - BIAS)
start = stop - diff
return int(start)
def stats(f, *args, **kwargs):
c = Counter()
for _ in range(STAT_COUNT):
c[f(*args, **kwargs)] += 1
print('stats for %s:' % f.__qualname__)
for k, v in sorted(c.items()):
percent = v * 100 / STAT_COUNT
print(' %s: %f%%' % (k, percent))
def main():
#stats(biased_rand)
stats(unbiased_rand, 1, 7+1)
print('used %f calls at bias %s' % (calls/STAT_COUNT, BIAS))
if __name__ == '__main__':
main()
diff *= 7
我的想法编写它……实际上,每次尝试都不需要使用相同的基准。
这也仅适用于和。
我们首先使用NcAdams回答的过程将(可能是)不公平硬币变成公平硬币:
翻转硬币两次。如果它降落
HH
或TT
,请忽略它,然后再次翻转两次。现在,硬币有相等的概率出现
HT
或TH
。如果出现HT
,叫这个H1
。如果出现TH
,叫这个T1
。
现在,我们使用公平硬币以二进制形式生成介于和之间的实数。令和。从字符串开始,翻转硬币,然后将结果数字附加到字符串的末尾。重复新的字符串。例如,该序列将为您提供数字。H1
T1
0.
H1 H1 T1
是一个重复的小数,并且右侧在以2为底的位置,我们得到:
继续翻转公平硬币以生成十进制数字,直到序列中的数字与上述序列之一不匹配为止,然后选择数字,以使生成的数字小于且大于。由于每个生成的数字均具有相同的可能性,因此我们选择了一个介于到之间的数字,且概率相同。
受AdamO的答案启发,这是一个避免偏见的Python解决方案:
def roll(p, n):
remaining = range(1,n+1)
flips = 0
while len(remaining) > 1:
round_winners = [c for c in remaining if random.choices(['H','T'], [p, 1.0-p]) == ['H']]
flips += len(remaining)
if len(round_winners) > 0:
remaining = round_winners
p = 1.0 - p
return remaining[0], flips
这里有两个主要变化:主要变化是,如果所有数字都在一轮中被丢弃,请重复该轮。我也翻转选择正面还是反面意味着每次都丢弃。当p = 0.999时,如果p接近0或1,则所需的翻转次数减少了约70%。
似乎我们每次都可以更改每次翻转的结果映射。因此,为了方便起见,使用前七个正整数,我们给出以下顺序:
等等
无用翻转的数量在这里往往会