如何生成随机值,其中百分比为0?

时间:2017-11-03 18:52:01

标签: java random java-8

我创建了一个随机流

Random random = new Random();
Stream<Integer> boxed = random.ints(0, 100000000).boxed();

但是我需要将60%的数字生成为0,而剩下的数字可能是真正随机的。我该怎么办?

编辑:

我只需要正数且在0-100之间

1
2
0
0
9
0
0
1
12

8 个答案:

答案 0 :(得分:16)

我假设OP希望大约60%的生成值为零,其余大约40%为(伪)随机值,范围为1-100,包括在内。

JDK库可以轻松生成N个不同值的流。 由于在[1,100]范围内有100个值,并且这代表输出的40%,因此需要150个值映射到零以覆盖剩余的60%。因此N是250。

我们可以在愤怒[0,249](包括)中创建一个int流,并将此范围内的最低150个值映射到零,剩余的值在[1,100]范围内。这是代码:

    IntStream is = random.ints(0, 250)
                         .map(i -> Math.max(i-149, 0));

<强>更新

如果任务是生成完全 60%的零,那么可以使用Knuth,TAOCP Vol 2,sec 3.4.2,随机抽样和随机抽样中的算法变体来实现,算法S.(我在this other answer中更详细地解释了这个算法。)这个算法允许从 N 的集合中随机选择 n 元素。总元素,对集合进行一次传递。

在这种情况下,我们不会从集合中选择元素。相反,我们发出已知数量的数字,要求它们的一些子集为零,其余为来自某个范围的随机数。基本思想是,当您发出数字时,发出零的概率取决于剩余要发射的零的数量与剩余要发射的数量的数量。由于这是一个固定大小的流,并且它有一些状态,我选择使用Spliterator实现它:

static IntStream randomWithPercentZero(int count, double pctZero, int range) {
    return StreamSupport.intStream(
        new Spliterators.AbstractIntSpliterator(count, Spliterator.SIZED) {
            int remainingInts = count;
            int remainingZeroes = (int)Math.round(count * pctZero);
            Random random = new Random();

            @Override
            public boolean tryAdvance(IntConsumer action) {
                if (remainingInts == 0)
                    return false;

                if (random.nextDouble() < (double)remainingZeroes / remainingInts--) {
                    remainingZeroes--;
                    action.accept(0);
                } else {
                    action.accept(random.nextInt(range) + 1);
                }
                return true;
            }
        },
        false);
}

有一些模板,但你可以在tryAdvance内看到算法的核心。如果没有剩余数字,则返回false,表示流的结束。否则,它以一定的概率(从60%开始)发出一个数字,它是零,否则是所需范围内的随机数。随着更多零点的发射,分子将降至零。如果已经发射了足够的零,则分数变为零并且不再发出零。

如果发射的零很少,则分母会下降,直到它接近分子,从而增加发射零的概率。如果发射的零值很少,则最终所需的零数量等于剩余的数量,因此分数的值变为1.0。如果发生这种情况,则流的其余部分为零,因此将始终发出足够的零以满足要求。关于这种方法的好处是没有必要收集数组中的所有数字并将它们混洗,或类似的东西。

调用这样的方法:

IntStream is = randomWithPercentZero(1_000_000, 0.60, 100);

获得1,000,000个整数的流,其中60%为零,其余为1-100(含)。

答案 1 :(得分:14)

由于目标间隔的大小可以被10整除,因此您可以依靠均匀分布的生成数字的最后一位数。因此,这种简单的方法应该有效:

  • 生成0..1000
  • 范围内的数字
  • 如果随机数<MoreText>的最后一位是0..5(包括0和5),则返回零
  • 否则,请返回r

以下是代码中的这种方法:

r / 10

Demo.

答案 2 :(得分:7)

您可以使用IntStream.map并重新使用Random实例生成0到9之间的随机数,如果它在前60%中返回0,否则生成号:

Stream<Integer> boxed = random.ints(0, 100)
                              .map(i -> (random.nextInt(10) < 6) ? 0 : i)
                              .boxed();

答案 3 :(得分:3)

构造10个对象。其中6个一直返回0。 and rest 4根据您的规格随机返回。

现在随机选择一个对象并调用

List<Callable<Integer>> callables = new ArrayList<>(10);
for (int i = 0; i < 6; i++) {
    callables.add(() -> 0);
}
Random rand = new Random();
for (int i = 6; i < 10; i++) {          
    callables.add(() -> rand.nextInt());
}

callables.get(rand.nextInt(10)).call();

这是实现它的更简单方法。您可以进一步优化它。

答案 4 :(得分:1)

如果你想要60%的零和40%的严格正数,你可以简单地使用模数检查:

Stream<Integer> boxed = IntStream.range(0, 100_000_000)
                            .map(i -> (i % 10 < 6) ? 0 : r.nextInt(Integer.MAX_VALUE) + 1)
                            .boxed();

您可能希望在此之后“改变”流,以避免每十个位置连续存在6个零。

答案 5 :(得分:1)

为什么不生成60%的数组(开始时为零值),然后随机生成另外40%的数组:

List<Integer> toShuffle = IntStream
            .concat(Arrays.stream(new int[60_000_000]),
                    random.ints(40_000_000, 0, Integer.MAX_VALUE))
            .boxed()
            .collect(Collectors.toCollection(() -> new ArrayList<>(100_000_000)));

    Collections.shuffle(toShuffle);

答案 6 :(得分:0)

您可以通过计算生成的值并生成零来达到所需的60%来控制您的流以生成正好60%的零。

public class RandomGeneratorSample {
    public static void main(String... strings) {
        Random random = new Random();
        Controll60PercentOfZero controll60 = new Controll60PercentOfZero();

        Stream<Integer> boxed = random.ints(0, 100).map(x -> controll60.nextValueMustBeZero(x) ? 0 : x).boxed();

        boxed.forEach(System.out::println);
    }

    static class Controll60PercentOfZero {
        private long count_zero = 1;
        private long count_not_zero = 1;

        public boolean nextValueMustBeZero(int x) {

            if (x == 0) {
                count_zero++;
            } else {
                count_not_zero++;
            }           
            boolean nextValueMustBeZero= (count_zero * 100 / count_not_zero) < 60;
            if(nextValueMustBeZero){
                count_zero++;
            }
            return nextValueMustBeZero;
        }
    }

}

答案 7 :(得分:0)

有趣的问题。
大多数其他答案都非常相关,每个答案都从不同的角度来处理 我想贡献。

要获得Collection(而非流)正好占据0的60%并且这些&#34;伪随机&#34;,您可以:

  • 声明并实例化列表

  • 然后循环你想要添加它的元素数量

  • 在其中,每10次迭代,你就会在List中的随机索引处添加0。否则,在List中的随机索引处添加随机值。

缺点是大约40%的时间,nextInt()被调用两次:一次生成值,另一次生成索引,在列表中插入值。

以下是从10000生成1000元素的示例代码,其中60%是0

Random random = new Random();
List<Integer> values = new ArrayList<>();

for (int i = 0; i < 1000; i++) {
    int nextValue = i % 10 < 6 ? 0 : random.nextInt(1000) + 1;
    int indexInList = values.size() <= 1 ? 0 : random.nextInt(values.size() - 1);
    values.add(indexInList, nextValue);
}