python小練習之多維疊加和統計分佈(一)

寫在開頭:昨天晚上一位朋友突然問了我一個問題,想了許久終不得果,後來又想了許久想到了一個解決辦法,但我猜測應該還有更好的辦法來進行解決。

多維累加和統計分佈

這個名字是筆者根據任務要求臨時想的一個,下面我們首先來描述一下任務內容,
任務目標
計算對一個數組,放回隨機抽取n次,輸出n次抽取的和的所有可能,已經統計所有和的出現次數與概率。
舉個小例子,我們對從0,1,2中抽取兩次,來統計前後兩次和的所有可能已經結果,下面我們畫一個圖。我們可以看到兩次抽取後的所有結果分別是0到4,
在這裏插入圖片描述
然後得出每個結果的出現次數,
在這裏插入圖片描述
這就是我們的任務,當對某個數組抽取n次時,n次結果和的統計結果是怎麼樣的呢?
任務自述
這個問題因爲筆者不清楚是否有數學歸納式,但是如果僅從程序的角度出發,會有以下兩個需要思考的問題:
1.一般可以使用for循環,但當抽取n次的時候,意味着需要n個for循環,這無疑是無意義的,那麼該如何替換for循環?
2.如果我們選擇先將n次抽取的結果先保存,然後再相加,那麼我們需要面臨高緯度的數組存儲問題,可以使用樹來解決,但創建的複雜度比較高。那麼如何簡單的儲存數據也是一個問題。
下面筆者簡單的講述一下本任務的研究思路,
首先針對第一個問題如果要對多個for循環進行更換,我們可以使用numpy進行矩陣運算,或者誰用遞歸進行重複操作的循環運算,我們不妨將所抽取到的數組的所有可能進行展示,我們以對(0,1,2)這三個數進行抽取爲例,抽取三次的結果如下,
在這裏插入圖片描述
我們可以發現將所有的抽取結果展現出來後,呈現了明顯的規律性,我們可以看到每次所有可能的第一行,可以按照n個0,n個1,n個2這樣排列,具體的長度就是原數組長度的n次方。然後第一排的規律找到了,那麼後面幾排的規律呢?
從紅色和綠色來看,上一次抽取的所有可能將會被整理爲下一次抽取所有結果的非首行結果,如圖所示,第一次的結果分別出現在第二次的012下面,同理。這樣我們可以使用一個遞歸的程序將上一次的數據用到下一次的生成中,這樣就能夠得到完整的抽取結果。
針對第二個問題,如果我們是先將所有的結果保存下來,那麼我們可以看到,我們所需的結構框架是n×n3n\times n^{3}的增長,這樣的結構很不合理。爲了解決這樣一個問題,我們結合我們的目標,是爲了求各種結果的和,於是我們直接計算每次抽取結果的和作爲迭代對象,
在這裏插入圖片描述
這樣我們每次只需要計算,一排0,1,2和上一次抽取的和的加法運算,中間過程不需要考慮構建數組,也不需要過分考慮數字排列,只需要設置好遞歸的次數,就可以得到我們想要的結果,然後再統計每一種加和出現的次數。任務get!下面我們來用程序開始實現,程序的大部分運算來時numpy,但有些地方因爲對numpy不熟練所以可能操作比較繁瑣,以後會來改進的。


任務實現
我們這裏對[0,1,2,3,4,5]中抽取4次,抽取的結果的和的統計分佈進行展示,

import numpy as np
import copy



# 統計抽取累積和函數定義
def sumStatic(choiceList, k, n):
    """
        描述:該函數用於統計對m個數字,放回隨機抽樣抽取n次,每次的值相加得到的和的所有可能結果,
              以及可能結果出現的次數和概率的統計。
        輸入:
            chiceList:輸入的是一個np.array,是一個m維的行向量。我們將從choiceList中抽取數值進行加和計算;
            k:循環控制變量,控制函數遞歸的次數,初始值爲0,每遞歸一次k要增加1;
            n:抽取次數,通過手動設定想要抽取多少次。
        返回:
            y:返回的是對於各種和的統計次數,並調用到下一個函數中,進行可視化的展示
    """
    z = np.array([])  # 創建空np.array數組,方便結尾進行儲存
    store = []        # 創建空list,儲存每一步的計算結果
    k += 1
    for i in range(d.shape[0]):
        lists = copy.deepcopy(choiceList)  # 對輸入數組進行深拷貝
        lists += d[i]                      # 對每個數組進行加和
        store.append(lists)                # 添加到list中
    z = np.hstack(store)                   # 將list處理爲一個行np.array
    if k == (n - 1):                    # 如果遞歸次數到了n-1次,那麼返回所有和的結果出現次數統計到可視化函數中
        return distribution(np.bincount(z))
    return sumStatic(z, k, n)             # 如果沒有到達既定的抽取次數,那麼繼續遞歸


# 統計次數可視化
def distribution(z):
    number = np.argwhere(z)
    #sum = np.sum(z)
    for i in range(number.shape[0]):
        print(number[i], "=====>", z[i],"次")  # 輸出每一個和的結果出現的概率
		#如果想計算概率可以把sum和下面這個語句輸出
		#print(number[i], "=====>", round(z[i], 4))

n = int(input("[請輸入你想抽取的次數]> "))
d = np.array([0, 1, 2, 3, 4, 5])
sumStatic(d, 0, n)
[請輸入你想抽取的次數]> 4
[0] =====> 1 次
[1] =====> 4 次
[2] =====> 10 次
[3] =====> 20 次
[4] =====> 35 次
[5] =====> 56 次
[6] =====> 80 次
[7] =====> 104 次
[8] =====> 125 次
[9] =====> 140 次
[10] =====> 146 次
[11] =====> 140 次
[12] =====> 125 次
[13] =====> 104 次
[14] =====> 80 次
[15] =====> 56 次
[16] =====> 35 次
[17] =====> 20 次
[18] =====> 10 次
[19] =====> 4 次
[20] =====> 1 次

然後有些朋友可能想繪圖,這個就比較簡單了,只需要對number和z進行繪圖即可,我們可以對distribution進行重構,

def distribution(z):
    x = np.argwhere(z)
    X = np.reshape(x,(1,-1))[0]
    plt.bar(X,z,color="#ffcfdc")

同樣調用可以得到,我們的統計圖了。
在這裏插入圖片描述


結語
到此,我們結束了對於這種累加和的統計分佈的實現,但該程序還存在問題,比如當抽樣400次的時候,理論上的維度就是5的400次方,電腦運行幾乎不行,如果以後有機會,看看能不能得到一個快速降維的辦法,或者說可以尋找到每個柱狀圖之間差距的公式,如果能得到,那麼就很快能夠繪製出相應的統計結果。
謝謝閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章