【问题标题】:Java generating non-repeating random numbersJava生成非重复随机数
【发布时间】:2013-04-06 16:23:27
【问题描述】:

我想在 Java 中创建一组不重复的随机数。

例如,我有一个数组来存储从 0 到 9999 的 10,000 个随机整数。

这是我目前所拥有的:

import java.util.Random;
public class Sort{

    public static void main(String[] args){

        int[] nums = new int[10000];

        Random randomGenerator = new Random();

        for (int i = 0; i < nums.length; ++i){
            nums[i] = randomGenerator.nextInt(10000);
        }
    }
}

但是上面的代码会产生重​​复。如何确保随机数不重复?

【问题讨论】:

  • 但是如果你去掉重复的数字,那么它们就不是随机的了
  • 你想要 all 数组中的 10.000 个随机数,还是想要 10.000 个随机数?因为你不能有 0 - 9.999 范围内的 10.000 个随机数(那么它们就不再是随机的了)
  • 是的,我只是不想让他们重复这是最重要的。
  • 你希望它不以“1 1 2”重复的方式重复吗? “1 2 1”是可接受的序列吗?

标签: java random duplicates


【解决方案1】:
Integer[] arr = {...};
Collections.shuffle(Arrays.asList(arr));

例如:

public static void main(String[] args) {
    Integer[] arr = new Integer[1000];
    for (int i = 0; i < arr.length; i++) {
        arr[i] = i;
    }
    Collections.shuffle(Arrays.asList(arr));
    System.out.println(Arrays.toString(arr));

}

【讨论】:

  • 随机播放很棒,但首先你应该创建一个包含 0 到 9999 数字的数组,然后再随机播放。还有,shuffle的时间复杂度是多少?
  • @Martinsos 我创建了数组并对其进行了洗牌。我不确定,但我认为 shuffle 的时间复杂度应该是 O(n)。因为如果只是在数组内随机交换。
【解决方案2】:

可以在书Programming Pearls p 中找到一个简单的算法,它可以为您提供没有重复的随机数。 127.

注意:结果数组包含按顺序排列的数字!如果您希望它们以随机顺序排列,则必须使用Fisher–Yates shuffle 或使用列表并调用Collections.shuffle() 来打乱数组。

这种算法的好处是你不需要创建一个包含所有可能数字的数组,并且运行时复杂度仍然是线性的O(n)

public static int[] sampleRandomNumbersWithoutRepetition(int start, int end, int count) {
    Random rng = new Random();

    int[] result = new int[count];
    int cur = 0;
    int remaining = end - start;
    for (int i = start; i < end && count > 0; i++) {
        double probability = rng.nextDouble();
        if (probability < ((double) count) / (double) remaining) {
            count--;
            result[cur++] = i;
        }
        remaining--;
    }
    return result;
}

【讨论】:

  • 注意:(回复:您的“注意”部分)Collections.shuffle 正在进行 Fisher-Yates 洗牌,所以这不是“非此即彼”的情况。
  • 你说得对,Collections.shuffle 进行了 Fisher-Yates 洗牌,但您需要 List 才能使用它。 Arrays.asList 需要数组的类型为 Integer 而不是 int 才能正确进行转换,那么您不必分配额外的内存。自己编写 Fisher-Yates shuffle 可以避免转换,并且不需要额外的内存。
  • 只是想了解为什么需要probability &lt; ((double) count) / (double) remaining?为什么不从头到尾填充数组并随机播放?
  • 这个答案没有得到应有的赞誉。一直在对结果进行一些直方图以检查随机性,到目前为止,结果与预期的一样均匀分布。例如,sampleRandomNumbersWithoutRepetition(0, 100, 1)Math.floor(Math.random()*100) 之间没有明显差异。
  • @brainstorm 这种方法避免了构建整个阵列。例如,如果您只想要 0..1000 范围内的 5 个数字,这可以避免创建 995 个要丢弃的东西。
【解决方案3】:

在 Java 8 中,如果你想在 range (a, b) 中拥有一个不重复的 N 随机整数 list,其中 b 是独占的,你可以使用这样的东西:

Random random = new Random();
List<Integer> randomNumbers = random.ints(a, b).distinct().limit(N).boxed().collect(Collectors.toList());

【讨论】:

  • 这相当于 "random.ints(a, b).boxed().distinct().mapToInt(i -> i).limit(N).boxed().collect(. ..)”。不太有效。此外,在引擎盖下,它与 Vaibhav Jain 的实现相同,需要 500500 次迭代来处理 1000 个元素。请花时间了解您使用的库。
  • @Torben 实际上,在 Vaibhav Jain 实现中,每次迭代找到正确随机数的概率由 (N-n+1)/N 给出。其中 N 是请求的随机数的总数,n 是当前迭代的次数。然后,预期的迭代次数由 O(N log(N)) 给出,平均小于 500500。参见 en.wikipedia.org/wiki/Coupon_collector%27s_problem
【解决方案4】:

Achintya Jha 的想法是正确的。您无需考虑如何删除重复项,而是首先删除创建重复项的能力。

如果您想坚持使用整数数组并希望随机化它们的顺序(手动,这很简单),请按照以下步骤操作。

  1. 创建大小为 n 的数组。
  2. 循环遍历索引 i 处的每个值并将其初始化为值 i(或 i+1,如果您希望数字从 1 到 n,而不是从 0 到 n-1)。
  3. 最后,再次循环遍历数组,将每个值交换为随机索引处的值。

您的代码可以修改为如下所示:

import java.util.Random;

public class Sort
{
    // use a constant rather than having the "magic number" 10000 scattered about
    public static final int N = 10000;

    public static void main(String[] args)
    {
        //array to store N random integers (0 - N-1)
        int[] nums = new int[N];

        // initialize each value at index i to the value i 
        for (int i = 0; i < nums.length; ++i)
        {
            nums[i] = i;
        }

        Random randomGenerator = new Random();
        int randomIndex; // the randomly selected index each time through the loop
        int randomValue; // the value at nums[randomIndex] each time through the loop

        // randomize order of values
        for(int i = 0; i < nums.length; ++i)
        {
             // select a random index
             randomIndex = randomGenerator.nextInt(nums.length);

             // swap values
             randomValue = nums[randomIndex];
             nums[randomIndex] = nums[i];
             nums[i] = randomValue;
        }
    }
}

如果我是你,我可能会将这些块中的每一个分解成单独的、更小的方法,而不是拥有一个大的 main 方法。

希望这会有所帮助。

【讨论】:

    【解决方案5】:

    如果您需要生成带间隔的数字,可以这样:

    Integer[] arr = new Integer[((int) (Math.random() * (16 - 30) + 30))];
    for (int i = 0; i < arr.length; i++) {
    arr[i] = i;
    }
    Collections.shuffle(Arrays.asList(arr));
    System.out.println(Arrays.toString(arr));`
    

    结果:

    [1, 10, 2, 4, 9, 8, 7, 13, 18, 17, 5, 21, 12, 16, 23, 20, 6, 0, 22, 14, 24, 15, 3, 11, 19]

    注意:

    如果你需要零不离开你可以放一个“如果”

    【讨论】:

      【解决方案6】:

      这个怎么样?

      LinkedHashSet<Integer> test = new LinkedHashSet<Integer>();
      Random random = new Random();
      do{
          test.add(random.nextInt(1000) + 1);
      }while(test.size() != 1000);
      

      然后用户可以使用 for 循环遍历 Set

      【讨论】:

        【解决方案7】:
        public class RandomNum {
            public static void main(String[] args) {
                Random rn = new Random();
                HashSet<Integer> hSet = new HashSet<>();
                while(hSet.size() != 1000) {
                    hSet.add(rn.nextInt(1000));
                }
                System.out.println(hSet);
            }
        }
        

        【讨论】:

          【解决方案8】:

          如果您使用的是 JAVA 8 或以上版本,请按照以下方式使用流功能,

          Stream.generate(() -> (new Random()).nextInt(10000)).distinct().limit(10000);
          

          【讨论】:

            【解决方案9】:

            我们开始吧!

            public static int getRandomInt(int lower, int upper) {
                if(lower > upper) return 0;
                if(lower == upper) return lower;
                int difference = upper - lower;
                int start = getRandomInt();
                
                //nonneg int in the range 0..difference - 1
                start = Math.abs(start) % (difference+1);
                
                start += lower;
                return start;
            }
            
            public static void main(String[] args){
                
                List<Integer> a= new ArrayList();
                
                int i;
                int c=0;
                for(;;) {
                    c++;
                    i= getRandomInt(100, 500000);
                    if(!(a.contains(i))) {
                        a.add(i);
                        if (c == 10000) break;
                        System.out.println(i);
                    }
                    
                    
                }
                
                for(int rand : a) {
                    System.out.println(rand);
                }
                
                
                
            }
            

            获取随机数返回一个随机整数 x 满足下 upper,则返回 0。@param lower @param upper @return

            在我创建列表的主要方法中,然后我检查随机数是否存在于列表中,如果不存在,我会将随机数添加到列表中

            速度很慢,但很直接。

            【讨论】:

              【解决方案10】:

              一个简单的流解决方案:

                 new Random().ints(0, 10000)
                      .distinct()
                      .limit(10000)
                      .forEach(System.out::println);
              

              【讨论】:

                【解决方案11】:
                public class Randoms {
                
                static int z, a = 1111, b = 9999, r;
                
                public static void main(String ... args[])
                {
                       rand();
                }
                
                    public static void rand() {
                
                    Random ran = new Random();
                    for (int i = 1; i == 1; i++) {
                        z = ran.nextInt(b - a + 1) + a;
                        System.out.println(z);
                        randcheck();
                    }
                }
                
                private static void randcheck() {
                
                    for (int i = 3; i >= 0; i--) {
                        if (z != 0) {
                            r = z % 10;
                            arr[i] = r;
                            z = z / 10;
                        }
                    }
                    for (int i = 0; i <= 3; i++) {
                        for (int j = i + 1; j <= 3; j++) {
                            if (arr[i] == arr[j]) {
                                rand();
                            }
                        }
                
                    }
                }
                }
                

                【讨论】:

                  【解决方案12】:
                  HashSet<Integer>hashSet=new HashSet<>();
                  Random random = new Random();
                  //now add random number to this set
                  while(true)
                  {
                      hashSet.add(random.nextInt(1000));
                      if(hashSet.size()==1000)
                          break;
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2011-11-24
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-11-19
                    相关资源
                    最近更新 更多