เสียงสุ่มบนพื้นฐานของเมล็ด


16

ฉันกำลังทำงานกับโปรแกรมที่ควรสร้างสัญญาณรบกวนแบบสุ่มบนหน้าจอโดยยึดตาม 'พิกัด' ของพิกเซล พิกัดควรมีสีเดียวกันทุกครั้งที่คุณเริ่มโปรแกรมใหม่ อย่างไรก็ตามการใช้ประโยชน์ของ Java การสุ่มผลลัพธ์ที่ฉันได้รับนั้นไม่ได้สุ่มอย่างที่ฉันต้องการ:

บันทึกหน้าจอ, พิมพ์หน้าจอ

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

นี่คือรหัสที่ฉันใช้:

public class Generate {

static Random Random;

    public static int TileColor(int x, int y){          
        Random = new Random(Integer.valueOf(Integer.toString(x)+Integer.toString(y)));
        int b = 1 + Random.nextInt(50);
        int g = 1 + Random.nextInt(50);
        int r = 1 + Random.nextInt(50);
        int color = -Color.rgb888(r, g, b);
        return color;
    }
}

รูปแบบที่โปรแกรมสร้างขึ้นเนื่องจากฟังก์ชั่น Random ของ java ทำงานได้หรือฉันกำลังทำสิ่งผิดปกติและฉันควรลองวิธีอื่นหรือไม่?

อัปเดต: ฉันพยายามกำจัดปัญหาที่เกิดขึ้นกับการต่อข้อมูลโดยใช้รหัสต่อไปนี้:

public static int TileColor(int x, int y){  
            Randomy = new Random(y);
            Randomx = new Random(x);
            Random = new Random(Integer.valueOf(Integer.toString(Randomx.nextInt(1234))+Integer.toString(Randomy.nextInt(1234))));
            int b = 1 + Random.nextInt(100);
            int g = 1 + Random.nextInt(100);
            int r = 1 + Random.nextInt(100);
            int color = -Color.rgb888(r, g, b);
            return color;
}

อย่างใดนี้ยังให้ภาพสุ่ม (ในความคิดของฉัน) เพียงพอ:

ภาพ mew

อย่างไรก็ตามรหัสนี้มีขนาดต่อสามครั้งต่อพิกเซล แม้ว่าตอนนี้จะไม่เป็นปัญหาสำหรับฉันในตอนนี้ฉันจะพิจารณาเปลี่ยนรหัสนี้ในกรณีที่ฉันต้องการ preformance ที่ดีกว่าในภายหลัง


3
ไม่แน่ใจเกี่ยวกับการสุ่มของ Java แต่ฉันค่อนข้างแน่ใจว่ามันไม่สุ่มจริง ... อ่านen.wikipedia.org/wiki/Pseudorandom_number_generator คุณจะเข้าใจว่าทำไมคุณถึงเห็นรูปแบบเหล่านั้น
Salketer

23
สิ่งสำคัญที่ขาดหายไปจากคำตอบอื่น ๆ : อย่าส่งข้อมูล RNG ใหม่สำหรับทุกพิกเซล เริ่มต้นทันทีและสร้างค่าที่ต่อเนื่องกันสำหรับพิกเซลทั้งหมดในภาพของคุณ
Konrad Rudolph

4
หมายเหตุ: ตัวเลขทั่วไปปลอมอาจกระจายอย่างสม่ำเสมอในมิติเดียวแต่ล้มเหลวเมื่อใช้มากกว่าหนึ่งมิติ ... คุณกำลังสร้างจุดใน 3D (r, g และ b และ 3 พิกัดที่แตกต่างกัน) ดังนั้นคุณต้องมีตัวสร้างแบบสุ่ม รับประกันไม่เพียง แต่ค่าที่สร้างขึ้นจะมีการกระจายอย่างสม่ำเสมอ แต่ยังรวมถึงสามส่วนที่สร้างขึ้นอย่างสม่ำเสมอในพื้นที่ 3D
Bakuriu

6
@Bakuriu ถ้า X, Y และ Z เป็นตัวแปรสุ่มแบบอิสระฉันก็ค่อนข้างแน่ใจ (X, Y, Z) นั้นเหมือนกันในอวกาศ 3 มิติ
Jack M

2
คุณสามารถทดลองกับการใช้ RNGs ที่แตกต่างกันเช่นMersenne Twister
เควิน

คำตอบ:


21

ของ Java java.util.Randomระดับมักจะช่วยให้คุณมีลำดับของตัวเลข pseudorandom ที่ดีพอสำหรับการใช้งานในเกมที่1 อย่างไรก็ตามคุณลักษณะนั้นจะใช้กับลำดับของตัวอย่างจำนวนมากที่ยึดตามเมล็ดเท่านั้น เมื่อคุณกำหนดค่าเริ่มต้น RNG ใหม่ด้วยการเพิ่มค่าเมล็ดและดูเฉพาะค่าแรกของแต่ละลำดับลักษณะการสุ่มจะไม่ดีเท่า

คุณสามารถทำอะไรแทน:

  • ใช้เมล็ดเดียวกันเพื่อสร้างพิกเซลทั้งหมดในเวลา ตัวอย่างเช่นเมื่อคุณต้องการค่าสีของพิกเซล 425: 487 ป้อนพิกัด 400: 400 ลงใน RNG ให้สร้าง 10000 สีแบบสุ่มและใช้สีที่ดัชนี 2587 (25 * 100 + 87) ควรสร้างแคชในลักษณะนั้นเพื่อหลีกเลี่ยงการสร้างสีแบบสุ่มใหม่ 10,000 สีสำหรับแต่ละพิกเซลของอันนั้น
  • แทนที่จะใช้ตัวสร้างตัวเลขสุ่มให้ใช้ฟังก์ชันสรุปข้อความเพื่อเปลี่ยนคู่พิกัดให้เป็นค่าสี การส่งออกของ MDF ส่วนใหญ่นั้นไม่สามารถคาดการณ์ได้เพียงพอที่จะทดสอบการสุ่มได้มากที่สุด โดยทั่วไปแล้วเอาต์พุตจะมากกว่า 24 บิตที่คุณต้องการสำหรับค่า RGB แต่การตัดทอนนั้นมักจะไม่มีปัญหา

    เพื่อปรับปรุงประสิทธิภาพคุณสามารถรวมการสร้างข้อความสรุปเข้ากับชิ้น สร้างพิกเซลขนาดเล็กซึ่งมีขนาดใหญ่พอที่จะใช้ความยาวเต็มหนึ่งเอาต์พุตของฟังก์ชันย่อยของคุณ

1 เมื่อจำเป็นอย่างยิ่งที่ไม่มีใครสามารถคาดเดาหมายเลขถัดไปได้ให้ใช้ค่าที่ช้าลง แต่คาดเดาได้น้อยลงjava.security.SecureRandom


13

พิกัดควรมีสีเดียวกันทุกคนที่คุณเริ่มโปรแกรมใหม่

ในกรณีนี้คุณจะต้องใช้ฟังก์ชั่นเสียงรบกวนที่กำหนดขึ้นเช่นเสียง Perlinหรือเสียงรบกวนแบบง่าย

( ดูคำถามนี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสัญญาณรบกวน Perlin พร้อมภาพสวย ๆ )

ส่วนใหญ่แล้วการใช้random()ฟังก์ชั่นในตัวหรือที่คล้ายกันจะให้คุณค่าที่แตกต่างกันทุกครั้งที่คุณเรียกใช้โปรแกรมเนื่องจากอาจใช้นาฬิกาเป็นอินพุตหรือค่า pseudorandom อื่น ๆ

อีกทางเลือกหนึ่งคือการสร้าง "แผนที่เสียง" หนึ่งครั้งออฟไลน์จากนั้นใช้เป็นแหล่งที่มาของหมายเลขสุ่มของคุณในภายหลัง

ในการดำเนินการของคุณคุณจะเชื่อมโยงการเป็นตัวแทนสตริงของ x yและ นั่นเป็นสิ่งที่ไม่ดีเพราะไม่ซ้ำกันทั่วทั้งโดเมน ตัวอย่างเช่น

x    y   concatenated
40   20  4020
402   0  4020
10   10  1010
101   0  1010
12   34  1234
123   4  1234
1   234  1234

โชคดี!


1
จุดที่ดีเกี่ยวกับตัวเลขที่ตัดแบ่ง อย่างไรก็ตามโปรแกรมจะให้ผลลัพธ์เหมือนกันทุกครั้งหากฉันใช้งานโปรแกรมหลายครั้ง ฉันได้พิจารณาเสียงเพอร์ลิน / ซิมเพล็กซ์แล้วเช่นกันอาจมีลักษณะดังกล่าวและดูว่าทำงานได้ดี อย่างไรก็ตามฉันยังไม่แน่ใจว่าทำไม Java สร้างรูปแบบสำหรับปัญหาการเรียงต่อกันดูเหมือนจะไม่สามารถแก้ไขได้ทั้งหมด
dragonfly

1
มันไม่เพียงพอที่จะทำการสุ่มเลือกด้วยค่าคงที่ของเมล็ดก่อนที่จะสร้างพิกเซลหรือไม่?
Jack M

1
@ Jackm ขึ้นอยู่กับอัลกอริทึม PRNG ทั้งหมดในการเล่น
3Dave

4
"ฉันเคยเห็นการใช้งานแรนด์ () ที่ใช้แต่ละค่าเป็นเมล็ดสำหรับค่าถัดไป" นั่นไม่ใช่วิธีที่เครื่องกำเนิดตัวเลขเทียมที่ไม่ใช่การเข้ารหัสส่วนใหญ่ทำงานอย่างไร พวกเขาใช้หมายเลขสุ่มก่อนหน้า (หรือรัฐ) เป็นอินพุตเพื่อสร้างหมายเลข / สถานะสุ่มถัดไป
JAB

2
@DavidLively แทบทุก PRNGs ทำสิ่งนี้หรือสิ่งที่เทียบเท่าเว้นแต่สถานะภายในของพวกเขาจะใหญ่กว่าช่วงของตัวเลขที่พวกเขาสร้าง (เช่น Mersenne twisters) และจากนั้นลำดับของการสุ่มตัวเลขแน่นอนโดยสิ้นเชิงจากเมล็ด
Konrad Rudolph

9

มาดูกันว่าคุณกำลังทำอะไรกันแน่:

  • คุณวนซ้ำพิกเซลทั้งหมดทีละหนึ่ง
  • สำหรับแต่ละพิกเซลคุณใช้การต่อข้อมูลพิกัดเป็นเมล็ด
  • จากนั้นคุณเริ่มสุ่มใหม่จากเมล็ดที่กำหนดและนำหมายเลข 3 ออกมา

ทั้งหมดนี้ฟังดูโอเค แต่คุณได้รับลวดลายเพราะ:

พิกเซลที่ 1,11 และพิกเซลที่ 11,1 ทั้งคู่มีหมายเลข 111 ดังนั้นจึงมั่นใจได้ว่าจะมีสีเดียวกัน

นอกจากนี้ตราบใดที่คุณวนรอบด้วยวิธีเดียวกันคุณสามารถใช้เครื่องกำเนิดไฟฟ้าเพียงเครื่องเดียวโดยไม่จำเป็นต้องใช้เครื่องกำเนิดสัญญาณเดียวสำหรับแต่ละพิกเซล หนึ่งภาพรวมจะทำ! ยังคงมีรูปแบบบางอย่างเนื่องจากการสุ่มหลอก @David_Lively ถูกต้องเกี่ยวกับการใช้อัลกอริธึม Noise มันจะทำให้ดูสุ่มมากขึ้น


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

4
อันที่จริง“ทั้งหมดนี้” ไม่ได้เสียงไม่เป็นไร - reseeding RNG กำหนดทุกพิกเซลเป็นกลยุทธ์ที่น่ากลัวเว้นแต่เมล็ดตัวเองจาก RNG การเข้ารหัสลับ (และแม้แล้วมันเป็นกลยุทธ์ที่ wonky สำหรับเหตุผลที่ไม่เกี่ยวข้องกับการจัดจำหน่าย)
คอนราดรูดอล์ฟ

คุณอาจรวมวิธีที่ถูกต้องในการเชื่อมตัวเลขในบริบทนี้ กล่าวคือ (x + y ที่กว้าง *)
Taemyr

1

ทำเครื่องกำเนิดสีแล้วผลิตสีของคุณสำหรับกระเบื้องของคุณ เมล็ดพันธุ์เพียงครั้งเดียว! คุณไม่จำเป็นต้องเพาะเมล็ดมากกว่านั้นอย่างน้อยต่อแผ่น

public class RandomColorGenerator {
  private final int minValue;
  private final int range;
  private final Random random;
  public RandomColorGenerator(int minValue, int maxValue, Random random) {
    if (minValue > maxValue || (long)maxValue - (long)minValue > (long)Integer.MAX_VALUE) {
      throw new IllegalArgumentException();
    }
    this.minValue = minValue;
    this.range = maxValue - minValue + 1;
    this.random = Objects.requireNonNull(random);
  }

  public int nextColor() {
    int r = minValue + random.nextInt(range);
    int g = minValue + random.nextInt(range);
    int b = minValue + random.nextInt(range);
    return -Color.rgb888(r, g, b);
  }
}

public class Tile {
  private final int[][] colors;
  public Tile(int width, int height, RandomColorGenerator colorGenerator) {
    this.colors = new int[width][height];
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        this.colors[x][y] = colorGenerator.nextColor();
      }
    }
  }

  public int getColor(int x, int y) {
    return colors[x][y];
  }
}

และการใช้งานจะเป็นดังนี้:

RandomColorGenerator generator = new RandomColorGenerator(1, 100, new Random(0xcafebabe));
Tile tile = new Tile(300, 200, generator);
...
// getting the color for x, y:
tile.getColor(x, y);

ด้วยวิธีนี้หากคุณไม่พอใจกับผลลัพธ์เพียงแค่เปลี่ยนRandomเมล็ด นอกจากนี้คุณจะต้องจัดเก็บ / สื่อสารเมล็ดพันธุ์และขนาดเพื่อให้ลูกค้าทั้งหมดมีภาพเดียวกัน


1

แทนที่จะใช้การสุ่มพิจารณาการใช้แฮชสรุปย่อยเช่น MD5 มันมีค่าที่ยากต่อการคาดการณ์ 'สุ่ม' ตามอินพุตที่แน่นอน แต่มักจะเป็นค่าเดียวกันสำหรับอินพุตเดียวกันเสมอ

ตัวอย่าง:

public static int TileColor(int x, int y){
        final MessageDigest md = MessageDigest.getInstance("MD5");
        final ByteBuffer b = ByteBuffer.allocate(8);
        b.putInt(x).putInt(y);
        final byte[] digest = md.digest(b.array());
        return -Color.rgb888(digest[0], digest[1], digest[2]);
}

หมายเหตุ: ฉันไม่รู้ว่า Color.rgb888 (.. ) มาจากไหนฉันจึงไม่รู้ว่าช่วงที่อนุญาตคืออะไร 0-255 เป็นเรื่องปกติ

การปรับปรุงที่ต้องพิจารณา:

  • สร้างตัวแปร MessageDigest และ ByteBuffer นอกคลาสเพื่อปรับปรุงประสิทธิภาพ คุณจะต้องรีเซ็ต ByteBuffer เพื่อทำเช่นนั้นและวิธีการจะไม่ปลอดภัยต่อเธรดอีกต่อไป
  • อาเรย์ย่อยจะมีค่าไบต์ 0-255 หากคุณต้องการช่วงอื่นคุณต้องทำคณิตศาสตร์กับมัน
  • หากคุณต้องการผลลัพธ์ 'สุ่ม' ที่แตกต่างกันคุณสามารถเพิ่ม 'เมล็ดพันธุ์' บางชนิดได้ ตัวอย่างเช่นเปลี่ยนเป็น ByteBuffer.allocate (12) และเพิ่ม .putInt (seed)

1

คนอื่น ๆ ได้ชี้ให้เห็นว่าวิธีหนึ่งที่จะทำให้พฤติกรรมที่คุณต้องการคือการใช้ฟังก์ชันแฮชหรือที่เรียกว่า "ฟังก์ชันการแยกย่อยข้อความ" ปัญหาคือว่าสิ่งเหล่านี้มักจะขึ้นอยู่กับอัลกอริทึมเช่น MD5 ซึ่งมีความปลอดภัย cryptographically (เช่นจริงๆจริงๆสุ่มจริงๆ) แต่ช้ามาก หากคุณใช้ฟังก์ชันแฮชการเข้ารหัสทุกครั้งที่คุณต้องการพิกเซลแบบสุ่มคุณจะพบกับปัญหาประสิทธิภาพการทำงานที่ค่อนข้างรุนแรง

อย่างไรก็ตามมีฟังก์ชันแฮชที่ไม่ใช่การเข้ารหัสที่สามารถสร้างค่าที่สุ่มพอสำหรับวัตถุประสงค์ของคุณในขณะที่ยังรวดเร็ว หนึ่งฉันมักจะเข้าถึงสำหรับเป็นmurmurhash ฉันไม่ใช่ผู้ใช้ Java แต่ดูเหมือนว่ามีการใช้ Javaอย่างน้อยหนึ่งรายการ หากคุณพบว่าคุณต้องการให้แต่ละพิกเซลถูกสร้างขึ้นจากพิกัดของมันแทนที่จะสร้างพวกเขาทั้งหมดในครั้งเดียวและเก็บไว้ในพื้นผิวแล้วนี่จะเป็นวิธีที่ดีในการทำ


1

ฉันจะใช้ตัวเลือกที่มีค่ามากกว่า 2000 (ความละเอียดทั่วไปสูงสุด)
วิธีนี้จะลดขนาด (หรือกำจัดเมล็ดซ้ำ)

public class Generate {

    static Random Random;

    public static int TileColor(int x, int y){          
        Random = new Random(x + 2213 * y);
        int b = 1 + Random.nextInt(50);
        int g = 1 + Random.nextInt(50);
        int r = 1 + Random.nextInt(50);
        int color = -Color.rgb888(r, g, b);
        return color;
    }
}

0

Randomสุ่มพอ คุณใช้ผิดเพราะสองสาเหตุหลัก

  • มันไม่ได้ถูกออกแบบมาให้ใส่ซ้ำซ้ำ คุณสมบัติการสุ่มเก็บไว้สำหรับตัวเลขสุ่มลำดับเดียวเท่านั้น
  • มีความสัมพันธ์กันอย่างมาก Integer.valueOf(Integer.toString(x)+Integer.toString(y))ระหว่างพิกเซลที่คุณกำลังเพาะกับ

ฉันแค่ใช้รูปแบบของรหัสต่อไปนี้ซึ่งคุณสามารถเลือกฟังก์ชันแฮช (อย่าใช้ Integer.getHashCode) จากคำตอบที่/programming/9624963/java-simplest-integer- กัญชา

public static int TileColor(int x, int y) {
    return hash(x ^ hash(y));
}

ฟังก์ชันแฮชอาจอยู่ที่ใด


0

คุณสามารถลองใช้เวลานาฬิกาปัจจุบันของระบบเป็นเมล็ดพันธุ์เช่นนี้:

Random random = new Random(System.currentTimeMillis())

หวังว่ามันจะสร้างค่าสุ่มมากขึ้น


ที่ไม่ได้สร้างค่า dame สำหรับพิกัด dame ทุกครั้งอย่างไรก็ตาม
dragonfly

0

นี่คือฟังก์ชั่น Shader แบบคงที่หนึ่งบรรทัดที่ฉันคิดขึ้นมา - poltergeist (Noisy Ghost)

ต้องใช้พิกัด 2D และเมล็ดและแสดงผลแบบโมโนโทนตามที่ร้องขอ มันทำงานแบบเรียลไทม์เฟรมต่อวินาทีโดยไม่คำนึงถึงความละเอียดหน้าจอ นี่คือสิ่งที่ GPUs มีไว้สำหรับ

// poltergeist (noisy ghost) pseudo-random noise generator function
// dominic.cerisano@standard3d.com 03/24/2015

precision highp float;

float poltergeist(in vec2 coordinate, in float seed) 
{
    return fract(sin(dot(coordinate*seed, vec2(12.9898, 78.233)))*43758.5453); 
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) 
{   
    fragColor = vec4(poltergeist(fragCoord, iGlobalTime)); 
}

ความละเอียดใด ๆ พื้นผิวใด ๆ บนอุปกรณ์ใด ๆ (มือถือด้วย) รองรับ GL (ซึ่งค่อนข้างมากกับหน้าจอ)

เห็นมันทำงานที่นี่ตอนนี้!

https://www.shadertoy.com/view/ltB3zD

คุณสามารถรวมส่วนนี้ในโปรแกรมจาวาของคุณได้อย่างง่ายดายโดยใช้ OpenGL มาตรฐานหรือในเบราว์เซอร์ใด ๆ ที่ใช้ webgl มาตรฐาน

เพื่อความสนุกฉันโยนถุงมือเพื่อให้ทุกคนเอาชนะ Poltergeist ด้วยคุณภาพและประสิทธิภาพในทุกอุปกรณ์ กฎผีที่มีเสียงดัง! พ่ายแพ้!

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