【问题标题】:Bulgarian Solitaire Java保加利亚纸牌爪哇
【发布时间】: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

标签: java algorithm solver


【解决方案1】:
    package learningofAlgorithm;
    import java.util.ArrayList;
    import java.util.Random;
    import java.util.Collections;
    public class bulgarianSolitaire {
    private Random gen;
    private int i=1;
    ArrayList<Integer> bs=new ArrayList<>();


    public bulgarianSolitaire(){
       /**gen=new Random();
       bs.add(gen.nextInt(45)+1);
       int sum=bs.get(0);
       while(sum!=45){
            bs.add(gen.nextInt(45-sum)+1);
            sum=sum+bs.get(i);
            i++;

        }*/
        bs.add(20);
        bs.add(5);
        bs.add(1);
        bs.add(9);
        bs.add(10);
    }

    public void dis(){
        for(Integer element:bs){
            System.out.print(element+" ");
        }
    }


    public void removeCard(ArrayList<Integer> s){
         int i=0;
        while(i<s.size()){
            if(s.get(i)==0){
                s.remove(i);
            }
             else{
                i++;
            }
        }

    }


     public void bsstep(){
         int newheap=0;
         for(int i=0;i<bs.size();i++){
             int key=bs.get(i);
                 bs.set(i, key-1);

             newheap++;

         }
        removeCard(bs);

         bs.add(newheap);   
    }


    public boolean checkCard()
    {   ArrayList<Integer> bscheck=new ArrayList<>();
        for(int i=1;i<10;i++){
        bscheck.add(i);
          }
        boolean flag=true;
        ArrayList<Integer> sortbs=bs;
        Collections.sort(sortbs);
        if(bscheck.size()!=sortbs.size()){
            flag=false;
        }

     for(int i=0;i<bscheck.size();i++){
         if(bscheck.size()==sortbs.size()){
              if(bscheck.get(i)!=sortbs.get(i)){
                  flag=false;
              }
          }
        }
        return flag;

    }


    }

【讨论】:

  • 亲爱的我已经尝试了 removeCard 方法的 while 循环,它可以工作。
【解决方案2】:

我已经尝试了上面的代码,但它不起作用。 我编写了一个新代码,它可以工作:

import java.util.ArrayList;
import java.util.Collections;


public class P74_BulgarianSolitaire {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    final int MAX_CARD = 45; 
    ArrayList<ArrayList<Integer>> twoDArrayList =  new ArrayList<ArrayList<Integer>>();

    int column = 1; 
    int card = 0;
    int left = MAX_CARD;


    do{ 
        column = (int) (Math.random()*(left)+1);  // 
        System.out.println("Column :"+column);

        ArrayList<Integer> row = new ArrayList<Integer>(); //New row to add. Must declare here. 
        for (int j=0;j<column;j++){
            card++;

            row.add(card);
        }

        twoDArrayList.add(row);

        left = MAX_CARD - card ;


    } while (card <MAX_CARD);
    System.out.println(twoDArrayList);
    System.out.println();

    boolean finish = false;

    while (!finish){
        ArrayList<Integer> row = new ArrayList<Integer>(); //New row

        //remove one card from each row
        for (int i=0;i<twoDArrayList.size();i++){
            row.add(twoDArrayList.get(i).get(0));
            twoDArrayList.get(i).remove(0);  //Remove the first column 
            if (twoDArrayList.get(i).isEmpty()){
                twoDArrayList.remove(twoDArrayList.get(i)); 
                i--;
            }
        }

        twoDArrayList.add(row);

        ArrayList<Integer> size = new ArrayList<Integer>(); // New list
        for (int i=0;i<twoDArrayList.size();i++){
            size.add(twoDArrayList.get(i).size());
        }
        Collections.sort(size);

        for (int i=1;i<size.size();i++){
            if (size.get(i)== size.get(i-1)+1 ) finish = true; 
            else {
                finish = false;
                break;
            }
        }

        for (int i=0;i<twoDArrayList.size();i++) Collections.sort(twoDArrayList.get(i));
        System.out.println(twoDArrayList);
    }
    System.out.println(twoDArrayList);

}

}

【讨论】:

    【解决方案3】:

    你的缺陷在于你的removeZeros 方法。

    public void removeZeros(ArrayList<Integer> list) {
        for (int j = 0; j < list.size(); j++) {
            if (list.get(j) == 0) {
                list.remove(j);
            }
        }
    }
    

    如果您删除j 处的元素,则列表大小会减少1。您也必须减少j

    为此更改:

      public void removeZeros(ArrayList<Integer> list) {
            for (int j = 0; j < list.size(); j++) {
                if (list.get(j) == 0) {
                    list.remove(j);
                    j--;
                }
            }
        }
    

    您的检查方法也过于复杂。

    在您的单人纸牌步骤中,将所有应该等于零的值设置为零。

    然后,删除零(使用修改后的方法)循环外

    然后,对数组进行排序。

    然后,在您的检查方法中,由于数组已排序:

    public boolean checkCards() {
        for(int i = 0; i < cards.size(); i++) {
            if(cards.get(i) != i + 1) {
                return false;
            }
        }
        return true;
    }
    

    简单得多。它有效。

    【讨论】:

    • 不应该是 2, 2, 3, 3, 4, 17, 6, 8 而不是 2, 2, 3, 3, 4, 17, 8 只是为了清楚我们在这里做同样的事情吗?
    • 哦!嘿看那个!感谢帮助,我一定是刷新太早了。