【问题标题】:Generating many random numbers within a range with some exceptions in java在一个范围内生成许多随机数,但在 java 中有一些例外
【发布时间】:2021-06-17 16:09:58
【问题描述】:

我需要在一个范围内生成大量随机数,但有一些例外。现在我打算这样做,

public class Main
{
    static List<Integer> except = Arrays.asList(5, 6, 11, 12, 17, 18, 23, 25, 28, 29);
    
    
    public static void main(String[] args) {
        
        List<Integer> randomNums = new ArrayList<>();
        
        Random random = new Random();
        
        int z;
        for(i=0; i<20; i++) {
            z = random.nextInt(30);
            while(except.contains(z)) z = random.nextInt(30);

            randomNums.add(z);
        }           
        
        System.out.println(randomNums);
    }
}

在我的情况下,“except”和“randomNums”的大小会大得多。所以代码会在一段时间内花很多时间来避免我不想要的数字。

我很想知道我可以加快我的代码速度吗?如果我可以删除 while 循环,那么肯定会是 O(n)。但我该怎么做。 谢谢。

【问题讨论】:

  • except 使用集合而不是列表。那么contains() 方法的 O(1) 而不是 O(n)。
  • 据我所知,无法删除 while 循环!
  • 我在想,如果我可以使用“随机数”->“我需要的数字(不包括除外中的数字)”中的某种映射,那么我可以删除 while 循环。 @K。史密斯
  • 你的随机数的范围是变化的还是不变的?
  • 您可以创建一个可接受数字的列表,然后使用随机生成器从该列表中的给定位置获取数字,但是我不知道这有多有用,因为您必须生成此列表一开始。

标签: java list random


【解决方案1】:

我的建议是,您将想要在结果中的所有数字列一个列表,然后每次从该列表中随机选择一个成员。它需要一些初始化,但之后你的循环应该会运行得很快。

    int maxExclusive = 30;
    Integer[] baseArr = new Integer[maxExclusive];
    Arrays.setAll(baseArr, Integer::valueOf);
    List<Integer> base = new ArrayList<>(Arrays.asList(baseArr));
    base.removeAll(except);
    
    List<Integer> randomNums = new ArrayList<>();
    
    Random random = new Random();
    
    for (int i = 0; i < 20; i++) {
        Integer z = base.get(random.nextInt(base.size()));
        randomNums.add(z);
    }
    
    System.out.println(randomNums);

示例输出:

[1, 10, 27, 2, 24, 22, 7, 8, 0, 27, 19, 27, 15, 14, 21, 22, 13, 24, 2, 13]

【讨论】:

  • 这绝对是一个更好的方法。谢谢。 +1
  • @abhimanyue 顺便说一句,以防万一您不希望出现重复的specialize 列表,您可以使用Collections.shuffle 并按顺序将它们从列表中拉出。
【解决方案2】:

这是一种方法:

    public static void main(String[] args) {

        final int exceptMin = 5;
        final int exceptMax = 29;

        Set<Integer> except = new HashSet<>(
                Arrays.asList(5, 6, 11, 12, 17, 18, 23, 25, 28, 29)
        );

        List<Integer> safeValues = getSafeValues(except, exceptMin, exceptMax);

        List<Integer> randomValues = getRandomValues(except, safeValues);
    }

    public static List<Integer> getSafeValues(Set<Integer> except, int exceptMin, int exceptMax) {

        List<Integer> safeValues = new ArrayList<>();
        for(int i = exceptMin; i < exceptMax; i++) {

            if(!except.contains(i))
                safeValues.add(i);
        }

        return safeValues;
    }

    public static List<Integer> getRandomValues(Set<Integer> except, List<Integer> safeValues) {

        List<Integer> randomValues = new ArrayList<>();
        for(int i = 0; i < 800_0000; i++) {
            int randomNumber = getRandomValue(except, safeValues);
            randomValues.add(randomNumber);
        }

        return randomValues;
    }

    public static int getRandomValue(Set<Integer> except, List<Integer> safeValues) {

        ThreadLocalRandom  random = ThreadLocalRandom.current();

        int randomNumber = random.nextInt(0, 30);

        if(except.contains(randomNumber)) {
            int randomIndex = random.nextInt(safeValues.size());
            randomNumber = safeValues.get(randomIndex);
        }

        return randomNumber;
    }

因此,只需使用另一个列表来存储可以安全使用的值,并在随机生成的值失败时选择其中一个。正如@vanje 已经提到的,它使用 HashSet 来执行您的查找,因为它具有恒定的时间复杂度 (https://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html)。

这种方法(在我的机器上)执行如下:

generated Numbers  time in ms
800_000            300
400_000            156
200_000            87
100_000            38

我使用了您的答案中指定的例外情况,并生成了介于 0(含)和 30(不含)之间的数字,因此有很多“未命中”。

但是,与生成“新”随机值相比,这种方法会消耗更多内存,并且需要一些准备工作。例如,当异常为{0, 100_0000} 时,您将生成大量“安全”数字。您可以将safeNumbers 列表拆分为多个列表来处理该问题。 除此之外,这种方法可能会破坏随机数的分布(如果这对您来说是个问题)。

编辑:当我写这个答案时,我没有意识到@Ole V.V.也使用了这种方法。如果需要,我将删除此答案。

【讨论】:

  • 我建议你可以在这里留下答案。一方面,有些人可能会觉得你的时间测量很有趣。此外,HashSet 的使用可能被认为是对我的代码的改进。
猜你喜欢
  • 1970-01-01
  • 2018-04-08
  • 2011-09-20
  • 1970-01-01
  • 2014-05-18
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多