Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One Random instance per thread, seeded by the original Random instance to attain reproducibility

I need to do 1000 calculations, each one based on a random number. The entire run needs to be reproducible.

In a single threaded environment, I just create random based on a seed and use that for each calculation:

Random random = new Random(37);
for (Calculation c : calculations) {
    c.doCalculation(r.nextInt());
}

In a multi threaded enviromnent, where I have 10 threads, I 'd have a seeded random seed the thread Random's:

Random initRandom = new Random(37);
for (List<Calculation> p : calculationPartitions) {
    final Random threadRandom = new Random(initRandom.nextLong());
    executorService.submit(() -> {
        for (Calculation c : p) {
            c.doCalculation(threadRandom.nextInt());
        }
    });
}
// Merge the calculation results back in the same order as the calculationPartitions
...

Is this a good idea? Is it still an evenly distributed random in general? The fact that each threadRandom is seeded by the initRandom, does it not break the uniform distribution?

For reproducible reasons, I cannot share 1 global random, as some threads might run faster than others in some runs, so they would not always call the global random in the same order (and the contention could cause performance degradation).

like image 296
Geoffrey De Smet Avatar asked Dec 07 '25 03:12

Geoffrey De Smet


2 Answers

You will get an even distribution of numbers via Random, however the order that the each list of Calculation objects in your list will run is not guaranteed so if these then pass the result to a shared list the order may vary over runs.

like image 78
UserF40 Avatar answered Dec 08 '25 17:12

UserF40


Specifically about the uniform distribution, it seems to be ok with 1 random location per 10 pixels:

distribution

Oh wait, with 1 random location per 1 pixel, the seeded threaded approach even seems better!? In a perfect uniform distribution (for size 250000) it would be all black:

enter image description here

Left:

public class SingleRandomProof extends JFrame {

    public static final int WIDTH = 500;
    public static final int HEIGHT = 500;
    public static final int SIZE = WIDTH * HEIGHT;

    public static void main(String[] args) {
        SingleRandomProof proof = new SingleRandomProof();
        proof.pack();
        proof.setVisible(true);
        proof.doCalc();
    }

    private JLabel panel;

    public SingleRandomProof() throws HeadlessException {
        super("1 random");
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        panel = new JLabel(new ImageIcon(image));
        setContentPane(panel);
    }

    private void doCalc() {
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.BLACK);
        Random r = new Random(37);
        for (int i = 0; i < SIZE; i++) {
            int position = r.nextInt(SIZE);
            g.fillRect(position % HEIGHT, position / HEIGHT, 1, 1);
        }
        panel.setIcon(new ImageIcon(image));
    }

}

Right:

public class SeededThreadRandomProof extends JFrame {

    public static final int WIDTH = 500;
    public static final int HEIGHT = 500;
    public static final int SIZE = WIDTH * HEIGHT;

    public static void main(String[] args) {
        SeededThreadRandomProof proof = new SeededThreadRandomProof();
        proof.pack();
        proof.setVisible(true);
        proof.doCalc();
    }

    private JLabel panel;

    public SeededThreadRandomProof() throws HeadlessException {
        super("10 seeded randoms");
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        panel = new JLabel(new ImageIcon(image));
        setContentPane(panel);
    }

    private void doCalc() {
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.BLACK);
        Random initRandom = new Random(37);
        for (int j = 0; j < 10; j++) {
            Random r = new Random(initRandom.nextLong());
            for (int i = 0; i < SIZE / 10; i++) {
                int position = r.nextInt(SIZE);
                g.fillRect(position % HEIGHT, position / HEIGHT, 1, 1);
            }
        }
        panel.setIcon(new ImageIcon(image));
    }

}
like image 41
Geoffrey De Smet Avatar answered Dec 08 '25 16:12

Geoffrey De Smet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!