如何有效地生成奇数(或偶数)随机数?

时间:2018-05-30 20:42:45

标签: java optimization random

通过查看有关生成偶数或奇数的其他问题,我很好奇哪种方法最有效(就速度而言)。

例如,让我们说我想生成许多奇数,范围从1(含)到1000(不包括);对于每次迭代,我通常使用的方法如下:

  • 生成0(包括)和500(不包括)之间的随机数
  • 乘以2
  • 添加1

有没有更好的方法?

2 个答案:

答案 0 :(得分:4)

我今天碰巧偶然发现了这种方法,我无法在StackOverflow上找到任何用于生成偶数或奇数的方法。

此方法利用以下事实:在二进制中,所有偶数(包括0)的最低有效位设置为0,而所有奇数都设置其最低有效位到1

因此,为了生成一个奇数,我们可以简单地在所需范围内生成一个随机数,并按位生成1

ThreadLocalRandom.current().nextInt(0, 1_000) | 1

使用这种方法,如果发生器选择一个奇数,那么它将保持不变。但是,如果选择偶数,则使用1进行按位或“按”基本上会增加它。

同样,要在2(包括)和1000(不包括)之间生成偶数,我们只需要清除最不重要的位。要做到这一点,我们可以简单地按位 - 和值-2

ThreadLocalRandom.current().nextInt(2, 1_000) & -2

使用这种方法,如果生成器选择偶数,那么它将保持不变。但是,如果选择了一个奇数,那么按{和-2进行逐位递减会使它基本递减。

这些方法与负值完美配合,long也适用。

这是一个JMH基准,比较了在1(包括)和1000之间生成奇数的两种方法(不包括):

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 20, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(5)
public class MyBenchmark {

    private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();

    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }

    @Benchmark
    public int oldMethod() {
        return RANDOM.nextInt(0, 500) * 2 + 1;
    }

    @Benchmark
    public int newMethod() {
        return RANDOM.nextInt(0, 1000) | 1;
    }
}

结果:

Benchmark              Mode  Cnt  Score   Error  Units
MyBenchmark.newMethod  avgt  100  6.079 ± 0.137  ns/op
MyBenchmark.oldMethod  avgt  100  6.325 ± 0.009  ns/op
使用oldMethod代替<< 1可以略微改善

* 2,但newMethod仍然稍快一些。

答案 1 :(得分:1)

您的2n + 1方法很好。如果您愿意,可以使用流并将其包装在辅助方法中以方便使用:

import java.util.Random;

public static IntStream randomOdds(int from, int upTo) {
    return new Random().ints(from/2, upTo/2).map(n -> 2*n + 1);
}

用法:

randomOdds(1, 1000).limit(20).forEach(System.out::println);