简体   繁体   English

在Java中生成随机整数

[英]Generate random integers in java

How to generate random integers but making sure that they don't ever repeat? 如何生成随机整数,但要确保它们不会重复?

For now I use : 现在我使用:

Random randomGenerator = new Random();
randomGenerator.nextInt(100);

EDIT I

I'm looking for most efficient way, or least bad 我正在寻找最有效的方法,或者最糟糕的方法

EDIT II

Range is not important 范围不重要

ArrayList<Integer> list = new ArrayList<Integer>(100);
for(int i = 0; i < 100; i++)
{
  list.add(i);
}
Collections.shuffle(list);

Now, list contains the numbers 0 through 99, but in a random order. 现在, list包含数字0到99,但顺序随机。

If what you want is a pseudo-random non-repeating sequence of numbers then you should look at a linear feedback shift register . 如果您想要的是伪随机非重复数字序列,则应查看线性反馈移位寄存器 It will produce all the numbers between 0 and a given power of 2 without ever repeating. 它将产生0到给定2的幂之间的所有数字,而无需重复。 You can easily limit it to N by picking the nearest larger power of 2 and discarding all results over N. It doesn't have the memory constraints the other colleciton based solutions here have. 您可以通过选择最接近的2的大方幂并舍弃N上的所有结果,轻松地将其限制为N。它不具有此处基于其他大学模式的解决方案所具有的内存限制。

You can find java implementations here 您可以在这里找到Java实现

How to generate random integers but making sure that they don't ever repeat? 如何生成随机整数,但要确保它们不会重复?

First, I'd just like to point out that the constraint that the numbers don't repeat makes them non-random by definition . 首先,我只是想指出,这些数字不重复的约束使得他们的非随机的定义

I think that what you really need is a randomly generated permutation of the numbers in some range; 我认为您真正需要的是在一定范围内随机生成的数字排列 eg 0 to 99 . 例如099 Even then, once you have used all numbers in the range, a repeat is unavoidable. 即使这样,一旦使用了该范围内的所有数字,就不可避免地需要重复。

Obviously, you can increase the size of your range so that you can get a larger number without any repeats. 显然,您可以增加范围的大小,以便获得更大的数字而无需重复。 But when you do this you run into the problem that your generator needs to remember all previously generated numbers. 但是,当您执行此操作时,就会遇到生成器需要记住所有先前生成的数字的问题。 For large N that takes a lot of memory. 对于较大的N ,这会占用大量内存。

The alternative to remembering lots of numbers is to use a pseudo-random number generator with a long cycle length, and return the entire state of the generator as the "random" number. 记住大量数字的另一种方法是使用周期长的伪随机数生成器,然后将生成器的整个状态作为“随机”数返回。 That guarantees no repeated numbers ... until the generator cycles. 这样可以保证没有重复的数字...直到发电机循环。

(This answer is probably way beyond what the OP is interested in ... but someone might find it useful.) (此答案可能超出了OP感兴趣的范围,但可能有人觉得有用。)

If you have a very large range of integers (>>100), then you could put the generated integers into a hash table. 如果整数范围很大(>> 100),则可以将生成的整数放入哈希表中。 When generating new random numbers, keep generating until you get a number which isn't in your hash table. 生成新的随机数时,请继续生成,直到获得哈希表中没有的数字为止。

Since I can't comment on the earlier answers above due to not having enough reputation (which seems backwards... shouldn't I be able to comment on others' answers, but not provide my own answers?... anyway...), I'd like to mention that there is a major flaw with relying on Collections.shuffle() which has little to do with the memory constraints of your collection: 由于我没有足够的声誉而无法在上面的早期答案中发表评论(这似乎是倒退的……我不应该评论别人的答案,但不能提供自己的答案吗? 。),我想指出的是,依赖Collections.shuffle()有一个主要缺陷,它与您的集合的内存限制无关:

Collections.shuffle() uses a Random object, which in Java uses a 48-bit seed. Collections.shuffle()使用随机对象,在Java中使用48位种子。 This means there are 281,474,976,710,656 possible seed values. 这意味着可能有281,474,976,710,656个种子值。 That seems like a lot. 好像很多。 But consider if you want to use this method to shuffle a 52-card deck. 但是请考虑是否要使用此方法来洗牌52张牌。 A 52-card deck has 52! 52张牌中有52张! (over 8*10^67 possible configurations). (超过8 * 10 ^ 67种可能的配置)。 Since you'll always get the same shuffled results if you use the same seed, you can see that the possible configurations of a 52-card deck that Collections.shuffle() can produce is but a small fraction of all the possible configurations. 由于如果使用相同的种子,将始终获得相同的混洗结果,因此可以看到Collections.shuffle()可以生成的52张卡片组的可能配置只是所有​​可能配置的一小部分。

In fact, Collections.shuffle() is not a good solution for shuffling any collection over 16 elements. 实际上,对于将超过16个元素的任何集合进行混洗,Collections.shuffle()并不是一个好的解决方案。 A 17-element collection has 17! 一个17个元素的集合有17个! or 355,687,428,096,000 configurations, meaning 74,212,451,385,344 configurations will never be the outcome of Collections.shuffle() for a 17-element list. 或355,687,428,096,000配置,这意味着74,212,451,385,344个配置将永远不是17个元素列表的Collections.shuffle()的结果。

Depending on your needs, this can be extremely important. 根据您的需求,这可能非常重要。 Poor choice of shuffle/randomization techniques can leave your software vulnerable to attack. 随机/随机化技术选择不当会使您的软件容易受到攻击。 For instance, if you used Collections.shuffle() or a similar algorithm to implement a commercial poker server, your shuffling would be biased and a savvy computer-assisted player could use that knowledge to their benefit, as it skews the odds. 例如,如果您使用Collections.shuffle()或类似的算法来实现商用扑克服务器,则您的混洗将有偏见,并且精明的计算机辅助玩家可以利用这些知识来受益,因为这会使赔率发生偏差。

If you want 256 random numbers between 0 and 255, generate one random byte, then XOR a counter with it. 如果要在0到255之间使用256个随机数,请生成一个随机字节,然后将其与计数器异或。

byte randomSeed = rng.nextInt(255);
for (int i = 0; i < 256; i++) {
    byte randomResult = randomSeed ^ (byte) i;
    << Do something with randomResult >>
}

Works for any power of 2. 适用于2的任何幂。

Matthew Flaschen has the solution that will work for small numbers. 马修·弗拉申(Matthew Flaschen)的解决方案适用于少数人。 If your range is really big, it could be better to keep track of used numbers using some sort of Set : 如果您的范围确实很大,则最好使用某种Set来跟踪使用的数字:

Set usedNumbers = new HashSet();
Random randomGenerator = new Random();
int currentNumber;
while(IStillWantMoreNumbers) {
    do {
        currentNumber = randomGenerator.nextInt(100000);
    } while (usedNumbers.contains(currentNumber));
}

You'll have to be careful with this though, because as the proportion of "used" numbers increases, the amount of time this function takes will increase exponentially. 但是,您必须对此小心,因为随着“已使用”数字的比例增加,此功能花费的时间将成倍增加。 It's really only a good idea if your range is much larger than the amount of numbers you need to generate. 如果您的范围比您需要生成的数字量大得多,这实际上只是一个好主意。

Depending on the application, you could also generate a strictly increasing sequence, ie start with a seed and add a random number within a range to it, then re-use that result as the seed for the next number. 根据应用程序的不同,您还可以生成严格增加的序列,即从种子开始并向其添加一个范围内的随机数,然后将该结果重新用作下一个数字的种子。 You can set how guessable it is by adjusting the range, balancing this with how many numbers you will need (if you made incremental steps of up to eg, 1,000, you're not going to exhaust a 64-bit unsigned integer very quickly, for example). 您可以通过调整范围来设置可猜测性,将其与所需的数字进行平衡(如果您递增的步数最多为1,000,那么您将不会很快用尽64位无符号整数,例如)。

Of course, this is pretty bad if you're trying to create some kind of unguessable number in the cryptographic sense, however having a non-repeating sequence would probably provide a reasonably effective attack on any cypher based on it, so I'm hoping you're not employing this in any kind of security context. 当然,如果您尝试创建某种意义上的不可猜测的数字,这是非常糟糕的,但是具有非重复序列可能会对基于它的任何密码提供合理有效的攻击,所以我希望您不会在任何类型的安全上下文中使用此方法。

That said, this solution is not prone to timing attacks, which some of the others suggested are. 就是说,这种解决方案不容易受到定时攻击的影响,而其他一些建议就是这样。

If the Range of values is not finite, then you can create an object which uses a List to keep track of the Ranges of used Integers. 如果值的范围不是有限的,则可以创建一个使用列表的对象,以跟踪使用的整数的范围。 Each time a new random integer is needed, one would be generated and checked against the used ranges. 每次需要一个新的随机整数时,都会生成一个整数并对照使用的范围进行检查。 If the integer is unused, then it would add that integer as a new used Range, add it to an existing used Range, or merge two Ranges as appropriate. 如果未使用该整数,则它将将该整数添加为新的使用范围,将其添加到现有的使用范围,或适当合并两个范围。

But you probably really want Matthew Flaschen's solution. 但是您可能真的想要Matthew Flaschen的解决方案。

线性同余生成器可用于生成具有不同随机数的周期(完整周期)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM