伯特兰悖论模拟



前几天我在Hacker News上看到了这个:http://web.mit.edu/tee/www/bertrand/problem.html

它基本上是说半径为1的圆上随机弦的长度大于根号3的概率是多少。

看着它,答案似乎很明显是1/3,但是HN上的评论中有比我更聪明的人在争论这个问题。https://news.ycombinator.com/item?id=10000926

我不想争论,但我确实想确定我没有疯。所以我编码了我认为可以证明P = 1/3的东西,但我最终得到P ~ 0.36。所以,我的代码肯定有问题。

我可以得到一个完整性检查吗?

    package com.jonas.betrand;
    import java.awt.geom.Point2D;
    import java.util.Random;
    public class Paradox {
        final static double ROOT_THREE = Math.sqrt(3);
        public static void main(String[] args) {
            int greater = 0;
            int less = 0;
            for (int i = 0; i < 1000000; i++) {
                Point2D.Double a = getRandomPoint();
                Point2D.Double b = getRandomPoint();
                //pythagorean
                if (Math.sqrt(Math.pow((a.x - b.x), 2) + Math.pow((a.y - b.y), 2)) > ROOT_THREE) {
                    greater++;
                } else {
                    less++;
                }   
            }
            System.out.println("Probability Observerd: " + (double)greater/(greater+less));
        }
        public static Point2D.Double getRandomPoint() {
            //get an x such that -1 < x < 1
            double x = Math.random();
            boolean xsign = new Random().nextBoolean();
            if (!xsign) {
                x *= -1;
            }
            //formula for a circle centered on origin with radius 1: x^2 + y^2 = 1
            double y = Math.sqrt(1 - (Math.pow(x, 2)));
            boolean ysign = new Random().nextBoolean();
            if (!ysign) {
                y *= -1;
            }
            Point2D.Double point = new Point2D.Double(x, y);
            return point;
        }
    }

编辑:多亏了一群人让我明白,我发现我寻找随机点的方法并不是那么随机。这是一个修复函数,它返回大约1/3。

        public static Point2D.Double getRandomPoint() {
            //get an x such that -1 < x < 1
            double x = Math.random();
            Random r = new Random();
            if (!r.nextBoolean()) {
                x *= -1;
            }
            //circle centered on origin: x^2 + y^2 = r^2. r is 1. 
            double y = Math.sqrt(1 - (Math.pow(x, 2)));
            if (!r.nextBoolean()) {
                y *= -1;
            }
            if (r.nextBoolean()) {
                return new Point2D.Double(x, y);
            } else {
                return new Point2D.Double(y, x);
            }
        }

我认为你需要假设一个固定点,比如在(0,1),然后选择一个随机的旋转量,在圆周围[0,2 *pi],作为弦的第二个点的位置。

只是为了好玩,我用Swift写了你的错误版本(学习Swift!):

    struct P {
        let x, y: Double
        init() {
            x = (Double(arc4random()) / 0xFFFFFFFF) * 2 - 1
            y = sqrt(1 - x * x) * (arc4random() % 2 == 0 ? 1 : -1)
        }
        func dist(other: P) -> Double {
            return sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y))
        }
    }
    let root3 = sqrt(3.0)
    let total = 100_000_000
    var samples = 0
    for var i = 0; i < total; i++ {
        if P().dist(P()) > root3 {
            samples++
        }
    }
    println(Double(samples) / Double(total))

答案确实是0.36。正如评论所解释的那样,随机的X值更有可能选择pi/2附近的"平坦区域",而极不可能选择0和pi附近的"垂直挤压"区域。

在p的构造函数中很容易修复:(Double(arc4random()) / 0xFFFFFFFF是[0,1)中随机浮点数的花哨说法)

        let angle = Double(arc4random()) / 0xFFFFFFFF * M_PI * 2
        x = cos(angle)
        y = sin(angle)
        // outputs 0.33334509

伯特兰悖论正是:一个悖论。答案可以是1/3或1/2,这取决于如何解释这个问题。似乎你采用了随机弦法,线的一边是固定的,然后你在圆的任意部分随机画一个弦。使用这种方法,画出比sqrt(3)长的和弦的机会确实是1/3。

但是如果你用另一种方法,我叫它随机半径方法,你会发现它可以是1/2!随机半径是这样的,在圆上画一个半径,然后随机取这个半径所平分的弦。在这一点上,一个随机弦将比sqrt(3) 1/2的时间长。

最后是随机中点法。在圆中选择一个随机点,然后以这个随机点作为弦的中点画一条弦。如果该点落在半径为1/2的同心圆内,则弦短于根号(3)。如果它落在同心圆之外,它比根号(3)长。半径为1/2的圆的面积是半径为1的圆的1/4,因此弦小于根号(3)的概率是1/4。

至于你的代码,我还没有时间去看它,但希望这澄清悖论(这只是一个不完整的问题,而不是一个悖论):D

我认为伯特兰悖论与其说是一个悖论,不如说是概率论上的一堂警诫课。它实际上是在问一个问题:random是什么意思?

Bertrand认为有三种自然但不同的方法来随机选择和弦,给出三种不同的答案。当然,还有其他随机方法,但这些方法可能不是最自然的方法(也就是说,不是首先想到的方法)。例如,我们可以以非均匀的方式随机定位两个和弦端点。或者我们根据一些非均匀密度来定位弦中点,就像截断的双变量正态线。

要用编程语言模拟这三种方法,您需要能够在单位间隔上生成一致的随机变量,这是所有标准(伪)随机数生成器都应该做的。对于其中一种方法/解决方案(随机中点方案),你必须取其中一个均匀随机变量的平方根。然后将随机变量乘以一个合适的因子(或重新缩放)。然后,对于每种模拟方法(或解决方案),一些几何给出了两个端点的表达式。

关于这个问题的更多细节,我已经写了一篇文章。我推荐我在文章末尾引用的链接和书籍,在进一步阅读部分。例如,请参阅这组新出版的课堂讲稿的1.3节。贝特朗悖论也出现在艾萨克的《概率的乐趣》中。在Clark的从a到Z的悖论一书中以一种非数学的方式讨论了这个问题。

我还上传了一些MATLAB, R和Python的仿真代码,可以在这里找到。

例如,在Python中(使用NumPy):

import numpy as np; #NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt #for plotting
from matplotlib import collections  as mc #for plotting line chords      
###START Parameters START###
#Simulation disk dimensions
xx0=0; yy0=0; #center of disk
r=1; #disk radius
numbLines=10**2;#number of lines
###END Parameters END###
###START Simulate three solutions on a disk START###
#Solution A
thetaA1=2*np.pi*np.random.uniform(0,1,numbLines); #choose angular component uniformly
thetaA2=2*np.pi*np.random.uniform(0,1,numbLines); #choose angular component uniformly
#calculate chord endpoints
xxA1=xx0+r*np.cos(thetaA1);
yyA1=yy0+r*np.sin(thetaA1);
xxA2=xx0+r*np.cos(thetaA2);
yyA2=yy0+r*np.sin(thetaA2);
#calculate midpoints of chords
xxA0=(xxA1+xxA2)/2; yyA0=(yyA1+yyA2)/2;
#Solution B
thetaB=2*np.pi*np.random.uniform(0,1,numbLines); #choose angular component uniformly
pB=r*np.random.uniform(0,1,numbLines); #choose radial component uniformly
qB=np.sqrt(r**2-pB**2); #distance to circle edge (alonge line)
#calculate trig values
sin_thetaB=np.sin(thetaB);
cos_thetaB=np.cos(thetaB);
#calculate chord endpoints
xxB1=xx0+pB*cos_thetaB+qB*sin_thetaB;
yyB1=yy0+pB*sin_thetaB-qB*cos_thetaB;
xxB2=xx0+pB*cos_thetaB-qB*sin_thetaB;
yyB2=yy0+pB*sin_thetaB+qB*cos_thetaB;
#calculate midpoints of chords
xxB0=(xxB1+xxB2)/2; yyB0=(yyB1+yyB2)/2;
#Solution C
#choose a point uniformly in the disk
thetaC=2*np.pi*np.random.uniform(0,1,numbLines); #choose angular component uniformly
pC=r*np.sqrt(np.random.uniform(0,1,numbLines)); #choose radial component
qC=np.sqrt(r**2-pC**2); #distance to circle edge (alonge line)
#calculate trig values
sin_thetaC=np.sin(thetaC);
cos_thetaC=np.cos(thetaC);
#calculate chord endpoints
xxC1=xx0+pC*cos_thetaC+qC*sin_thetaC;
yyC1=yy0+pC*sin_thetaC-qC*cos_thetaC;
xxC2=xx0+pC*cos_thetaC-qC*sin_thetaC;
yyC2=yy0+pC*sin_thetaC+qC*cos_thetaC;
#calculate midpoints of chords
xxC0=(xxC1+xxC2)/2; yyC0=(yyC1+yyC2)/2;
###END Simulate three solutions on a disk END###

相关内容

  • 没有找到相关文章

最新更新