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次方,电脑运行几乎不行,如果以后有机会,看看能不能得到一个快速降维的办法,或者说可以寻找到每个柱状图之间差距的公式,如果能得到,那么就很快能够绘制出相应的统计结果。
谢谢阅读。

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