【问题标题】:Checking for an integer in array检查数组中的整数
【发布时间】:2025-12-25 01:40:06
【问题描述】:

我在大学里有一个练习要做,其中一部分是制作一副卡片,然后必须洗牌。我将卡片放在一个数组中(未洗牌),并想将它们洗牌并将它们推到自制的堆栈上,我可以从中弹出卡片来处理它们。

我的问题是我想检查我生成的随机数(代表数组中的一张牌)是否已经在堆栈中。通过阅读这个论坛,我想出了下面的代码,在我看来它应该可以工作。在调试时,虽然我注意到重复。

根据我下面的评论,我们不能使用 Collections 框架(编辑)

private Stack<Card> deck;//to hold cards
private Card[] protoDeck;//to hold cards before shuffling
private Random randomer;

private int cardsDealt;//how many cards used. Used for other methods
private static final int TOTALCARDS = 52;//sets limit of deck for all decks

public void shuffle(){//remove cards from deck and put back in random order
    randomer = new Random();
    int[] temp = new int[TOTALCARDS];//to keep track of random numbers
    int rand = 0;

    for (int i = 0; i < temp.length ; i++) {
        do {//keep creating randoms if 
            rand = randomer.nextInt(TOTALCARDS);
            deck.push(protoDeck[rand]);//puts the Card onto the Deck in a random position
            temp[i] = rand;
        } while (!(Arrays.asList(temp).contains(rand)));//check if the number already used  
    }
}

@PeterLawrey 我已经对代码进行了如下微调,因为我只需要洗牌整副牌,而且效果很好,我会从堆栈中弹出卡片来处理


public void shuffle() {
    randomer = new Random();
    for(int i = 0; i < TOTALCARDS; i++) {
        // pick a random card from the rest of the deck
        int j = randomer.nextInt(protoDeck.length - i) + i;
        // swap cards
        Card tmp = protoDeck[i];
        protoDeck[i] = protoDeck[j];
        protoDeck[j] = tmp;
        deck.push(protoDeck[i]);
    }

}

感谢彼得和所有其他贡献者。 M.

【问题讨论】:

  • 对不起,我应该说我们不能使用集合框架。
  • 为避免重复,请在推送前检查deck.contains();这应该显示卡片是否已经在洗牌的桌子上。
  • 不使用 Collections.shuffle,你可以使用相同的代码。您可以优化代码以随机挑选 N 张牌,而不是洗牌。
  • +1 表示努力并向我们展示代码

标签: java arrays random


【解决方案1】:

开始

private final Card[] deck;//to hold cards before shuffling
private final Random rand = new Random();

你可以的

public void shuffle() {
    // no need the shuffle the last card.
    shuffle(deck.length - 1);
}

// will leave the first N card random without duplicates.
public void shuffle(int numberOfCards) {
    for(int i = 0; i < numberOfCards; i++) {
        // pick a random card from the rest of the deck
        int j = rand.nextInt(protoDeck.length - i) + i;
        // swap cards
        Card tmp = deck[i];
        deck[i] = deck[j];
        deck[j] = tmp;
    }
}

成本是 O(N),其中 N 是随机卡片的数量。


想象一下你有一个像这样的小甲板

AS AC AD AH 2S 2C 2D 2H

你需要随机选择第一张牌,从牌组中选择一张并交换那张牌。说 nextInt() 是 5 => 2C

2C | AC AD AH 2S AS 2D 2H

桌子由随机​​选择+未选择的卡片组成。您没有重复,因为相同的卡片会四处移动。下一张随机牌是 2H 与 AC 交换

2C 2H | AD AH 2S AS 2D AC

最后恰好选择了AD。

2C 2H AD | AH 2S AS 2D AC

这会给你三张随机牌,其余的。可以再次使用相同的数组,因为从排序或随机的牌组开始不会使结果或多或少随机。


回复Why does this simple shuffle algorithm produce biased results?的回答如果有123个,可能的结果是

123
 +- 123          - swap 1 and 1 (these are positions, not numbers)
 |   +- 123      - swap 2 and 2
 |   +- 132      - swap 2 and 3
 +- 213          - swap 1 and 2
 |   +- 213      - swap 2 and 2
 |   +- 231      - swap 2 and 3
 +- 321          - swap 1 and 3
     +- 321      - swap 2 and 2
     +- 312      - swap 2 and 3

如您所见,只有 6 种可能的结果,所有可能性都相同。

【讨论】:

  • +1 表示不使用额外内存的解决方案;)
  • @Peter Lawrey:对不起,我认为这行不通。 int j = rand.nextInt(protoDeck.length - i) + i;此行可以为您每次运行提供 1,因为 nextInt 的参数只是一个上限,因此数字不会是唯一的。
  • @user2424380 这个数字不需要。想象一下,您正在从一副牌中随机选择一张牌,而不是替换它。如果您选择另一张卡片,它不会重复,因为您不是从已选择的卡片中选择。想象一下,您每次只选择第一张牌,而 nextInt() 每次都为零。您将选择 0, 1, 2, 3, 4, 5 ... 卡。它仍然是独一无二的。
  • @user2424380 只要偏向于赌场,我相信他们不会介意。我已经告诉(并证明)给我的孩子们为什么他们不应该为了钱玩二十一点……
  • @user2424380 我已使用您提供的链接中的答案来探索 1 2 3 的所有可能结果
【解决方案2】:

实际上 Stack 扩展了 Vector。所以你可以使用 contains 方法来检查已经存在的元素是否存在。

【讨论】:

  • 这是一个自制的堆栈,所以我希望不必从头开始创建所有方法。不过好点,我会看看在我自制的方法中制作 contains() 方法。
【解决方案3】:

原来的问题是Arrays.asList(temp)创建的不是List&lt;Integer&gt;而是List&lt;int[]&gt;

因此Arrays.asList(temp).contains(rand) 总是返回false

如果您使用包装类Integer (Integer[] temp = new Integer[TOTALCARDS];),它会起作用,但您的方法效率非常低,因为您必须在集合中生成一个“随机”数,每次迭代都会减少。

一种方法是创建一个包含位置 0 到 51 的数组,将其洗牌,然后遍历它以将牌推入牌组。

public void shuffle(){//remove cards from deck and put back in random order
    int[] temp = new int[TOTALCARDS];//to keep track of random numbers
    for(int i = 0; i < temp.length; i++){
        temp[i] = i;
    }

    //shuffle the array

    for (int i = 0; i < temp.length ; i++) {
        deck.push(protoDeck[temp[i]]);             
    }
}

这种方法在 O(n) 时间内运行。

【讨论】:

  • 与其打乱数组或int[],不如直接打乱Card[]
  • @PeterLawrey 是的,如果他不想保留“排序”的卡片数组,那么您的选择绝对是最好的。
  • @PeterLawrey 以便原始 Card[] protoDeck 包含 4 批随机顺序的 13 张卡片?这将删除一个步骤,我可以在每次需要洗牌时创建一个新牌组。就像二十一点一样,我只需要在所有牌都用完或有二十一点的情况下洗牌。我一定会试试这个并回复你。
  • @ZouZou 谢谢,这是有道理的。尽管List&lt;Card&gt; tempCards = Arrays.asList(protoDeck); 这似乎可行
【解决方案4】:

也许我听错了,但我认为你的概念很糟糕。

  1. 生成随机数
  2. 将其放入新牌组
  3. 检查它是否已经存在

对我来说,这听起来像是一个错误的顺序。另一方面,如果你想要独特的元素,使用 Set 总是有效的。

【讨论】:

    【解决方案5】:

    正如@ZouZou 所述,Arrays.asList(temp) 返回一个List&lt;int[]&gt;。您这样做可能是因为answer 带来的混乱(顺便说一句,这是错误的)。这个answer 展示了一种使用二进制搜索的方法:

    public boolean contains(final int[] array, final int key) 
    {  
         Arrays.sort(array);  
         return Arrays.binarySearch(array, key) >= 0;  
    }  
    

    这会修改传入的数组。您可以选择复制数组并处理原始数组,即int[] sorted = array.clone(); 但这只是短代码的一个示例。运行时是O(NlogN)

    根据 Java 文档,binarySearch 将返回以下之一(我强调了重点):

    搜索键的索引,如果它包含在列表中;否则,(-(插入点) - 1)插入点被定义为键将被插入到列表中的点:第一个大于键的元素的索引,或者 list.size() 如果所有元素都在该列表小于指定的键。请注意,这保证了当且仅当找到键时返回值将 >= 0。

    【讨论】:

    • 如果此方法计划在元素在数组中时返回 true,则必须执行 return Arrays.binarySearch(array, key) &gt;= 0;。如文档所述,“搜索键的索引,如果它包含在数组中;否则,(-(insertion point) - 1)。插入点定义为将插入键的点数组”
    • @ZouZou:你说得对,让我编辑。我又读了一遍文档