简体   繁体   English

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

[英]How to efficiently generate only odd (or even) random numbers?

From looking at other questions regarding the generation of only even or odd numbers, I was curious as to what method would be the most efficient (in terms of speed). 通过查看关于生成奇数或偶数的其他问题,我很好奇哪种方法(在速度方面)最有效。

For example, let's say that I wanted to generate many odd numbers that range from 1 (inclusive) to 1000 (exclusive); 例如,假设我想生成许多奇数,范围从1(含)到1000(不含); for each iteration, the method I would normally use is the following: 对于每次迭代,我通常使用的方法如下:

  • Generate a random number between 0 (inclusive) and 500 (exclusive) 生成介于0(含)和500(不含)之间的随机数
  • Multiply by 2 乘以2
  • Add 1 1

Is there any better method? 有没有更好的方法?

I happened to stumble upon this method today and I can't find any use of it on StackOverflow for generating only even or odd numbers. 我今天偶然发现了这种方法,并且在StackOverflow上找不到用于生成偶数或奇数的任何用法。

This method takes advantage of the fact that, in binary, all even numbers (including 0 ) have their least-significant bit set to 0 whereas all odd numbers have their least-significant bit set to 1 . 该方法利用以下事实:二进制的所有偶数(包括0 )的最低有效位均设置为0而所有奇数的最低有效位均设置为1

Therefore, to generate an odd number, we can simply generate a random number within the desired range and bitwise-or it with 1 : 因此,要生成一个奇数,我们可以简单地生成一个期望范围内的随机数,然后按位或1生成一个随机数:

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

With this method, if the generator selects an odd number, then it is left alone. 使用这种方法,如果生成器选择了一个奇数,则将其保留下来。 However, if an even number is selected, then bitwise-or'ing it with 1 essentially increments it. 但是,如果选择了偶数,则将其按1进行按位或运算实质上会使其递增。

Likewise, to generate even numbers between 2 (inclusive) and 1000 (exclusive), then we just need to clear the least-significant bit. 同样,要生成介于2 (含)和1000 (不含)之间的偶数,则只需要清除最低有效位即可。 To do this, we can simply bitwise-and the value with -2 : 为此,我们可以简单地按位运算-然后用-2表示值:

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

With this method, if the generator selects an even number, then it is left alone. 使用这种方法,如果生成器选择一个偶数,则它会被保留。 However, if an odd number is selected, then bitwise-and'ing it with -2 essentially decrements it. 但是,如果选择了奇数,则按位加“ -2实际上会使它递减。

These methods work perfectly fine with negative values, and long as well. 这些方法的工作负值完美的罚款,并long为好。

Here is a JMH benchmark comparing the two methods for generating an odd number between 1 (inclusive) and 1000 (exclusive): 这是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;
    }
}

And the results: 结果:

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 can be improved slightly by using << 1 instead of * 2 , but newMethod is still slightly faster. oldMethod可以稍微通过使用能够提高<< 1代替* 2 ,但newMethod仍然是稍快。

Your 2n + 1 approach is good. 您的2n + 1方法很好。 You can use streams and wrap it up in a helper method for ease of use if you like: 如果愿意,您可以使用流并将其包装在帮助程序方法中,以便于使用:

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

Usage: 用法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM