发出「;套牌洗牌“;程序:在Java结果中获取意外的零

发出「;套牌洗牌“;程序:在Java结果中获取意外的零,java,arrays,shuffle,Java,Arrays,Shuffle,我的任务是创建一个卡片组洗牌程序,但我对洗牌的算法有问题。想法是从牌组中随机抽取一张牌(值[]),然后将其放入新的洗牌牌组(洗牌[])。然而,我的程序正在抛出包含太多零的洗牌。原始数组包含int值0-51。出于某种原因,第一个零也始终保留在第一个插槽中。这是我的密码: public static void selectionShuffle(int[] values) { int[] shuffled = new int[52]; for (int k = 51; k > 0

我的任务是创建一个卡片组洗牌程序,但我对洗牌的算法有问题。想法是从牌组中随机抽取一张牌(值[]),然后将其放入新的洗牌牌组(洗牌[])。然而,我的程序正在抛出包含太多零的洗牌。原始数组包含int值0-51。出于某种原因,第一个零也始终保留在第一个插槽中。这是我的密码:

public static void selectionShuffle(int[] values) {
    int[] shuffled = new int[52];
    for (int k = 51; k > 0;) {
        int r = (int)(Math.random() * (double)51);
        if (values[r] != -1) {
            shuffled[k] = values[r];
            values[r] = -1;
            k--;
        }
    }
    for (int i = 0; i < values.length; i++) {
        values[i] = shuffled[i];
    }

}

Results of 8 consecutive random selection shuffles
1: 0 22 40 43 6 14 31 4 47 1 36 41 0 3 24 12 5 39 27 23 11 28 50 38 7 18 16 32 17 20 21 2 8 13 15 46 19 26 9 48 25 34 45 42 10 33 29 49 30 44 37 35
2: 0 17 34 2 26 12 4 13 38 27 20 29 40 28 47 0 48 9 6 43 46 33 23 1 19 3 49 41 7 39 30 8 25 32 10 24 0 16 45 36 14 37 42 11 44 15 50 21 31 18 22 5
3: 0 23 2 44 20 38 45 46 47 27 50 7 26 28 0 21 24 37 11 19 40 10 1 29 36 14 30 12 25 22 16 6 39 0 0 8 18 33 9 48 42 43 34 13 32 17 41 15 49 4 3 31
4: 0 32 49 0 13 41 25 46 18 2 28 23 7 40 0 47 0 29 45 22 21 27 8 30 1 19 4 26 37 14 44 20 15 39 50 12 10 11 36 34 42 0 24 6 3 17 33 16 48 38 9 43
5: 0 29 26 13 1 15 0 20 47 9 17 21 30 34 28 0 22 18 0 3 6 2 0 38 12 48 23 27 11 16 42 32 39 40 33 0 37 44 50 41 46 49 8 24 10 7 14 25 19 45 36 4
6: 0 7 32 26 36 33 28 27 15 39 47 30 45 23 0 0 42 50 17 40 22 48 0 20 37 14 21 49 10 19 9 18 2 16 0 3 8 24 41 1 0 38 13 0 29 44 12 46 11 25 34 6
7: 0 9 0 21 29 18 48 33 45 20 15 24 44 46 47 0 36 2 39 28 0 12 0 50 19 42 32 27 8 38 37 23 0 11 25 13 10 3 0 34 26 40 17 0 41 7 30 14 1 22 16 49
8: 0 46 41 20 8 38 9 36 40 3 14 26 33 44 10 47 24 27 29 16 28 32 0 18 39 48 0 34 12 0 30 17 0 23 15 22 13 0 25 7 45 0 37 11 21 42 50 19 2 0 0 1
publicstaticvoidselectionshuffle(int[]值){
int[]shuffled=新int[52];
对于(int k=51;k>0;){
int r=(int)(Math.random()*(double)51);
如果(值[r]!=-1){
乱序[k]=值[r];
值[r]=-1;
k--;
}
}
对于(int i=0;i
我认为问题在下面的代码行中。如果不初始化大于或等于0的数组的所有值,则不会更改该值。因为默认的int值是0,所以会遇到这种情况

for (int k = 51; k > 0;) {
应该是

for (int k = 51; k >= 0;) {
  • 用52张牌填满一副牌
  • 解析数据组51次,在rand()*(numOfCardsRemainingDeck)处抓取卡
  • 将所有卡从“拔出”位置移到“向左”位置
  • 经过51次迭代后,剩下的最后一张牌卡在结果牌组的最后一个位置

  • 问题是,您的算法从52张牌中取出51张,将
    洗牌[0]
    保留在
    0
    。通过查看洗牌的结果,您可以看到正在发生的情况:位置0始终设置为零,而其余位置设置为随机值

    其中一张卡在第一次迭代时保持未缓冲状态;零取代了它的位置

    下一次迭代也可以洗牌该零,因此现在有三个零。下一次迭代可能会再加一个零,以此类推

    要解决此问题,请确保循环一直到零,即

    for (int k = 51; k >= 0;) {
    //                  ^
    
    您还需要将random乘以52,否则您将永远不会命中数组的最后一个元素:

    int r = (int)(Math.random() * (double)52);
    
    您还可以制作一个新的随机数生成器,并调用它


    请注意,点击最后一个元素可能需要一段时间。另一种解决方案是运行循环51次,然后将最后一个未重置为-1的剩余值置于零位。

    我建议使用列表来执行此操作,而不是数组

    如果使用
    列表
    ,则可以使用

    • 使用指定的随机性源随机排列指定的列表。假设随机性的来源是公平的,所有排列都以相同的可能性发生。 这个实现向后遍历列表,从最后一个元素一直到第二个元素,重复地将随机选择的元素交换到“当前位置”。元素从列表中从第一个元素到当前位置(包括第一个元素)的部分随机选择
    此方法以线性时间运行。如果指定的列表未实现RandomAccess接口且较大,则此实现会在洗牌之前将指定列表转储到数组中,并将洗牌后的数组转储回列表中。这避免了由于将“顺序访问”列表洗牌而导致的二次行为

    public static void shuffle(List<?> list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
            Object arr[] = list.toArray();
    
            // Shuffle array
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));
    
            // Dump array back into list
            ListIterator it = list.listIterator();
            for (int i=0; i<arr.length; i++) {
                it.next();
                it.set(arr[i]);
            }
        }
    }
    
    publicstaticvoidshuffle(列表,随机rnd){
    int size=list.size();
    if(大小1;i--)
    掉期(清单,i-1,rnd.nextInt(i));
    }否则{
    对象arr[]=list.toArray();
    //洗牌阵列
    对于(int i=size;i>1;i--)
    掉期(arr,i-1,rnd.nextInt(i));
    //将数组转储回列表
    ListIterator it=list.ListIterator();
    对于(int i=0;i

    公共类卡{
    String name;//为了简单起见,只需要卡的名称
    公共卡(字符串名称){
    this.name=名称;
    }
    公共字符串getName(){return name;}
    }
    // ...
    列表组=新的ArrayList();
    initDeck();//向牌组添加大约52张新牌
    收藏。洗牌(牌组);//洗牌
    抽牌=牌组。获取(0);//抽第一张牌
    甲板。移除(绘制);//将其从甲板上移除
    
    发现



    您不希望循环中包含零。在
    for(int k=51;k>=0;)
    中,不幸的是,我必须以一种不使用内置洗牌算法作为赋值的一部分的方式来完成它。当我这样做时,程序会编译,但拒绝洗牌,这让我相信程序被困在无限循环中,因为
    public class Card {
      String name;   // for simplicity, just the card's name
      public Card(String name) {
        this.name = name;
      }
      public String getName() {return name;}
    }
    
    // ...
    
    List<Card> deck = new ArrayList<Card>();
    initDeck();  // adds like 52 new cards to the deck
    
    Collections.shuffle(deck);   // shuffle the deck
    Card drawn = deck.get(0);    // draw the first card
    deck.remove(drawn);          // remove it from the deck