Tại sao tôi có được mẫu màu đặc biệt này khi sử dụng rand ()?


170

Tôi đã cố gắng tạo một tập tin hình ảnh, như thế này:

uint8_t raw_r[pixel_width][pixel_height];
uint8_t raw_g[pixel_width][pixel_height];
uint8_t raw_b[pixel_width][pixel_height];
uint8_t blue(uint32_t x, uint32_t y)
{
    return (rand()%2)? (x+y)%rand() : ((x*y%1024)%rand())%2 ? (x-y)%rand() : rand();
}
uint8_t green(uint32_t x, uint32_t y)
{
    return (rand()%2)? (x-y)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
uint8_t red(uint32_t x, uint32_t y)
{
    return (rand()%2)? (y-x)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}

for (y=0; y<pixel_height; ++y)
{
    for (x=0; x<pixel_width; ++x)
    {
        raw_b[x][y]=blue(x, y);
        raw_g[x][y]=green(x, y);
        raw_r[x][y]=red(x, y);
    }
}

Tôi dự kiến ​​sẽ nhận được một cái gì đó ngẫu nhiên (tiếng ồn trắng). Tuy nhiên, đầu ra rất thú vị:

Bạn có biết lý do tại sao?


Biên tập

Bây giờ, rõ ràng là nó không có gì để làm với rand().

Cũng thử mã này:

for (x=0; x<pixel_width; ++x)
    for (y=0; y<pixel_height; ++y)
    {
        r[x][y] = (x+y);
        g[x][y] = (y-x);
        /* b[x][y] = rand()%2? x : y; */
    }


26
cos rand isnt rand - bản demo đẹp của nó. Nó có tính quyết định 100%
pm100


50
@ pm100: Vì câu trả lời của bames53 giải thích rất tốt, bạn sẽ có được mẫu tương tự ngay cả khi bạn sử dụng trình tạo số ngẫu nhiên hoàn hảo.
TonyK

13
Xin hỏi một câu hỏi: Vì bạn đang sử dụng tọa độ x và y để tính toán các giá trị pixel, tại sao bạn lại mong đợi các giá trị đó độc lập với tọa độ? Nếu hình ảnh trông quá ngẫu nhiên, đó là những gì bạn cần, phải không?
Thomas Padron-McCarthy

15
Bài học rút ra: Làm những điều ngẫu nhiên với số ngẫu nhiên không tạo ra kết quả ngẫu nhiên :)
Hagen von Eitzen

Câu trả lời:


355

Ban đầu tôi sẽ có câu trả lời giống như những người khác và đưa ra vấn đề này rand(). Tuy nhiên, tôi nghĩ tốt hơn khi làm như vậy và thay vào đó phân tích phân phối mà toán học của bạn thực sự tạo ra.

TL; DR: Mẫu bạn thấy không liên quan gì đến trình tạo số ngẫu nhiên cơ bản và thay vào đó chỉ đơn giản là do cách chương trình của bạn đang thao tác các số.

Tôi sẽ sử dụng chức năng màu xanh của bạn vì tất cả đều giống nhau.

uint8_t blue(uint32_t x, uint32_t y) {
    return (rand() % 2)                  ? (x + y) % rand() :
           ((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
                                           rand();
}

Mỗi giá trị điểm ảnh được lựa chọn từ một trong ba chức năng: (x + y) % rand(), (x - y) % rand(), và rand();

Chúng ta hãy nhìn vào hình ảnh được tạo ra bởi mỗi một mình.

  • rand()

Đây là những gì bạn mong đợi, chỉ là tiếng ồn. Gọi đây là "Hình ảnh C"

nhập mô tả hình ảnh ở đây


  • (x + y) % rand()

Ở đây bạn đang thêm các tọa độ pixel lại với nhau và lấy phần còn lại từ chia cho một số ngẫu nhiên. Nếu hình ảnh là 1024x1024 thì tổng số nằm trong phạm vi [0-2046]. Số ngẫu nhiên bạn đang lặn nằm trong phạm vi [0, RAND_MAX], trong đó RAND_MAX có ít nhất 32 nghìn và trên một số hệ thống là 2 tỷ. Nói cách khác, có ít nhất 1 trên 16 cơ hội rằng phần còn lại không chỉ (x + y). Vì vậy, đối với hầu hết các phần, chức năng này sẽ chỉ tạo ra một dải màu xanh lam tăng dần theo hướng + x + y.

Tuy nhiên, bạn chỉ sử dụng 8 bit thấp nhất, vì bạn trả về a uint8_t, do đó, bạn sẽ có các dải màu rộng 256 pixel.

Gọi đây là "Hình ảnh A"

nhập mô tả hình ảnh ở đây


  • (x - y) % rand()

Ở đây bạn làm một cái gì đó tương tự, nhưng với phép trừ. Miễn là x lớn hơn y, bạn sẽ có một cái gì đó tương tự như hình ảnh trước đó. Nhưng trong trường hợp y lớn hơn, kết quả là một con số rất lớn bởi vì xykhông dấu (kết quả âm tính bao quanh đỉnh của phạm vi không dấu), và sau đó các % rand()cú đá vào và bạn thực sự bị nhiễu.

Gọi đây là "Hình ảnh B"

nhập mô tả hình ảnh ở đây

Mỗi pixel trong hình ảnh cuối cùng của bạn được lấy từ một trong ba hình ảnh này bằng cách sử dụng các chức năng rand() % 2((x * y % 1024) % rand()) % 2. Đầu tiên trong số này có thể được đọc là lựa chọn với xác suất 50% (bỏ qua các vấn đề với rand()và các bit thứ tự thấp của nó.)

Đây là rand() % 2ảnh chụp gần đúng nơi (pixel trắng) để Image A được chọn.

nhập mô tả hình ảnh ở đây

Hàm thứ hai ((x * y % 1024) % rand()) % 2một lần nữa có vấn đề rand()thường lớn hơn thứ bạn đang chia, (x * y % 1024)nhiều nhất là 1023. Sau đó, (x*y%1024)%2không tạo ra 0 và 1 thường xuyên như nhau. Bất kỳ số lẻ nào nhân với bất kỳ số chẵn nào là số chẵn. Bất kỳ số chẵn nào nhân với bất kỳ số chẵn nào cũng là số chẵn. Chỉ một số lẻ nhân với một số lẻ là số lẻ, và cứ như vậy, %2các giá trị chẵn ba phần tư thời gian sẽ tạo ra 0 ba phần tư thời gian.

Đây là một cái nhìn cận cảnh về nơi ((x * y % 1024) % rand()) % 2là đúng để có thể chọn Image B. Nó chọn chính xác nơi cả hai tọa độ là số lẻ.

nhập mô tả hình ảnh ở đây

Và đây là một cái nhìn cận cảnh về nơi Image C có thể được chọn:

nhập mô tả hình ảnh ở đây

Cuối cùng kết hợp các điều kiện ở đây, nơi Hình ảnh B được chọn:

nhập mô tả hình ảnh ở đây

Và nơi Image C được chọn:

nhập mô tả hình ảnh ở đây

Sự kết hợp có thể được đọc là:

Với xác suất 50%, sử dụng pixel từ Ảnh A. Thời gian còn lại chọn giữa Ảnh B và Ảnh C, B trong đó cả hai tọa độ là số lẻ, C trong đó một trong hai là số chẵn.

Cuối cùng, vì bạn đang làm giống nhau cho ba màu khác nhau, nhưng với các hướng khác nhau, các mẫu được định hướng khác nhau trong mỗi màu và tạo ra các dải chéo hoặc mẫu lưới mà bạn nhìn thấy.


45

Nhiều tính toán mà bạn đang thực hiện trong mã của mình sẽ không dẫn đến các giá trị thực sự ngẫu nhiên. Những đường sắc nét mà bạn nhìn thấy tương ứng với các vị trí nơi các giá trị tương đối của tọa độ x và y của bạn giao dịch với nhau và khi điều đó xảy ra, bạn đang sử dụng các công thức khác nhau cơ bản. Ví dụ, máy tính (x + y) % rand()nói chung sẽ cung cấp cho bạn sao lưu các giá trị x + y, vì rand()ý chí (thường) trả về một số nhiều, lớn hơn nhiều so với x + ycho rằng RAND_MAXthường là một số lượng khá lớn. Theo nghĩa đó, bạn không nên mong đợi lấy lại nhiễu trắng, vì thuật toán bạn đang sử dụng để tạo ra mọi thứ bị lệch khỏi việc tạo ra nhiễu trắng. Nếu bạn muốn nhiễu trắng, chỉ cần đặt từng pixel thànhrand(). Nếu bạn thích một mẫu đẹp như mẫu bạn có ở trên, nhưng với một chút ngẫu nhiên được đưa vào đây và đó, hãy tiếp tục sử dụng mã mà bạn đã viết.

Ngoài ra, như @ pm100 đã lưu ý trong các nhận xét, randhàm không trả về các số thực sự ngẫu nhiên và thay vào đó sử dụng hàm giả ngẫu nhiên để tạo ra các giá trị. Việc triển khai mặc định randtrên nhiều hệ thống sử dụng một loại trình tạo số giả ngẫu nhiên được gọi là trình tạo cộng hưởng tuyến tính tạo ra các số mà trong các cụm ngắn có thể xuất hiện ngẫu nhiên, nhưng thực tế là không có giá trị trong thực tế. Ví dụ: đây là một hình ảnh động từ Wikipedia cho thấy các điểm ngẫu nhiên trong không gian được chọn với một trình tạo cộng hưởng tuyến tính cuối cùng rơi vào một số siêu phẳng cố định:

Bức hình

Nếu bạn thay thế x, y, z và tọa độ với R, G, và tọa độ B, ngoại hình này khá giống với sản lượng được sản xuất bởi chương trình của bạn. Tôi nghi ngờ rằng đây có lẽ không phải là vấn đề cốt lõi ở đây, vì khía cạnh khác được đề cập ở trên có lẽ sẽ rõ rệt hơn nhiều.

Nếu bạn đang tìm kiếm số ngẫu nhiên chất lượng cao hơn, bạn sẽ cần sử dụng nguồn ngẫu nhiên chất lượng cao hơn. Trong C, bạn có thể xem xét việc đọc byte từ /dev/urandom/(trên một hệ thống giống như Linux), cung cấp các giá trị ngẫu nhiên khá đồng đều. C ++ hiện có một số nguyên hàm tạo số ngẫu nhiên tốt trong các thư viện tiêu chuẩn của nó, nếu có sẵn cho bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.