如何做一个随机且唯一的生成器?

Posted

技术标签:

【中文标题】如何做一个随机且唯一的生成器?【英文标题】:How to do a random-and-unique generator? 【发布时间】:2011-02-18 05:51:10 【问题描述】:

我已经写了一个随机生成器,它接受参数 a 和 b,其中 a 是最小值,b 是最大值,就像这个 randomGenerator(int a, int b)

接下来我要做的是:使用循环,然后生成从 a 到 b 的唯一编号。示例:

I want to have 8 unique numbers,
int a = 1;
int b = 10;
int value;

如果我进行循环,那么相同的数字会出现多次。知道怎么做吗?

我自己的方式是:

while(int i <= 8)
  randomGenerator(a,b);
  // if value is not in array, then insert into array

我被困在评论部分。有没有办法检查数组中是否存在变量?

编辑,根据nailxx的回答,我的理解是:

从 a 到 b 的列表(如果按照我的例子,1 - 10)

“随机播放”它

取前 8 项。是这个意思吗?

在 java 世界中,是否有“洗牌”功能或者我需要自己创建?

【问题讨论】:

随机和独特几乎是相互排斥的术语。您只想创建一个“随机”数字列表,然后随机播放列表并从列表中弹出。 【参考方案1】:

获取从ab 顺序分布的元素的列表,将其打乱并在每个请求上返回带有后续索引的元素。

【讨论】:

如果范围很小,这将非常有效。如果范围很大,您最终会分配一个巨大的临时数组。 我赞成你的回答,但如果你的语言没有实现“随机播放”,很容易出错。请参阅robweir.com/blog/2010/02/microsoft-random-browser-ballot.html,了解一篇关于一个壮观例子的有趣文章。 假设 a 是 3,b 是 7。取一个列表 [3, 4, 5, 6, 7]。将其随机播放(例如使用 Mark Byers 建议的 Fisher-Yates 算法),您将得到 [4, 7, 6, 5, 3]。比一个一个地取数字 我觉得你的方法比我的效率高很多,谢谢你回答我的问题。 我通常通过创建对象数组来进行随机播放。我为每个项目中我想要的信息添加了一个成员。我还添加了一个成员来存储随机数生成器的结果。然后,您按随机数对数组进行排序。简单可靠。【参考方案2】:

由于您的样本大小几乎与您的愤怒大小相同,您可以只生成从 1 到 10 的所有整数,shuffle them 然后取前 8 个元素。

如果样本大小 (k) 远小于范围大小 (n),您可以使用 Fisher-Yates shuffle 的变体,在 k 步后停止算法,而不是对整个数组进行洗牌。

如果样本量很小但范围大小 [0,n) 太大而无法存储在内存中,那么您可以使用另一种变体,首先在 [0,n) 中选择一个随机数 s1。然后在 [0, n-1) 中选择一个数字,如果它等于或大于 s1,则在其上加一,得到 s2。然后在 [0, n-2) 中选择一个数字,并为 s1、s2 中的每一个加一,它大于或等于等等...

【讨论】:

【参考方案3】:

如果您的想法是获得 8 个随机数,您始终可以使用字典/哈希或列表(具有 Contains(int i) 方法),然后将字典或列表转换为数组:

List<int> x = ...;
int[] data = x.ToArray();

【讨论】:

【参考方案4】:
List<Integer> list=new ArrayList<Integer>(8);

while(int i <= 8) 
  do 
    Integer n=randomGenerator(a,b); 
   while (list.contains(n))
  list.add(n);
 

当然,如果 a 和 b 之间没有至少 8 个不同的值,这将陷入无限循环。在开始之前我会为此设置一个陷阱。

只有 8 个值,一个简单的 ArrayList 可能是最容易使用的东西。如果你有成百上千个值,为每个新条目遍历所有值可能会令人望而却步,你可能想要做一些更复杂的事情,比如使用 HashMap,或者保留一个单独的标志数组来说明所使用的值,或类似的。

【讨论】:

【参考方案5】:

如果您的范围很小(或接近您要选择的元素数量),上面提出的解决方案很好。

如果你的范围变大,一个更有效(和清晰)的方法如下:


    int[] pickRandom(int numberOfChoices, int lowerBound, int upperBound) 
      Set<Integer> choices = new HashSet<Integer>();
      while (choices.size() <= numberOfChoices) 
        choices.add(yourRandomGenerator (lowerBound, upperBound));
      
      int[] ret = new int[choices.size()];
      int ii=0;
      for (Integer choice: choices) 
        ret[ii++] = choice.intValue();
      
      return ret;
    

【讨论】:

【参考方案6】:

这里有一些很好的答案,但我要补充一个重要的警告:不要使用List,使用HashSet。掸掉我的计算机科学书呆子术语,检查一个元素是否已经存在于 List 中,运行时间为 O(n);用 HashSet 做同样的事情会给你 O(lg n) 的性能,这要快得多。对于小型数据集(例如 1 到 10 之间的 8 个数字),这很简单。对于大型数据集,它可以产生影响。

应该这样做:

    HashSet resultSet = new HashSet();
    while (resultSet.size() < targetSize) 
       resultSet.add(randomGenerator(a,b));
    

...当然,在您的先决条件中进行健全性检查,以确保您的目标大小不超过您的范围。

如果订单很重要,请在使用HashSet 确认它是新号码后将它们粘贴在List 中。或者只是将HashSet 转储到新的List 并随机播放。

【讨论】:

【参考方案7】:

Collections 类中有一个内置的随机播放功能。

例如

List values = new ArrayList(); for( int i = 1; i < 10; i++ ) values.add(i); Collections.shuffle(values);

【讨论】:

以上是关于如何做一个随机且唯一的生成器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Java程序中写一个方法可随机生成12位数字,且永远不会重复,唯一的。

生成唯一且随机的整数

如何在Java程序中写一个方法可随机生成12位数字,且永远不会重复,唯一的。

Java 如何实现生成有序且唯一的id

如何从给定范围生成固定长度值的随机列表?

如何在laravel中为每个用户生成唯一的随机值并将其添加到数据库中