Java如何防止使用Random类创建重复的数字?

时间:2019-05-26 18:44:58

标签: java

我正在通过阅读“ JAVA,如何编程”一书来学习Java,我已经读到了第7章,它讨论了数组和数组操作。在7.5节(案例研究:纸牌洗牌和交易模拟)中,它介绍了一个程序,该程序创建一副纸牌,对其进行洗牌,并显示经过洗牌的纸牌,而没有任何重复。

在程序中,它使用shuffle方法在创建卡片后对其进行洗牌。该方法使用随机对象创建一个介于0到51之间的随机数,以从卡片组中选择一张卡片并将其分配给一个数组。代码:

 public void shuffle() {
     // after shuffling, dealing should start at deck[ 0 ] again
     currentCard = 0; // reinitialize currentCard

     // for each Card, pick another random Card (0-51) and swap them
     for (int first = 0; first < deck.length; first++) {
         // select a random number between 0 and 51
         int second = randomNumbers.nextInt( NUMBER_OF_CARDS ); 

         // swap current Card with randomly selected Card
         Card temp = deck[ first ];
         deck[ first ] = deck[ second ];
         deck[ second ] = temp;
    }
}

如果int second = randomNumbers.nextInt( Number_OF_CARDS );该怎么办 生成一个已经选择的随机数?甲板上不会有重复的东西吗?如果不是这种情况,为什么此行没有给出重复的数字?我想念什么?

我了解到,在给定的范围内,“几乎”均等地生成了随机数,但是仍有机会生成之前已经生成的数字。

3 个答案:

答案 0 :(得分:2)

它不会重复,因为您总是在交换卡,并且交换也永远不会重复。例如,假设我的列表以[a, b, c, d]开始。如果我选择1作为我的第一个随机数,则我交换元素0和2,最后得到[c, b, a, d]。如果我再次选择2作为第二个数字,则交换1和2,最后得到[c, a, b, d]

但是您是正确的,该算法是错误的!您可以交换已交换的卡,这意味着较早交换的卡更有可能再次被交换,这会带来偏差。 This CodingHorror blog post对此进行了讨论,如果您搜索“错误的改组算法”之类的话,在线上还有许多其他讨论。

相反,如果您尝试将卡交换到位置i,则应该从位置x随机选择一张卡,例如i <= x < deck.length。我暂时忘了这本书的(不正确的)算法的名字,但是我提到的变种叫做Fisher–Yates shuffle

答案 1 :(得分:0)

此代码不会从卡组中挑选卡片并插入新列表。而是多次交换两张随机卡。卡组内的卡仍然彼此“唯一”。多次选择一个索引不是问题,它只是与另一张卡交换了。因此,在一种情况下,它可能会在索引514处交换卡,而在另一次for循环迭代中,它可能会在索引11和{{1}处交换卡。 }。但是最后,卡片组包含了相同的卡片,但是它们不再是原始顺序了。

答案 2 :(得分:0)

这是我多年前学到的算法。关键是要不断减少随机数的限制,这样您就不会替换已替换的内容。


public class ShuffleDemo {
   int max = 10;

   public static void main(String[] args) {
      new ShuffleDemo().shuffle();
   }

   public void shuffle() {
      // start off with an array of 1 to max
      int[] vals = IntStream.rangeClosed(0, max).toArray();
      while (max > 0) {
         int selection = ThreadLocalRandom.current().nextInt(max);
         int temp = vals[max - 1];
         vals[max - 1] = vals[selection];
         vals[selection] = temp;
         max--;
      }

      System.out.println(Arrays.toString(vals));
   } // end method
}

卡仍然有可能最终回到其原始位置。但是,那也可以在真正的套牌中发生