【问题标题】:One Random instance per thread, seeded by the original Random instance to attain reproducibility每个线程一个 Random 实例,由原始 Random 实例播种以获得可重复性
【发布时间】:2017-01-08 07:10:56
【问题描述】:

我需要进行1000 计算,每个计算都基于一个随机数。 整个运行需要可重现

在单线程环境中,我只是根据种子创建随机数并将其用于每次计算:

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

在多线程环境中,我有10 线程,我将有一个种子随机种子线程 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
...

这是个好主意吗? 总的来说,它仍然是一个均匀分布的随机数吗?每个threadRandom 都被initRandom 播种,不破坏均匀分布吗?

出于可重现的原因,我不能共享 1 个全局随机数,因为某些线程在某些运行中可能比其他线程运行得更快,因此它们不会总是以相同的顺序调用全局随机数(争用可能会导致性能下降)。

【问题讨论】:

  • Random 生成均匀分布的数字,所以答案是:是的。
  • 这是一个合理的想法。如果您共享一个随机数,它们是同步的,这使得在不同线程中使用相同的随机数变得困难。您甚至可以使用除 List&lt;Calculation&gt; 之外的其他东西,而是使用包含 List 并且还包含 Random 的自定义类。
  • @Andreas 所以线程都使用由原始 Random 中的随机数播种的 Random 的事实不会破坏均匀分布的保证?
  • Random 是用特定值(硬编码或来自其他地方)播种的事实,并不会改变 Random 从那时起生成均匀分布的数字的事实。如果种子 Random 没有做到这一点,那么对于单线程环境,您甚至无法做到这一点,现在可以吗?
  • Geoffrey,请看stackoverflow.com/questions/28991299/… 和内部讨论,它可能会给你一些见解...

标签: java random distribution uniform-distribution


【解决方案1】:

您将通过 Random 获得均匀的数字分布,但是无法保证列表中每个 Calculation 对象列表的运行顺序,因此如果这些将结果传递给共享列表,则顺序可能会随着运行而变化。

【讨论】:

  • 列表保留顺序,为什么您认为它们的顺序没有保证?
  • Executor 服务无法保证提交的 Runnables 将以什么顺序运行。
  • 因为它们是相互独立运行的线程?当然;当他们只是在某处“发布”他们的结果时,该结果集中的顺序可以完全是……随机的。
  • 我第一次没有很好地解析你的句子。您的编辑有助于使其更清晰。当然执行顺序可能会改变,但与特定计算关联的Random 不会改变。
  • 没错,我们只能推测没有完整的实现。
【解决方案2】:

特别是关于均匀分布,每 10 个像素有 1 个随机位置似乎没问题:

哦,等等,每 1 个像素有 1 个随机位置,种子线程方法似乎更好!?在完美的均匀分布中(尺寸为 250000),它将是全黑的:

左:

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

}

对:

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

}

【讨论】:

  • 又和REPRODUCIBLE的问题有什么关系?
  • 这只是回答了问题的一部分,如果它仍然是均匀分布的随机(我删除了上面代码中的线程复杂性,但添加线程不会产生影响)。对此我还是有疑问的。我已经接受了另一个答案,因为它回答了大部分问题。
猜你喜欢
  • 2014-07-21
  • 2017-08-26
  • 2011-01-11
  • 1970-01-01
  • 2011-03-09
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 2020-03-16
相关资源
最近更新 更多