如何做一个随机且唯一的生成器?
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】:获取从a
到b
顺序分布的元素的列表,将其打乱并在每个请求上返回带有后续索引的元素。
【讨论】:
如果范围很小,这将非常有效。如果范围很大,您最终会分配一个巨大的临时数组。 我赞成你的回答,但如果你的语言没有实现“随机播放”,很容易出错。请参阅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位数字,且永远不会重复,唯一的。