ทำไมฉันถึงได้รับสีลวดลายนี้เมื่อใช้แรนด์ ()


170

ฉันพยายามสร้างไฟล์รูปภาพเช่นนี้

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);
    }
}

ฉันคาดว่าจะได้รับสิ่งที่สุ่ม (เสียงสีขาว) อย่างไรก็ตามผลลัพธ์น่าสนใจ:

คุณรู้เหตุผลหรือไม่


แก้ไข

ตอนนี้มันชัดเจนว่ามันไม่มีอะไรเกี่ยวข้องกับ rand()ตอนนี้ก็เป็นที่ชัดเจนว่ามันมีอะไรจะทำอย่างไรกับ

ลองใช้รหัสนี้:

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 ไม่ใช่แรนด์ - เป็นตัวอย่างที่ดีของมัน มันกำหนดได้ 100%
pm100


50
@ pm100: ตามคำตอบของ bames53 อธิบายได้ดีคุณจะได้รับรูปแบบเดียวกันแม้ว่าคุณจะใช้ตัวสร้างตัวเลขสุ่มที่สมบูรณ์แบบ
TonyK

13
แก้ตัวคำถาม: เนื่องจากคุณใช้พิกัด x และ y เพื่อคำนวณค่าพิกเซลทำไมคุณถึงคิดว่าค่าเหล่านั้นเป็นอิสระจากพิกัด หากภาพดูสุ่มเกินไปก็เป็นสิ่งที่คุณต้องการใช่ไหม?
Thomas Padron-McCarthy

15
บทเรียนที่ได้รับ: การทำสิ่งสุ่มกับตัวเลขสุ่มไม่ได้ผลลัพธ์แบบสุ่ม :)
Hagen von Eitzen

คำตอบ:


355

ผมได้รับในตอนแรกจะมีคำตอบเช่นเดียวกับคนอื่น ๆ rand()ได้และชอล์กขึ้นนี้ปัญหาที่มี อย่างไรก็ตามฉันคิดว่าควรทำเช่นนั้นและวิเคราะห์การกระจายของคณิตศาสตร์ของคุณแทน

TL; DR: รูปแบบที่คุณเห็นไม่มีส่วนเกี่ยวข้องกับตัวสร้างตัวเลขแบบสุ่มและแทนที่จะเป็นเพราะวิธีการที่โปรแกรมของคุณจัดการกับตัวเลข

ฉันจะใช้ฟังก์ชันสีน้ำเงินของคุณเพราะมันคล้ายกันหมด

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

ค่าพิกเซลแต่ละคนจะเลือกจากหนึ่งในสามของฟังก์ชั่น: (x + y) % rand(), (x - y) % rand()และrand();

ลองดูที่ภาพที่ผลิตโดยสิ่งเหล่านี้เพียงอย่างเดียว

  • rand()

นี่คือสิ่งที่คุณคาดหวังเพียงแค่เสียงรบกวน เรียกสิ่งนี้ว่า "Image C"

ป้อนคำอธิบายรูปภาพที่นี่


  • (x + y) % rand()

ที่นี่คุณกำลังเพิ่มพิกัดพิกเซลพร้อมกันและดึงส่วนที่เหลือจากการหารด้วยตัวเลขสุ่ม หากภาพเป็น 1024x1024 แสดงว่าผลรวมอยู่ในช่วง [0-2046] ตัวเลขสุ่มที่คุณดำน้ำนั้นอยู่ในช่วง [0, RAND_MAX] โดยที่ RAND_MAX อย่างน้อย 32k และในบางระบบคือ 2 พันล้าน กล่าวอีกนัยหนึ่งมีโอกาส 1 ใน 16 ที่ดีที่สุดที่เหลือไม่ใช่แค่(x + y)โอกาสที่เหลือไม่ได้เป็นเพียง ดังนั้นส่วนใหญ่ฟังก์ชั่นนี้จะสร้างการไล่ระดับสีฟ้าที่เพิ่มขึ้นไปยังทิศทาง + x + y

อย่างไรก็ตามคุณจะใช้บิตต่ำสุดเพียง 8 เพราะคุณส่งคืน uint8_tดังนั้นคุณจึงมีแถบกว้างไล่ระดับสี 256 พิกเซล

เรียกสิ่งนี้ว่า "Image A"

ป้อนคำอธิบายรูปภาพที่นี่


  • (x - y) % rand()

ที่นี่คุณทำสิ่งที่คล้ายกัน แต่มีการลบ ตราบใดที่ x มากกว่า y คุณจะมีบางอย่างคล้ายกับภาพก่อนหน้า แต่ถ้า y มากขึ้นผลลัพธ์จะเป็นจำนวนมากเพราะxและyไม่ได้ลงนาม (ผลลัพธ์เชิงลบจะถูกล้อมรอบด้านบนของช่วงของประเภทที่ไม่ได้ลงชื่อ) จากนั้นการ% rand()เตะเข้าและคุณจะได้รับเสียงรบกวน

เรียกสิ่งนี้ว่า "Image B"

ป้อนคำอธิบายรูปภาพที่นี่

แต่ละพิกเซลในภาพสุดท้ายของคุณนั้นมาจากหนึ่งในสามภาพนี้โดยใช้ฟังก์ชั่นrand() % 2และ((x * y % 1024) % rand()) % 2และครั้งแรกของสิ่งเหล่านี้สามารถอ่านได้ตามที่เลือกด้วยความน่าจะเป็น 50% (ละเว้นปัญหากับrand()และบิตลำดับต่ำ)

นี่คือระยะใกล้ที่rand() % 2เป็นจริง (พิกเซลสีขาว) เพื่อเลือก Image A

ป้อนคำอธิบายรูปภาพที่นี่

ฟังก์ชั่นที่สอง((x * y % 1024) % rand()) % 2อีกครั้งมีปัญหาที่rand()มักจะมากกว่าสิ่งที่คุณหาร(x * y % 1024)ซึ่งมากที่สุดคือ 1,023 แล้ว(x*y%1024)%2ไม่ผลิต 0 และ 1 เท่ากัน จำนวนคี่ใด ๆ คูณด้วยจำนวนคู่ใด ๆ คือเลขคู่ จำนวนคู่ใด ๆ ที่คูณด้วยจำนวนคู่ใด ๆ ก็จะเท่ากับ เฉพาะจำนวนคี่คูณด้วยจำนวนคี่เป็นคี่และอื่น ๆ%2ค่าที่แม้แต่สามในสี่ของเวลาจะผลิต 0 สามในสี่ของเวลา

นี่คือระยะใกล้ของที่((x * y % 1024) % rand()) % 2เป็นจริงเพื่อให้สามารถเลือกรูปภาพ B มันกำลังเลือกตำแหน่งที่พิกัดทั้งคู่แปลก

ป้อนคำอธิบายรูปภาพที่นี่

และนี่คือภาพโคลสอัพที่สามารถเลือก Image C:

ป้อนคำอธิบายรูปภาพที่นี่

ในที่สุดก็รวมเงื่อนไขที่นี่คือที่เลือกภาพ B:

ป้อนคำอธิบายรูปภาพที่นี่

และตำแหน่งที่เลือกรูปภาพ C:

ป้อนคำอธิบายรูปภาพที่นี่

การรวมกันที่เกิดขึ้นสามารถอ่านได้เป็น:

ด้วยความน่าจะเป็น 50% ให้ใช้พิกเซลจาก Image A เวลาที่เหลืออยู่ระหว่าง Image B และ Image C, B ซึ่งทั้งสองพิกัดนั้นแปลก, C โดยที่ทั้งคู่เป็นคู่

ในที่สุดเนื่องจากคุณทำแบบเดียวกันกับสามสีที่แตกต่างกัน แต่ด้วยการวางแนวที่แตกต่างกันรูปแบบจะเน้นไปที่แต่ละสีแตกต่างกันออกไป


45

การคำนวณจำนวนมากที่คุณทำในรหัสของคุณจะไม่นำไปสู่ค่าสุ่มอย่างแท้จริง เส้นที่คมชัดเหล่านั้นที่คุณเห็นสอดคล้องกับสถานที่ที่ค่าสัมพัทธ์ของ x และ y ของคุณทำการค้าขายกับคนอื่นและเมื่อสิ่งนั้นเกิดขึ้นคุณกำลังใช้สูตรที่แตกต่างกันโดยพื้นฐาน ตัวอย่างเช่นการคำนวณ(x + y) % rand()โดยทั่วไปจะให้ค่ากลับมาx + yเนื่องจากrand()จะ (คืนค่าปกติ) คืนค่าจำนวนมากยิ่งใหญ่กว่ามาก หากคุณต้องการรูปแบบที่สวยงามเหมือนรูปแบบที่คุณมีด้านบน แต่ด้วยความสุ่มเล็กน้อยที่นี่และที่นั่นให้ใช้รหัสที่คุณเขียนต่อไปx + yกำหนดซึ่งRAND_MAXโดยปกติจะเป็นจำนวนมากพอสมควร ในแง่นี้คุณไม่ควรคาดหวังว่าจะได้เสียงรบกวนสีขาวคืนเนื่องจากอัลกอริทึมที่คุณใช้ในการสร้างสิ่งต่าง ๆ นั้นมีความลำเอียงจากการสร้างเสียงสีขาว หากคุณต้องการเสียงสีขาวเพียงตั้งค่าแต่ละพิกเซลเป็นrand()

นอกจากนี้เนื่องจาก @ pm100 ได้ระบุไว้ในความคิดเห็นแล้วrandฟังก์ชันจะไม่ส่งกลับตัวเลขสุ่มอย่างแท้จริงและใช้ฟังก์ชันหลอกเทียมแทนเพื่อสร้างค่า การใช้งานเริ่มต้นของrandระบบจำนวนมากใช้ตัวสร้างตัวเลขหลอกเทียมที่เรียกว่าเครื่องกำเนิดเชิงเส้นเชิงเส้นตรงที่ผลิตตัวเลขที่การปะทุระยะสั้นสามารถปรากฏแบบสุ่ม แต่ในทางปฏิบัติแล้ว ตัวอย่างเช่นต่อไปนี้เป็นภาพเคลื่อนไหวจาก Wikipedia แสดงให้เห็นว่าคะแนนสุ่มในอวกาศที่เลือกด้วยเครื่องกำเนิดไฟฟ้าเชิงเส้นเชิงเส้นจบลงที่จำนวนไฮเปอร์เพลนจำนวนคงที่:

รูปภาพ

หากคุณแทนที่พิกัด x, y และ z ด้วยพิกัด R, G และ B สิ่งนี้จะดูคล้ายกับเอาต์พุตที่โปรแกรมของคุณสร้างขึ้นอย่างน่าทึ่ง ฉันสงสัยว่านี่อาจไม่ใช่ประเด็นหลักที่นี่เนื่องจากประเด็นอื่น ๆ ที่กล่าวถึงข้างต้นอาจมีความเด่นชัดมากกว่านี้

หากคุณกำลังมองหาตัวเลขสุ่มที่มีคุณภาพสูงกว่าคุณจะต้องใช้แหล่งข้อมูลสุ่มที่มีคุณภาพสูงกว่า ใน C คุณสามารถพิจารณาอ่านไบต์จาก/dev/urandom/(บนระบบที่คล้ายกับ Linux) ซึ่งให้ค่าแบบสุ่มค่อนข้างสม่ำเสมอ ตอนนี้ C ++ มีการสร้างหมายเลขสุ่มที่ดีจำนวนหนึ่งในไลบรารีมาตรฐานหากคุณมีให้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.