【发布时间】:2026-01-04 10:25:06
【问题描述】:
我的 AP 计算机科学课程几天后要完成以下作业:
“在本作业中,您将对保加利亚纸牌游戏进行建模。游戏从 45 张牌开始。将它们随机分成若干随机大小的牌堆。例如,您可以从尺寸为 20、5、1 的牌堆开始, 9 和 10。在每一轮中,您从每一堆中取出一张牌,用这些牌组成一个新牌堆。例如,示例起始配置将转换为大小为 19、4、8、10 和 5 的牌堆。当纸牌的大小按某种顺序排列为 1、2、3、4、5、6、7、8 和 9 时,纸牌就结束了。
在您的程序中,生成一个随机启动配置并打印出来。然后继续应用纸牌步骤并打印结果。达到纸牌最终配置时停止。”
我想出了一个解决这个问题的程序,但问题是有时需要很长时间。其他时候它会像我预期的那样几乎立即解决它,但其他时候它可以迭代 18,000 次或更多。
根据http://mancala.wikia.com/wiki/Bulgarian_Solitaire,可以在 (k^2)-k 步或更短的时间内找到解决方案,在这种情况下 k 为 9。很多时候,我肯定不会在 72 步或更少的时间内找到解决方案。我已经研究这个程序好几个小时了,为了看看我是否可以让它更快,我无法让它在足够多的迭代中工作。所以现在我来到 Stack Overflow,看看你们中是否有人可以帮助我朝着正确的方向前进。
这是我的代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class BulgarianSolitaire {
ArrayList<Integer> cards = new ArrayList<Integer>();
Random rand = new Random();
boolean cont = true;
boolean cont2 = true;
public static void main(String[] args) {
BulgarianSolitaire game = new BulgarianSolitaire();
}
public BulgarianSolitaire() {
System.out.println("Started");
int sum = 0;
while (cont) {
if (sum < 45) {
cards.add(rand.nextInt(46 - sum));
} else {
cont = false;
}
sum = 0;
for (int i = 0; i < cards.size(); i++) {
sum += cards.get(i);
}
removeZeros(cards);
System.out.println(cards);
}
System.out.println("Finished Generating Start");
while (cont2) {
solitaireStep();
System.out.println(cards);
if (checkCards()) {
cont2 = false;
}
}
Collections.sort(cards);
System.out.println("Cards are sorted");
System.out.println(cards);
}
public void removeZeros(ArrayList<Integer> list) {
for (int j = 0; j < list.size(); j++) {
if (list.get(j) == 0) {
list.remove(j);
}
}
}
public void solitaireStep() {
int numberRemoved = 0;
for (int i = 0; i < cards.size(); i++) {
int value = cards.get(i);
cards.set(i, value - 1);
removeZeros(cards);
numberRemoved++;
}
cards.add(numberRemoved);
}
public boolean checkCards() {
ArrayList<Integer> expectedCards = new ArrayList<Integer>();
for (int i = 1; i < 10; i++) {
expectedCards.add(i);
}
ArrayList<Integer> sortedCards = cards;
Collections.sort(sortedCards);
boolean equal = true;
if (sortedCards.size() != expectedCards.size()) {
equal = false;
}
for (int i = 0; i < sortedCards.size(); i++) {
if (sortedCards.size() == expectedCards.size()) {
if (sortedCards.get(i) != expectedCards.get(i)) {
equal = false;
}
}
}
return equal;
}
}
所以我基本上首先生成一个介于 0 到 45 之间的随机数,然后将其添加到卡片列表中。然后我继续生成随机数并将它们放入列表中,只要总和小于 45,这样生成的随机数就在 0 到 45 之间——这是最后一次迭代中数字的总和。列表中的零也被删除。
一旦生成了列表,它将执行以下步骤:从列表中的每个数字中减去 1、删除零并添加一个与减少的堆栈数相等的新值。它还根据列表 {1, 2, 3, 4, 5, 6, 7, 8, 9} 检查卡片堆栈的有序版本,一旦找到匹配项,它将布尔值 cont2 设置为 false,以便它将停止执行单人纸牌步骤。
就是这样。我感谢任何可以提供帮助的人。
【问题讨论】:
-
我不明白你的例子。不应该将
20, 5, 1, 9, and 10桩转换为19, 4, 8, 9, and 5桩而不是19, 4, 8, 10, and 5桩吗? -
这正是我对新创建桩的想法(
10应该是9,因为一张卡片被转移到新的5桩中) -
哦,是的,你是对的。似乎可靠的旧教科书有某种错字!有趣...但是是的,应该是 19、4、8、9 和 5,对此感到抱歉。
-
你能给我们举一个输入的例子,让你的程序运行太多迭代吗?
-
当然,这是我记录的几个输出的简短列表,以及它们是否在前几秒钟内解决。
7,7,21,6,4 - No35, 1, 9 - Yes12, 13, 3, 16, 1 - No10, 5, 23, 6, 1 - Yes13, 3, 29 - Yes36, 6, 2, 1 - Yes21, 24 - Yes8, 1, 30, 5, 1 - Yes