【问题标题】:Generating Unique Random Numbers in Java在 Java 中生成唯一的随机数
【发布时间】:2011-12-28 06:43:10
【问题描述】:

我正在尝试获取 0 到 100 之间的随机数。但我希望它们是唯一的,而不是按顺序重复。例如,如果我有 5 个数字,它们应该是 82,12,53,64,32 而不是 82,12,53,12,32 我用过这个,但它会在一个序列中生成相同的数字。

Random rand = new Random();
selected = rand.nextInt(100);

【问题讨论】:

标签: java random


【解决方案1】:
  • list 结构中按顺序添加范围内的每个数字。
  • Shuffle它。
  • 取第一个“n”。

这是一个简单的实现。这将打印 1-10 范围内的 3 个唯一随机数。

import java.util.ArrayList;
import java.util.Collections;

public class UniqueRandomNumbers {
    
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i=1; i<11; i++) {
            list.add(i);
        }
        Collections.shuffle(list);
        for (int i=0; i<3; i++) {
            System.out.println(list.get(i));
        }
    }
}

正如 Mark Byers 在现已删除的答案中指出的那样,原始方法修复的第一部分是仅使用单个 Random 实例。

这就是导致数字相同的原因。 Random 实例由当前时间播种,以毫秒为单位。对于特定的种子值,“随机”实例将返回完全相同的伪随机数字序列。

【讨论】:

  • +1 用于指出单个随机实例回答问题。 :)
  • 您不必重新排列整个范围。如果你想要 n 个唯一的数字,那么你只需要使用 Fisher-Yates 洗牌来洗牌前 n 个位置。这可能对大列表和小 n 有所帮助。
【解决方案2】:

在 Java 8+ 中,您可以使用 Randomints 方法获取随机值的 IntStream,然后使用 distinctlimit 将流减少为多个唯一随机值。

ThreadLocalRandom.current().ints(0, 100).distinct().limit(5).forEach(System.out::println);

Random 也有创建 LongStreams 和 DoubleStreams 的方法,如果你需要的话。

如果您希望某个范围内的所有(或大量)数字以随机顺序排列,则将所有数字添加到列表中,打乱它并取第一个 n 可能会更有效,因为上面的示例目前是通过在请求的范围内生成随机数并将它们传递给一个集合(类似于Rob Kielty's answer)来实现的,这可能需要生成比传递给限制的数量多得多的数量,因为生成一个新的唯一数字的概率会随着每个一个发现。这是另一种方式的示例:

List<Integer> range = IntStream.range(0, 100).boxed()
        .collect(Collectors.toCollection(ArrayList::new));
Collections.shuffle(range);
range.subList(0, 99).forEach(System.out::println);

【讨论】:

  • 我在一些我正在做基准测试的代码中需要这个,Arrays#setAll() 比流快一点。所以: ` Integer[] 索引 = new Integer[n]; Arrays.setAll(indices, i -> i); Collections.shuffle(Arrays.asList(indices));返回 Arrays.stream(indices).mapToInt(Integer::intValue).toArray(); `
【解决方案3】:
  1. 创建一个包含 100 个数字的数组,然后随机排列它们的顺序。
  2. 设计一个范围为 100 的伪随机数生成器。
  3. 创建一个包含 100 个元素的布尔数组,然后在选择该数字时将一个元素设置为 true。当您选择下一个数字时,请检查数组并重试是否设置了数组元素。 (您可以使用 long 数组创建一个易于清除的布尔数组,您可以在其中移位和屏蔽以访问各个位。)

【讨论】:

  • +1 用于替代方法; pick() 就是一个例子。
  • 您可以使用HashSet,而不是使用布尔数组,在其中存储您已经生成的数字并使用contains 来测试您是否已经生成了该数字。 HashSet 可能会比布尔数组稍慢,但占用的内存更少。
  • @RoryO'Kane -- 我很确定布尔数组会占用更少的空间,如果实现为 long[2] 数组。你不可能把 HashSet 做得那么小。
  • 最后一种方法有点难看,因为它没有明确定义的步骤数来生成整个序列。你也不需要重新发明轮子 - BitSet
【解决方案4】:

在所有 100 个号码上使用 Collections.shuffle() 并选择前五个,如图所示 here

【讨论】:

    【解决方案5】:

    我觉得这个方法值得一提。

       private static final Random RANDOM = new Random();    
       /**
         * Pick n numbers between 0 (inclusive) and k (inclusive)
         * While there are very deterministic ways to do this,
         * for large k and small n, this could be easier than creating
         * an large array and sorting, i.e. k = 10,000
         */
        public Set<Integer> pickRandom(int n, int k) {
            final Set<Integer> picked = new HashSet<>();
            while (picked.size() < n) {
                picked.add(RANDOM.nextInt(k + 1));
            }
            return picked;
        }
    

    【讨论】:

      【解决方案6】:

      我重构了 Anand 的答案,不仅使用了 Set 的独特属性,还使用了 set.add() 在添加到集合失败时返回的布尔值 false。

      import java.util.HashSet;
      import java.util.Random;
      import java.util.Set;
      
      public class randomUniqueNumberGenerator {
      
          public static final int SET_SIZE_REQUIRED = 10;
          public static final int NUMBER_RANGE = 100;
      
          public static void main(String[] args) {
              Random random = new Random();
      
              Set set = new HashSet<Integer>(SET_SIZE_REQUIRED);
      
              while(set.size()< SET_SIZE_REQUIRED) {
                  while (set.add(random.nextInt(NUMBER_RANGE)) != true)
                      ;
              }
              assert set.size() == SET_SIZE_REQUIRED;
              System.out.println(set);
          }
      }
      

      【讨论】:

      • 好主意。一个重要的标记 - 如果SET_SIZE_REQUIRED 足够大(比方说,比NUMBER_RANGE / 2 大,那么您的预期运行时间要长得多。
      【解决方案7】:

      我做了这个。

          Random random = new Random();
          ArrayList<Integer> arrayList = new ArrayList<Integer>();
      
          while (arrayList.size() < 6) { // how many numbers u need - it will 6
              int a = random.nextInt(49)+1; // this will give numbers between 1 and 50.
      
              if (!arrayList.contains(a)) {
                  arrayList.add(a);
              }
          }
      

      【讨论】:

        【解决方案8】:

        这将产生唯一的随机数......

        import java.util.HashSet;
        import java.util.Random;
        
        public class RandomExample {
        
            public static void main(String[] args) {
                Random rand = new Random();
                int e;
                int i;
                int g = 10;
                HashSet<Integer> randomNumbers = new HashSet<Integer>();
        
                for (i = 0; i < g; i++) {
                    e = rand.nextInt(20);
                    randomNumbers.add(e);
                    if (randomNumbers.size() <= 10) {
                        if (randomNumbers.size() == 10) {
                            g = 10;
                        }
                        g++;
                        randomNumbers.add(e);
                    }
                }
                System.out.println("Ten Unique random numbers from 1 to 20 are  : " + randomNumbers);
            }
        }
        

        【讨论】:

          【解决方案9】:

          一个聪明的方法是在模数中使用原始元素的指数。

          例如,2 是原始根 mod 101,这意味着 2 mod 101 的幂为您提供了一个不重复的序列,可以看到从 1 到 100 的每个数字:

          2^0 mod 101 = 1
          2^1 mod 101 = 2
          2^2 mod 101 = 4
          ...
          2^50 mod 101 = 100
          2^51 mod 101 = 99
          2^52 mod 101 = 97
          ...
          2^100 mod 101 = 1
          

          在 Java 代码中,你会这样写:

          void randInts() {
          int num=1;
          for (int ii=0; ii<101; ii++) {
              System.out.println(num);
              num= (num*2) % 101;
              }
          }
          

          找到特定模数的原始根可能很棘手,但 Maple 的“primroot”函数将为您完成。

          【讨论】:

          • 这很有趣,但是我们如何确保生成的序列是随机的呢?似乎不是。在序列的开头有 1,2,4,8,16,... 似乎非常具有确定性。
          • 这不是随机的……它是伪随机的。没有人知道如何生成真正的随机数。如果您不喜欢初始模式,可以使用更大的基作为原始根。
          • 伪随机就可以了。但是在这里,对于给定的“范围”,原始根的数量以及因此唯一的序列是有限的,特别是对于较小的范围。所以模式似乎存在问题,例如总是有一个根的权力的子序列。除非我们应用更多的恶作剧,否则不会在多次运行中获得(可能)非常不同的序列。我想这取决于用例。无论如何,改变基础是一个不错的升级,尽管它只会“改变”模式。
          【解决方案10】:

          我是从另一个问题来到这里的,该问题与这个问题重复 (Generating unique random number in java)

          1. 在一个数组中存储 1 到 100 个数字。

          2. 生成1到100之间的随机数作为位置,返回array[position-1]取值

          3. 在数组中使用数字后,将值标记为-1(无需维护另一个数组来检查该数字是否已使用)

          4. 如果数组中的值为-1,则再次获取随机数以获取数组中的新位置。

          【讨论】:

            【解决方案11】:

            我对这个问题有简单的解决方案, 有了这个,我们可以很容易地生成 n 个唯一的随机数, 这只是逻辑,任何人都可以用任何语言使用它。

            for(int i=0;i<4;i++)
                    {
                        rn[i]= GenerateRandomNumber();
                        for (int j=0;j<i;j++)
                        {
                            if (rn[i] == rn[j])
                            {
                                i--;
                            }
                        }
                    }
            

            【讨论】:

            • 您可以在i—; 之后通过break; 进行优化
            【解决方案12】:

            以下代码将生成 5 个 1 到 100 之间的随机数

             public static void main(String[] args) {
                    Random random = new Random();
                    HashSet<Integer> set = new HashSet<>();
                    while (set.size() < 5) {
                        int a = random.nextInt(99)+1;
                        if (!set.contains(a)) {
                            set.add(a);
                        }
                    }
                    System.out.println(set);
                }
            

            【讨论】:

              【解决方案13】:

              虽然这是一个旧线程,但添加另一个选项可能不会有害。 (JDK 1.8 lambda 函数似乎让它变得简单);

              问题可以分解为以下几个步骤;

              • 获取提供的整数列表的最小值(为其生成唯一的随机数)
              • 获取提供的整数列表的最大值
              • 使用 ThreadLocalRandom 类(来自 JDK 1.8)根据先前找到的最小和最大整数值生成随机整数值,然后过滤以确保这些值确实包含在最初提供的列表中。最后对 intstream 应用 distinct 以确保生成的数字是唯一的。

              这里有一些描述的函数:

              /**
               * Provided an unsequenced / sequenced list of integers, the function returns unique random IDs as defined by the parameter
               * @param numberToGenerate
               * @param idList
               * @return List of unique random integer values from the provided list
               */
              private List<Integer> getUniqueRandomInts(List<Integer> idList, Integer numberToGenerate) {
              
                  List<Integer> generatedUniqueIds = new ArrayList<>();
              
                  Integer minId = idList.stream().mapToInt (v->v).min().orElseThrow(NoSuchElementException::new);
                  Integer maxId = idList.stream().mapToInt (v->v).max().orElseThrow(NoSuchElementException::new);
              
                          ThreadLocalRandom.current().ints(minId,maxId)
                          .filter(e->idList.contains(e))
                          .distinct()
                          .limit(numberToGenerate)
                          .forEach(generatedUniqueIds:: add);
              
                  return generatedUniqueIds;
              
              }
              

              因此,要为“allIntegers”列表对象获取 11 个唯一随机数,我们将调用函数:

                  List<Integer> ids = getUniqueRandomInts(allIntegers,11);
              

              该函数声明新的arrayList 'generatedUniqueIds' 并在返回之前使用每个唯一随机整数填充到所需的数字。

              附: ThreadLocalRandom 类在并发线程的情况下避免公共种子值。

              【讨论】:

                【解决方案14】:

                试试这个

                public class RandomValueGenerator {
                    /**
                     * 
                     */
                    private volatile List<Double> previousGenValues = new ArrayList<Double>();
                
                    public void init() {
                        previousGenValues.add(Double.valueOf(0));
                    }
                
                    public String getNextValue() {
                        Random random = new Random();
                        double nextValue=0;
                        while(previousGenValues.contains(Double.valueOf(nextValue))) {
                            nextValue = random.nextDouble();
                        }
                        previousGenValues.add(Double.valueOf(nextValue));
                        return String.valueOf(nextValue);
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  这与其他答案没有显着不同,但我最终想要整数数组:

                      Integer[] indices = new Integer[n];
                      Arrays.setAll(indices, i -> i);
                      Collections.shuffle(Arrays.asList(indices));
                      return Arrays.stream(indices).mapToInt(Integer::intValue).toArray();
                  

                  【讨论】:

                    【解决方案16】:

                    如果取值,您可以使用布尔数组填充 true,否则通过布尔数组导航以获取下面给出的值

                    package study;
                    
                    import java.util.ArrayList;
                    import java.util.Arrays;
                    import java.util.List;
                    
                    /*
                    Created By Sachin  Rane on Jul 18, 2018
                    */
                    public class UniqueRandomNumber {
                        static Boolean[] boolArray;
                        public static void main(String s[]){
                            List<Integer> integers = new ArrayList<>();
                    
                    
                            for (int i = 0; i < 10; i++) {
                                integers.add(i);
                            }
                    
                    
                            //get unique random numbers
                            boolArray = new Boolean[integers.size()+1];
                            Arrays.fill(boolArray, false);
                            for (int i = 0; i < 10; i++) {
                                System.out.print(getUniqueRandomNumber(integers) + " ");
                    
                            }
                    
                        }
                    
                        private static int  getUniqueRandomNumber(List<Integer> integers) {
                            int randNum =(int) (Math.random()*integers.size());
                            if(boolArray[randNum]){
                                while(boolArray[randNum]){
                                    randNum++;
                                    if(randNum>boolArray.length){
                                        randNum=0;
                                    }
                                }
                                boolArray[randNum]=true;
                                return randNum;
                            }else {
                                boolArray[randNum]=true;
                                return randNum;
                            }
                    
                        }
                    
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      从 0 到 m-1 中选择 n 个唯一的随机数。

                      int[] uniqueRand(int n, int m){
                          Random rand = new Random();
                          int[] r = new int[n];
                          int[] result = new int[n];
                          for(int i = 0; i < n; i++){
                              r[i] = rand.nextInt(m-i);
                              result[i] = r[i];
                              for(int j = i-1; j >= 0; j--){
                                  if(result[i] >= r[j])
                                      result[i]++;
                              }
                          }
                          return result;
                      }
                      

                      想象一个包含从 0 到 m-1 的数字的列表。要选择第一个数字,我们只需使用rand.nextInt(m)。然后从列表中删除该号码。现在还有 m-1 个数字,所以我们调用 rand.nextInt(m-1)。我们得到的数字代表列表中的位置。如果它小于第一个数字,则它是第二个数字,因为在第一个数字之前的列表部分没有因删除第一个数字而改变。如果位置大于或等于第一个数字,则第二个数字是位置+1。再推导一下,就可以得到这个算法了。

                      说明

                      该算法的复杂度为 O(n^2)。因此,它适用于从大量集合中生成少量唯一数字。而基于 shuffle 的算法至少需要 O(m) 才能进行 shuffle。

                      基于随机播放的算法也需要内存来存储每一个可能的结果来进行随机播放,这个算法不需要。

                      【讨论】:

                        【解决方案18】:

                        您可以使用 Collections 类。

                        一个名为 Collections 的实用程序类提供了可以在 ArrayList 等集合上执行的不同操作(例如,搜索元素、查找最大或最小元素、反转元素顺序等)。它可以执行的操作之一是随机播放元素。随机播放会将每个元素随机移动到列表中的不同位置。它通过使用 Random 对象来做到这一点。这意味着它是确定性随机性,但在大多数情况下都可以。

                        要打乱 ArrayList,请将 Collections 导入添加到程序顶部,然后使用 Shuffle 静态方法。它将 ArrayList 作为参数进行混洗:

                        import java.util.Collections;
                        import java.util.ArrayList;
                        public class Lottery {
                        public static void main(String[] args) {
                        //define ArrayList to hold Integer objects
                        ArrayList numbers = new ArrayList();
                        for(int i = 0; i < 100; i++)
                        {
                        numbers.add(i+1);
                        }
                        Collections.shuffle(numbers);
                        System.out.println(numbers);
                        }
                        }
                        

                        【讨论】:

                          【解决方案19】:

                          这是最简单的方法,可以在一个范围内或从一个数组中生成唯一的随机值

                          在此示例中,我将使用预定义的数组,但您也可以调整此方法来生成随机数。首先,我们将创建一个示例数组来检索我们的数据。

                          1. 生成一个随机数并将其添加到新数组中。
                          2. 生成另一个随机数并检查它是否已存储在新数组中。
                          3. 如果没有,则添加并继续
                          4. 否则重复该步骤。
                          ArrayList<Integer> sampleList = new ArrayList<>();
                          sampleList.add(1);
                          sampleList.add(2);
                          sampleList.add(3);
                          sampleList.add(4);
                          sampleList.add(5);
                          sampleList.add(6);
                          sampleList.add(7);
                          sampleList.add(8);
                          

                          现在,我们将从sampleList 生成五个唯一的随机数。

                          int n;
                          randomList = new ArrayList<>();
                          for(int  i=0;i<5;i++){
                              Random random = new Random();
                              n=random.nextInt(8);     //Generate a random index between 0-7
                          
                              if(!randomList.contains(sampleList.get(n)))
                              randomList.add(sampleList.get(n));
                              else
                                  i--;    //reiterating the step
                          }
                                  
                          

                          这在概念上非常简单。如果生成的随机值已经存在,那么我们将重复该步骤。这将一直持续到生成的所有值都是唯一的。

                          如果您发现此答案有用,那么您可以投票,因为与其他答案相比,它在概念上要简单得多

                          【讨论】:

                            【解决方案20】:

                            检查一下

                            public class RandomNumbers {
                            
                                public static void main(String[] args) {
                                    // TODO Auto-generated method stub
                                    int n = 5;
                                    int A[] = uniqueRandomArray(n);
                                    for(int i = 0; i<n; i++){
                                        System.out.println(A[i]);
                                    }
                                }
                                public static int[] uniqueRandomArray(int n){
                                    int [] A = new int[n];
                                    for(int i = 0; i< A.length; ){
                                        if(i == A.length){
                                            break;
                                        }
                                        int b = (int)(Math.random() *n) + 1;
                                        if(f(A,b) == false){
                                            A[i++] = b;
                                        } 
                                    }
                                    return A;
                                }
                                public static boolean f(int[] A, int n){
                                    for(int i=0; i<A.length; i++){
                                        if(A[i] == n){
                                            return true;
                                        }
                                    }
                                    return false;
                                }
                            }
                            

                            【讨论】:

                            • 把 Java 标准、可读性和可用性抛到一边了,嗯?
                            • 代码不是答案。你写一个答案,然后添加代码来解释你想要什么。
                            【解决方案21】:

                            以下是我用来生成唯一编号的方法。随机函数生成数字并将其存储在文本文件中,然后下次在文件中检查它时比较它并生成新的唯一编号,因此这样总是有一个新的唯一编号。

                            public int GenerateRandomNo()
                            {
                                int _min = 0000;
                                int _max = 9999;
                                Random _rdm = new Random();
                                return _rdm.Next(_min, _max);
                            }
                            public int rand_num()
                            {
                                randnum = GenerateRandomNo();
                                string createText = randnum.ToString() + Environment.NewLine;
                                string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
                                File.AppendAllText(file_path, createText);
                                int number = File.ReadLines(file_path).Count(); //count number of lines in file
                                System.IO.StreamReader file = new System.IO.StreamReader(file_path);
                                do
                                {
                                    randnum = GenerateRandomNo();
                                }
                                while ((file.ReadLine()) == randnum.ToString());
                                file.Close();
                                return randnum;
                            
                            }
                            

                            【讨论】:

                              【解决方案22】:

                              你可以在java中生成n个0到n-1之间的唯一随机数

                              public static void RandomGenerate(int n)
                              {
                                   Set<Integer> st=new HashSet<Integer>();
                                   Random r=new Random();
                                   while(st.size()<n)
                                   {
                                      st.add(r.nextInt(n));
                                   }
                              

                              }

                              【讨论】:

                                猜你喜欢
                                • 2012-03-14
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多