【问题标题】:Optimize shuffling an ArrayList优化 ArrayList 的洗牌
【发布时间】:2023-04-07 10:09:01
【问题描述】:

问题是这样的:我需要洗牌(ArrayList 或 52 个整数的数组(0 到 51))。我还需要在 Android 处理器上执行 200,000 次。请帮我优化它,因为即使在更高端的设备上(最多 10 秒),这也会花费令人难以置信的时间。

我尝试过的方法:

1) 使用 Collections.shuffle 洗牌并抽出我得到的第一张牌(在 Nexus7 上需要 8 秒):

for(long i = 0; i < ITERATIONS; i++) { // ITERATIONS is 200,000
        ArrayList<Integer> fakeDeck = (ArrayList) deck.clone(); // deck is the sorted deck.
        Collections.shuffle(fakeDeck);
        int card1 = fakeDeck.get(0);
        int card2 = fakeDeck.get(1);
        int card3 = fakeDeck.get(2);
        int card4 = fakeDeck.get(3);
        int card5 = fakeDeck.get(4);
        // do something with cards.
}

2) 从未洗牌的牌组中随机挑选牌(更好一点,需要 5 秒):

for(long i = 0; i < ITERATIONS; i++) { // ITERATIONS is 200,000
        ArrayList<Integer> fakeDeck = (ArrayList) deck.clone(); // deck is the sorted deck.
        int card1 = pullCardFromDeck(fakeDeck);
        int card2 = pullCardFromDeck(fakeDeck);
        int card3 = pullCardFromDeck(fakeDeck);
        int card4 = pullCardFromDeck(fakeDeck);
        int card5 = pullCardFromDeck(fakeDeck);
        // do something with cards.
}

// pullCardFromDeck 是:

private int pullCardFromDeck(ArrayList<Integer> deck) {
    int randomNumber = new Random().nextInt(deck.size());
    int card = deck.get(randomNumber); // get a random card.
    deck.remove(randomNumber); // remove the card from the deck.
    return card;
}

【问题讨论】:

  • 真的有必要每次都克隆套牌吗?
  • 在第一个例子中它不是必需的,我可以重新洗牌相同的牌组,在第二个例子中我需要,因为我移除了卡片。但这确实不是问题,因为与改组相比,克隆需要的时间很少。
  • Randoms 制作起来并不便宜。将其移出循环并将其传递给您的方法。
  • I also need to do this 200,000 times and on an Android processor. 为什么?另外,为什么要洗牌?你不能在需要的时候随机抽卡吗?
  • “因为与改组相比,克隆花费的时间很少”您通过基准测试确定了这一点...对吗?

标签: java android optimization arraylist


【解决方案1】:

如果你只需要五张牌,你可以每次洗 5 张牌。这可能会快 20 倍。

public static void main(String... ignored) {
    int[] cards = new int[52];
    for (int i = 0; i < cards.length; i++) cards[i] = i;

    long start = System.currentTimeMillis();
    int runs = 1000000;
    for (int i = 0; i < runs; i++) {
        shuffleN(cards, 5);
        int card1 = cards[0], card2 = cards[1], card3 = cards[2], card4 = cards[3], card5 = cards[4];
    }
    long time = System.currentTimeMillis() - start;
    System.out.printf("Took %.3f seconds to shuffle %,d times%n", time / 1e3, runs);
}

private static final Random RND = new Random();

public static void shuffleN(int[] numbers, int count) {
    for (int i = 0; i < count; i++) {
        int r = RND.nextInt(numbers.length - i) + i;
        if (i == r) continue;
        int tmp = numbers[i];
        numbers[i] = numbers[r];
        numbers[r] = tmp;
    }
}

打印

Took 0.115 seconds to shuffle 1,000,000 times

【讨论】:

  • 我需要从 52 张牌组中抽 5 张牌。
  • @VM 因此,您将计数为 5,并且前 5 张卡片将是随机的,即使其余卡片不是非常随机的。即,这就像洗牌,但在前 5 张牌后停止,使其可能快 10 倍。
  • 我不明白,你不是每次都交换相同的5张卡吗?这将产生相同的结果。对不起,我很慢。
  • @VM 这是从整副牌中随机交换五张牌并将它们放在最上面。前五个将是随机的,其余的 47 个将是其余的。下一次,从 52 个中随机选择 5 个,以此类推
【解决方案2】:

您可能最好考虑使用 int[] 的索引,您可以洗牌并使用它来访问单个牌组。要获取随机条目,请将选择的索引设置为零(当你得到一个随机数时,看看值是否已经为零,如果是选择一张新卡)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    相关资源
    最近更新 更多