求解四只鴨子在同一半圓池塘的概率


  • 問題模型

  有四只鴨子,隨機分布在圓形的池塘中,請問四只鴨子同時處於同一個半圓的概率有多大?

  • 統計模擬

  使用計算機進行大量的隨機實驗,統計推斷出未知變量的概率。我們將進行100萬次獨立采樣試驗,每次試驗中讓四只鴨子隨機分布在圓形池塘中,統計四只鴨子處於同一半圓的試驗次數。那么使用該次數除以100萬,就可以大概得到題目所求的概率。

  • 建立模型

  假設以原點為中心,半徑為1的單位圓即為問題里的池塘,四只鴨子為出現在單位圓內部隨機位置的四個點,那么這四個點位於同一半圓內的概率即為題目所求的概率值。

  • 怎么判斷四個點位於同一個半圓?

  四個點可能一下子有點抽象,那么不妨將問題弱化:考慮從四個點變為三個點的情況。怎么判斷三個點位於同一個半圓?

 

   如上圖所示,在圓中有三個點,我們可以畫出三個點組成的三角形的三條邊,不難發現,圓的圓心在三角形的外邊,這時我們一定可以找到圓的一條直徑,將三角形分在圓的某個半圓中。

   那如果圓心在三個點組成的三角形內部呢,能找到一條直徑將三個點分在同一個半圓嗎?稍微在頭腦中模擬一下,不難想到,答案是否定的。如下圖所示,無論怎樣選取直徑,都不能將三個點分到同一個半圓中。

 

  於是我們可以得到一個結論,只要圓心位於三角形外,就一定能將三角形分在同一個半圓中。

  • 那么推廣到四個點的情況,結論會是怎樣的?

  首先,如果圓中隨機分布的四個點共半圓,那么這四個點中的任意三個也一定共半圓,這個很容易理解。那么結論反過來成立嗎?也就是說,圓中有四個點,任意三個點都共半圓,那么這四個點一定共半圓嗎?

  我們可以使用反證法。

  假設存在圓中有四個點,其中任意三個點都共半圓,但是四個點不共半圓的情況。不失一般性,我們假設四個點為a,b,c,d。如下圖所示,我們畫出a,b,c點和經過這三個點的圓直徑。

  三條直徑將圓分為了6個區域,分別為S1~S6。現在我們考慮將第四個點放在什么地方,可以滿足任意三個點共半圓,但是四個點不共半圓的情況。

  如果d點放在區域S1,S2或者S3,顯然,abcd共藍色直徑左邊的半圓,矛盾;

  如果d點放在區域S6,顯然,abcd共紫色半徑下方的半圓,矛盾;

  如果d點放在區域S4,此時abcd不共半圓,但是此時abd點也不共半圓,與假設任意三個點共半圓,矛盾;

  如果d點放在區域S5,此時abcd不共半圓,但是此時acd點也不共半圓,與假設任意三個點共半圓,矛盾。

  綜上,d點無論放在哪個區域,都不滿足假設的條件,因此假設不成立!

  因此我們得到結論,圓內隨機分布的四個點,如果其中任意三個點都共半圓,那么這四個點也一定共半圓。

  於是我們得到了判斷四只鴨子位於池塘的同一半圓區域的判別條件。

  • 判斷點在三角形外部的方法

  判斷一個點在三角形外部還是內部有很多種方法,我常用的是兩種,一種是內角和法:如果點在三角形內部,那么該點與三角形的三個頂點形成的三個角的和為360度,反之,如果點在三角形外部,那么三個角之和一定小於360度。

  第二種是同向法。對於三角形Δabc,如果點p在三角形內部,那么對於三角形的每條邊構成的向量——例如向量ab——來說,其另外一個頂點c與該邊的起始點a構成的向量ca,這兩個向量的叉積方向與向量pa和向量ab的叉積方向是一致的,從數值上的表現來看,要么同為正值,要么同為負值。對於向量bc,向量ca的情況,結果也是一致的。而對於在三角形外部的點q來說,至少有一條邊的情況不滿足叉積方向一致。

  如上圖中的例子,先看三角形內部的點p。對於邊ab來說,向量ab叉乘向量ca,根據右手定則,叉積的方向指向屏幕里面,向量ab叉乘向量pa,根據右手定則,叉積的方向指向屏幕里面,兩個叉積方向一致;對於邊bc來說,向量bc叉乘向量ab,根據右手定則,叉積的方向指向屏幕里面,向量bc叉乘向量pb,根據右手定則,方向指向屏幕里面,兩個叉積方向一致;對於邊ca來說,向量ca叉乘向量bc,根據右手定則,方向指向屏幕里面,ca叉乘pc,方向同樣是指向屏幕里面。因此可以判斷點p在三角形內部。

  而對於q點,我們可以直接看邊ca,向量ca與向量bc的叉乘方向指向屏幕里面,但是向量ca與向量qc叉乘,根據右手定則,叉積的方向指向屏幕外面,兩者方向不一致,可以判斷q點在三角形外部。

  我們可以從數值上來驗證這個結論。對於二維向量a(ax,ay)和b(bx,by),其叉積的數值為ax*by-bx*ay。對於筆者所畫的上圖,如果以左下角為坐標原點,正右方向為x軸方向,正上方向為y軸方向,單個像素為單位長度的話,各點的坐標如下:

  a(75, 145), b(266, 76), c(241, 302), p(204, 164), q(95, 252)

  先看邊ab,

  ab X ca = (191, -69) X (-166, -157) = 191 * (-157) - (-69) * (-166) = -41441

  ab X pa = (191, -69) X (-129, -19) = 191 * (-19) - (-129) * (-69) = -12530

  ab X qa = (191, -69) X (-20, -107) = 191 * (-107) - (-20) * (-69) = -21887

  三個叉乘的方向一致,再看邊bc

  bc X ab = (-25, 226) X (191, -69) = -25 * (-69) - 226 * 191 = -44891

  bc X pb = (-25, 226) X (62, -88) = -25 * (-88) - 226 * 62 = -11812

  bc X qb = (-25, 226) X (171, -176) = -25 * (-176) - 226 * 171 = -34246

  三個叉乘方向一致,最后看邊ca,

  ca X bc = (-166, -157) X (-25, 226) = -166 * 226 - (-25) * (-157) = -41441

  ca X pc = (-166, -157) X (37, 138) = -166 * 138 - 37 * (-157) = -17099

  ca X qc = (-166, -157) X (146, 50) = -166 * 50 - 146 * (-157) = 14622

  向量ca與向量bc的叉積方向和向量ca與向量pc的叉積方向一致,但是和向量ca與向量qc的叉積方向不一致。所以得出結論,點p在三角形abc內部,而點q在外部。

  詳細的論述請參考鏈接http://www.blackpawn.com/texts/pointinpoly/default.html

  綜上,我們已經得到了求解題目的所有步驟。

  • 代碼

  使用同向法判斷點在三角形外部代碼

 1 bool beyondTriangle(Point2f& a, Point2f& b, Point2f& c, Point2f& p)
 2 {
 3     Point2f ab = Point2f(b.x - a.x, b.y - a.y);
 4     Point2f ca = Point2f(a.x - c.x, a.y - c.y);
 5     Point2f pa = Point2f(a.x - p.x, a.y - p.y);
 6     double cross_ca_ab = ca.x * ab.y - ab.x * ca.y;
 7     double cross_pa_ab = pa.x * ab.y - ab.x * pa.y;
 8     double orientation = cross_ca_ab * cross_pa_ab;
 9     if (orientation < 0) return true;
10     Point2f bc = Point2f(c.x - b.x, c.y - b.y);
11     Point2f pb = Point2f(b.x - p.x, b.y - p.y);
12     double cross_ab_bc = ab.x * bc.y - bc.x * ab.y;
13     double cross_pb_bc = pb.x * bc.y - bc.x * pb.y;
14     orientation = cross_ab_bc * cross_pb_bc;
15     if (orientation < 0) return true;
16     Point2f pc = Point2f(c.x - p.x, c.y - p.y);
17     double cross_bc_ca = bc.x * ca.y - ca.x * bc.y;
18     double cross_pc_ca = pc.x * ca.y - ca.x * pc.y;
19     orientation = cross_bc_ca * cross_pc_ca;
20     if (orientation < 0) return true;
21     return false;
22 }

 

  求解的主要代碼如下:

double calcFourDuckInCirclePoolPolarCoordinates()
{
    int PIECE = 36000;
    int REGION = 100000;
    int ITER = 1000000;
    double PI = acos(-1);
    uniform_int_distribution<unsigned> theta_d(0, PIECE);
    uniform_int_distribution<unsigned> r_d(0, REGION);
    default_random_engine theta_e((unsigned int)time(NULL));
    default_random_engine r_e(0);
    vector<Point2f> pos;
    pos.resize(4);
    int candies = 0;
    Point2f center(0.f, 0.f);
    for (int i = 0; i < ITER; ++i)
    {
        for (int duck = 0; duck < 4; ++duck)
        {
            auto theta_ = theta_d(theta_e);
            auto r_ = r_d(r_e);
            double theta = ((double)theta_ / 100.0 - 180) / 180 * PI;
            double r = (double)r_*1.0 / REGION;
            double x = r * cos(theta);
            double y = r * sin(theta);
            pos[duck] = Point2f(x, y);
        }
        if (!beyondTriangle(pos[0], pos[1], pos[2], center)) continue;
        if (!beyondTriangle(pos[0], pos[1], pos[3], center)) continue;
        if (!beyondTriangle(pos[0], pos[2], pos[3], center)) continue;
        if (!beyondTriangle(pos[1], pos[2], pos[3], center)) continue;
        ++candies;
    }
    double probability = candies*1.0 / ITER;
    cout << "probability: " << fixed << setprecision(5) << candies*1.0 / ITER << endl;
    return probability;
}

 

  • 結果

  多次的采樣實驗結果顯示,通過統計模擬求解四只鴨子位於池塘同一個半圓的概率的答案為0.5!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM