一道數學題的思考

  最近看見一道數學題,比較有意思:

房間裏有100個人,每人都有100元錢,他們在玩一個遊戲。每輪遊戲中,每個人都要拿出一元錢隨機給另一個人,最後這100個人的財富分佈是怎樣的?我們不妨把這場遊戲視作社會財富分配的簡化模型,從而模擬這個世界的運行規律。我們假設:每個人在18歲帶着100元的初始資金開始玩遊戲,每天玩一次,一直玩到65歲退休。“每天拿出一元錢”可理解爲基本的日常消費,“獲得財富的概率隨機”是爲了……嗯……簡化模型。以此計算,人一生要玩17000次遊戲,即獲得17000次財富分配的機會。答案有如下三種情況:

這裏寫圖片描述

  當看見這道題的時候,我認爲財富的分佈應該是接近均勻分佈,畢竟概率是均等的。但是答案竟然是接近冥律分佈,爲什麼呢?我決定使用代碼來計算一遍!

  使用代碼計算,首先考慮一個問題,就是隨機數,目前 Random 函數的隨機數存在“僞隨機”,就是每次的隨機數都是一樣的,我們可以通過代碼來測試一下:

import java.util.Random;

public class MyClass {
    public static void main(String[] args) {

        for (int i = 0; i < 3; i++) {
            System.out.println( "第"+i+"次輸出結果"+new Random(5).nextInt(100));
        }

    }
}

接下來看看日誌結果,大家就明白了!

第0次輸出結果87
第1次輸出結果87
第2次輸出結果87
第3次輸出結果87
第4次輸出結果87
第5次輸出結果87
第6次輸出結果87
第7次輸出結果87
第8次輸出結果87
第9次輸出結果87

避免隨機數的方法比較多,有減掉種子參數()、將 Random 對象生成一個對象,每次使用同一個對象,代碼驗證一下:

import java.util.Random;

public class MyClass {
    public static void main(String[] args) {
        Random rl = new Random();
        for (int i = 0; i < 10; i++) {
            System.out.println( "無種子參數第"+i+"次輸出結果"+ new Random().nextInt(10));
            System.out.println( "同 Random 對象第"+i+"次輸出結果"+ rl.nextInt(10));
        }

    }
}

日誌驗證結果:

無種子參數第0次輸出結果4Random 對象第0次輸出結果6
無種子參數第1次輸出結果0Random 對象第1次輸出結果1
無種子參數第2次輸出結果8Random 對象第2次輸出結果8
無種子參數第3次輸出結果4Random 對象第3次輸出結果4
無種子參數第4次輸出結果1Random 對象第4次輸出結果8
無種子參數第5次輸出結果7Random 對象第5次輸出結果6
無種子參數第6次輸出結果8Random 對象第6次輸出結果4
無種子參數第7次輸出結果8Random 對象第7次輸出結果2
無種子參數第8次輸出結果7Random 對象第8次輸出結果8
無種子參數第9次輸出結果0Random 對象第9次輸出結果1

上述的“僞函數”的原因是種子參數的使用——

在進行隨機時,隨機算法的起源數字稱爲種子數(seed),在種子數的基礎上進行一定的變換,從而產生需要的隨機數字。因此,相同種子數的Random對象,相同次數生成的隨機數字是完全相同的。

  瞭解了Random對象以後,接下來需要實現財富分配的過程,上面說的是規則不大清晰,只說了每個人的初始資金爲100元,每次中獎了就分配,然後總次數爲17000次。我們在實現的過程中需要考慮的問題還有幾點:

  1. for 循環的時候,是 100 個人每次抽獎 17000 次,還是抽 17000 次獎,每次遍歷 100 個人?
  2. 當每個人的資金減少到0元以後是否還有機會中獎?
  3. 當有人的資金減少爲 0 時,可分配的獎金響應減少,如何紀錄?
  4. 當問題 1 爲否定時,會發生無人中獎的情況,這個時候已經出資1元的資金需要取回,且總次數是否增加?

  第一、雖然總的計算次數都是1700000次,但是這裏的邏輯不一樣,前者抽獎次數達到了1700000次,後者抽獎次數固定爲17000次,按照邏輯判斷,應該選後者。

  第二、我將這個問題理解爲博弈,並非作者所提的社會問題(社會問題需要考慮到財富平均的財富再分配,博弈考慮的是我付出了有多大的收益),因此第一個問題我的答案是沒有機會中獎,這樣也會更加公平!

  第三、我的做法是在每輪博弈前設置一個獎金初始值 bonus,假設每次一開始都不知道有人需要退出,因此初始值一直是100元,接下來在每次收取博弈獎金的時候判斷參與者是否還有資金(資金是否爲 0 或者爲 -1)參與本輪博弈,沒有的話我會給他們一個標記(資金設爲 -1),並且將獎金 bonus 減去 1 。當 100 人循環完畢以後,博弈獎金就是 bonus 了!

  第四、該問題類似第二個問題,在每輪博弈前設置一個標記 isBouns ,定義爲無人中獎。然後在得到 bonus 以後,就可以開獎了。如果遇到無人中獎(假設中獎號爲 99 ,但是 99 號沒有投注博弈資金了,則本輪無人中獎),這個時候可以通過判斷 isBouns 來決定總次數是否增加。由於總次數 17000 次是來自時間的計算,所以這裏我也沒有增加總次數了。如果需要增加,我們可以對總次數定義一個變量 22K (可以自行百度22k 這個故事),初始值爲 17000 ,在無人中獎的時候加 1 即可。

代碼如下:

import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Created by κ?? on 2017/7/30.
 */

public class Test {
    static List<Integer> data = new ArrayList<>();
    public static void main(String []args) {
        Random rl = new Random();
        int count = 100;
        int all = 17000;


        for (int i = 0; i < 100; i++) {
            data.add(100);
        }
        int ran = rl.nextInt(100);
        for (int i = 0; i < 17000; i++) {
            boolean isBouns = false;
            int bonus = 100;
            for (int j = 0; j < 100; j++) {

                if(data.get(j) == 0){
                    data.set(j,-1);
                    bonus--;
                }else if(data.get(j) != -1){
                    int value = data.get(j);
                    data.set(j,value-1);
                }else{
                    bonus--;
                }

            }
            if(data.get(ran)!=-1){
                int value = data.get(ran);
                data.set(ran,value+bonus);
                isBouns = true;

            }

            ran = rl.nextInt(100);
            if(!isBouns){
                for (int j = 0; j < 100; j++) {
                    int value = data.get(j);
                    if(value!=-1){
                        data.set(j,value+1);
                    }

                }
            }



        }


        System.out.println(getData());
        //數據詳情
        System.out.println(new Gson().toJson(data));
    }

    private static int getData() {
        int result = 0;
        int count = 0;
        int winer = 0;
        for (int i = 0; i < 100; i++) {
            if(data.get(i) != -1 && data.get(i) != 0){
                result+=data.get(i);
                count++;
                //絕對贏家
                if(data.get(i)>100){
                    winer++;
                }
            }
        }
        System.out.println("共有"+winer+"人贏了!其中有"+count+"還有錢!");
        //總資金 10000,檢查這個值計算是否有計算出錯
        return result;
    }
}

  最後說一下我的計算結果:

  代碼運行10 次,累計208人還有錢,其中7人不足100元,也就是說平均每輪(幾十年的概念)有20.8%的概率還有能力繼續博弈,每輪有20.1%的概率盈利!


參考文章:

http://blog.csdn.net/hla199106/article/details/45030041


文章來源

https://media.weibo.cn/article?id=2309404134038146271148&from=timeline&jumpfrom=weibocom

發佈了61 篇原創文章 · 獲贊 28 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章