Use a linguagem C para verificar se eventos discretos com probabilidade não uniforme estão em conformidade com a curva de distribuição normal quando o tamanho da amostra é grande o suficiente (gerando uma imagem no formato PPM)

A razão pela qual eu quero escrever este artigo é ver [Official Bilingual] publicado pela famosa conta de popularização da matemática 3Blue1Brown Mas o que é o teorema do limite central? É mencionado em: Independentemente de a probabilidade de várias situações desse evento discreto ser média ou não, quando o número for certamente grande, ele ainda estará de acordo com a curva de distribuição normal. Eu só queria tentar para ver se esse era o caso, porque acho que o teorema do limite central e a distribuição normal são uma parte incrível da teoria da probabilidade.

Este trabalho usa os pontos do dado como um evento discreto, e encontra a probabilidade da soma dos pontos. Primeiro, realize o programa no estado de distribuição uniforme e, em seguida, ajuste a probabilidade de distribuição desigual. O código-fonte completo é colocado no final para evitar erros causados ​​por problemas como arquivos de cabeçalho.

Sob distribuição uniforme, a probabilidade da soma dos pontos

Primeiramente, crie um novo array para armazenar os pontos do dado, conforme segue:

int a[] = {
    
    1,2,3,4,5,6};

Para gerar uma imagem use a seguinte função writePPMImage:

void writePPMImage(int* data, int width, int height, const char *filename, int maxIterations)
{
    
    
    FILE *fp = fopen(filename, "wb");

    // write ppm header
    fprintf(fp, "P6\n");
    fprintf(fp, "%d %d\n", width, height);
    fprintf(fp, "255\n");

    for (int i = 0; i < width*height; ++i) {
    
    
        float mapped = pow( std::min(static_cast<float>(maxIterations), static_cast<float>(data[i])) / 256.f, .5f);
        unsigned char result = static_cast<unsigned char>(255.f * mapped);
        for (int j = 0; j < 3; ++j)
            fputc(result, fp);
    }
    fclose(fp);
    printf("Wrote image file %s\n", filename);
}

Os parâmetros desta função:

  1. dataÉ um array, em que cada elemento corresponde à informação de cor de cada pixel do bitmap (arranjo Z), ou seja, existe um valor na soma dos pontos correspondentes a um elemento (ou pixel) .
  2. widthe heightsão as dimensões do bitmap resultante.
  3. filenameé o arquivo bitmap gerado.
  4. maxIterationsé o valor máximo da cor, ou seja, o valor correspondente ao branco, aqui definimos como 256cor de 8 canais no código. Precisamos apenas de preto e branco, então pode ser mais conciso, escrito diretamente 1, e então usar apenas 0e 1dois valores inteiros para representar preto e branco.

Escreva o código diretamente abaixo, veja os comentários para a introdução de cada etapa:

int main() {
    
    
	//设置图片尺寸为1450x1000
    int width = 1450;
    int height = 1000;
    
    //待会需要随机从中选择一个元素,当作骰子的点数
    int a[] = {
    
    1,2,3,4,5,6};
    
    //用来存放各种点数之和的数量多数组,这里不要声明空数组,因为一些编译器会给没有值的元素分配一些很奇怪的值,导致运行错误(不像C语言是默认为0)
    int* sumArr = new int[width];
    //用来存放最后输出图像的像素色彩信息的数组
    int* output = new int[width*height];
	
	//样本量为30x1000=30000,也就是取3万次点数之和
    for (int i=0; i<height*30; i++) {
    
    
    	//获取到一个随机点数。模6表示随机值范围是0~5,刚好对应前面数组a的每个元素
        int temp = a[random()%6];
        //下面的循环将会累加100次,也就是表示多少个骰子点数之和
        for (int j=0; j<100; j++) {
    
    
            temp = temp + a[random()%6];
	        }
	        //给这个值对应的sumArr的元素加1
	        sumArr[temp] = sumArr[temp]+1;
	    }
	    
	    //因为输出图像的时候,条状图是从底部开始的,所以写这样的一个转换
	    for (int i=0; i<width; i++) {
    
    
	        for (int j=height-1; j>=height-sumArr[i]; j--) {
    
    
	            output[j*width+i]=256;
	        }
	    }
		//输出图像
	    writePPMImage(output, width, height, "output.ppm", 256);
	
	    delete[] sumArr;
	    delete[] output;
	    return 0;
	}

As imagens correspondentes às 30.000 amostras geradas são as seguintes:

Adicione uma descrição da imagem

É muito semelhante à curva de distribuição normal, mas é muito acentuada. Para torná-la mais óbvia, vamos "esticá-la e achatá-la". forA maneira de fazer isso é modificar o segundo loop grande da seguinte maneira:

for (int i=0; i<width; i++) {
    
    
        // sumArr[i]/2是为了压缩图像
        for (int j=height-1; j>=height-sumArr[i]/2; j--) {
    
    
            //拉宽图像
            for (int k=0; k<10; k++) {
    
    
                output[j*width+i*10+k]=256;
        }
    }
}

Ou seja, torna-se 2x10 pixels para representar uma amostra, e as imagens abaixo são todas exibidas de acordo com essa escala . Neste momento a imagem é a seguinte:

Adicione uma descrição da imagem

Neste momento, é muito semelhante à imagem da distribuição normal padrão. Se você realmente deseja obter a imagem da distribuição normal padrão, adicione a parte do cálculo da variância e da média amostral, apenas mais algumas etapas.

Probabilidade de soma de pontos sob distribuição não uniforme

Em seguida, vamos tentar um gráfico de probabilidades de distribuição não uniforme. Isso foi difícil para mim no começo. Eu não sabia como tornar a probabilidade de cada valor diferente, mas rapidamente percebi que isso é apenas pegar pequenas bolas (elementos) em uma caixa (array). Depois modifique os elementos do array. Quantidade e valor são suficientes, então o array de espaço amostral neste momento é:

int a[] = {
    
    1,1,1,1,1,2,3,4,5,6};

1Existem cinco, o que significa que 1a probabilidade é de 0,5 e o restante é de 0,1.

Neste momento, o código-fonte também precisa ser modificado, não apenas porque o número de elementos mudou, mas também o intervalo de valores aleatórios, e também é necessário considerar várias situações de teste e torná-lo mais geral, por isso é modificado no seguinte estilo:

int main() {
    
    
    int width = 1700;
    int height = 1000;
    int a[] = {
    
    1,1,1,1,1,2,3,4,5,6};
    //count用来统计样本空间的大小,这样就不用手动去下面依次修改了
    int count = sizeof(a)/sizeof(int);
    
    
    int* sumArr = new int[width];
    int* output = new int[width*height];

    for (int i=0; i<height*30; i++) {
    
    
        int temp = a[random()%count];
        for (int j=0; j<100; j++) {
    
    
            temp = temp + a[random()%count];
        }
        sumArr[temp] = sumArr[temp]+1;
    }
    
    for (int i=0; i<width; i++) {
    
    
        // sumArr[i]/2是为了压缩图像
        for (int j=height-1; j>=height-sumArr[i]/2; j--) {
    
    
            //拉宽图像
            for (int k=0; k<10; k++) {
    
    
                output[j*width+i*10+k]=256;
            }
        }
    }

    writePPMImage(output, width, height, "output.ppm", 256);

    delete[] sumArr;
    delete[] output;
    return 0;
}

A imagem gerada neste momento é a seguinte:

Adicione uma descrição da imagem

Pode-se ver que ainda está em conformidade com a curva de distribuição normal e 1a imagem não muda devido à alta probabilidade.

Que tal um pouco mais extremo? E se 1a probabilidade for tão alta quanto 99%?

Infelizmente, para tornar 1a probabilidade tão alta quanto 99%, a matriz do espaço amostral precisa ter 500 elementos, o que causará alguns erros de alocação de recursos. Basta tentar o 1caso em que a probabilidade é de 95%, então a matriz é a seguinte (a matriz está listado aqui Para a conveniência dos leitores, você pode copiá-lo e experimentá-lo você mesmo):

int a[] = {
    
    
       	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    //40个
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    //40个
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    //40个
        2,3,4,5,6,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,                          //15个1
    };

Neste momento, a imagem é a seguinte:

Adicione uma descrição da imagem

Pode-se ver que 1+1=2as amostras do valor mínimo são as maiores, mas o lado direito ainda é metade da distribuição normal, e daí se o número de acumulações for aumentado? Por exemplo, de 100 vezes a 1000 vezes (o número de amostras é reduzido para 10.000 vezes), a imagem neste momento é a seguinte:

Adicione uma descrição da imagem

Como existem muitas possibilidades, o tamanho da imagem aqui é 17000x1000px, o que é um pouco confuso, então cortei parte da imagem:

imagem cortada

Pode-se ver que no final ainda está de acordo com a curva de distribuição normal, que é exatamente o teorema do limite central.

código completo

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <algorithm>

using namespace std;

void
writePPMImage(int* data, int width, int height, const char *filename, int maxIterations)
{
    
    
    FILE *fp = fopen(filename, "wb");

    // write ppm header
    fprintf(fp, "P6\n");
    fprintf(fp, "%d %d\n", width, height);
    fprintf(fp, "255\n");

    for (int i = 0; i < width*height; ++i) {
    
    
        float mapped = pow( std::min(static_cast<float>(maxIterations), static_cast<float>(data[i])) / 256.f, .5f);
        unsigned char result = static_cast<unsigned char>(255.f * mapped);
        for (int j = 0; j < 3; ++j)
            fputc(result, fp);
    }
    fclose(fp);
    printf("Wrote image file %s\n", filename);
}

int main() {
    
    
	//输出图像的尺寸
	//图像会随着累加次数右移,所以增加累加次数的时候要把输出图像的宽度扩大一些
    int width = 17000;
    int height = 1000;
    int a[] = {
    
    
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    //40个
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    //40个
        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    //40个
        2,3,4,5,6,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,                          //15个
    };
    //count用来统计样本空间的大小,这样就不用手动去下面依次修改了
    int count = sizeof(a)/sizeof(int);
    
    //用来存放各种点数之和的数量多数组,这里不要声明空数组,因为一些编译器会给没有值的元素分配一些很奇怪的值,导致运行错误(不像C语言是默认为0)
    int* sumArr = new int[width];
    //用来存放最后输出图像的像素色彩信息的数组
    int* output = new int[width*height];
    
	//样本量为10x1000=10000,也就是取1万次点数之和
    for (int i=0; i<height*10; i++) {
    
    
    	//获取到一个随机点数。模6表示随机值范围是0~count,刚好对应前面数组a的每个元素
        int temp = a[random()%count];
        //下面的循环将会累加1000次,也就是表示多少个骰子点数之和
        for (int j=0; j<1000; j++) {
    
    
            temp = temp + a[random()%count];
        }
        sumArr[temp] = sumArr[temp]+1;
    }
    
    //因为输出图像的时候,条状图是从底部开始的,所以写这样的一个转换
    for (int i=0; i<width; i++) {
    
    
        // sumArr[i]/2是为了压缩图像
        for (int j=height-1; j>=height-sumArr[i]/2; j--) {
    
    
            //拉宽图像
            for (int k=0; k<10; k++) {
    
    
                output[j*width+i*10+k]=256;
            }
        }
    }

	//输出图像
    writePPMImage(output, width, height, "mandelbrot-serial.ppm", 256);

    delete[] sumArr;
    delete[] output;
    return 0;
}

É muito interessante, espero que possa ajudar alguém que precise~

Acho que você gosta

Origin blog.csdn.net/qq_33919450/article/details/130706860
Recomendado
Clasificación