1

对于静态模式数据库(5-5-5),请参阅(第 290 和 283 页)或下面有说明。什么是 15 拼图?
我正在创建一个静态模式数据库(5-5-5)。此代码用于将条目填充到第一个表中。我是通过递归函数来做的insertInDB()。递归函数的第一个输入是这个(实际上输入拼图包含在一维数组中。为了更好地理解,我在下面将它表示为二维)


1 2 3 4
0 6 0 0
0 0 0 0
0 0 0 0


这是我的代码:

class DBClass
{
    public Connection connection;
     public ResultSet rs;
      public PreparedStatement ps1;
    public PreparedStatement ps2;
    public int k;
      String read_statement,insert_statement;

    public DBClass()
    {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            try {
                connection = DriverManager
                    .getConnection("jdbc:mysql://localhost/feedback?"
                        + "user=ashwin&password=ashwin&autoReconnect=true&useUnicode=true&characterEncoding=utf8&validationQuery=Select 1");
                insert_statement="insert into staticpdb1(hash,permutation,cost) values(?,?,?)";
                read_statement="select SQL_NO_CACHE * from staticpdb1 where hash=? and permutation= ? LIMIT 1";
                 ps1=connection.prepareStatement(read_statement, ResultSet.TYPE_SCROLL_SENSITIVE, 
                            ResultSet.CONCUR_UPDATABLE);
                ps2=connection.prepareStatement(insert_statement);
                k=0;
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
    public int updateIfNecessary(FifteenPuzzle sub) 
       {
           String str=sub.toDBString();
           try
           {

               ps1.setInt(1, sub.hashcode());
               ps1.setString(2,str);
               rs=ps1.executeQuery();
           if(rs.next())
              {
                  //if a row exists, check if the cost is greater than sub's
                  int cost=rs.getInt(3);
                  if(sub.g_n<cost)  //if the cost of sub is less than db row's cost
                  {
                      //replace the cost
                      rs.updateInt(3, sub.g_n);
                      rs.updateRow();
                      return 1;   //only examine - do not insert
                  }
                  else
                      return 0;   //dont examine - return

              }
           else
               return 2;      //insert and examine
           }
           catch(SQLException e)
           {

               System.out.println("here1"+e);
               System.err.println("reported recursion level was "+e.getStackTrace().length);
               return 0;
           }
           finally{

               try{
                   rs.close();}
               catch(final Exception e1)
               {
                   System.out.println("here2"+e1);
               }

           }


       }
    public void insert(FifteenPuzzle sub)
    {

        try{
        String str=sub.toDBString();


         ps2.setInt(1,sub.hashcode());
         ps2.setString(2, str);
         ps2.setInt(3,sub.g_n);
         ps2.executeUpdate();
         ps2.clearParameters();
        }catch(SQLException e)
        {
            System.out.println("here3"+e);
        }
    }

    public void InsertInDB(FifteenPuzzle sub) throws SQLException
       {

           System.out.println(k++);

           int i;

           int p=updateIfNecessary(sub);
          if(p==0)
          {
              System.out.println("returning");
           return;
          }
          if(p==2)
          {
          insert(sub);
          System.out.println("inserted");
          }


           //FifteenPuzzle temp=new FifteenPuzzle(sub.puzzle.clone(),2,sub.g_n);
           for(i=0;i<sub.puzzle.length;i++)
           {
               if(sub.puzzle[i]!=0)
               {

                   //check the positions it can be moved to
                   if(i%4!=0 && sub.puzzle[i-1]==0)  //left
                   {
                       //create another clone and increment the moves
                       FifteenPuzzle temp_inner=new FifteenPuzzle(sub.puzzle.clone(),2,sub.g_n+1);
                       //exchange positions
                        int t=temp_inner.puzzle[i];
                        temp_inner.puzzle[i]=temp_inner.puzzle[i-1];
                        temp_inner.puzzle[i-1]=t;
                        InsertInDB(temp_inner);
                   }
                   if(i%4!=3 && sub.puzzle[i+1]==0)  //right
                   {
                       //create another clone and increment the moves
                       FifteenPuzzle temp_inner=new FifteenPuzzle(sub.puzzle.clone(),2,sub.g_n+1);
                       //exchange positions
                        int t=temp_inner.puzzle[i];
                        temp_inner.puzzle[i]=temp_inner.puzzle[i+1];
                        temp_inner.puzzle[i+1]=t;
                        InsertInDB(temp_inner);
                   }
                   if(i/4!=0 && sub.puzzle[i-4]==0)  //up
                   {
                       //create another clone and increment the moves
                       FifteenPuzzle temp_inner=new FifteenPuzzle(sub.puzzle.clone(),2,sub.g_n+1);
                       //exchange positions
                        int t=temp_inner.puzzle[i];
                        temp_inner.puzzle[i]=temp_inner.puzzle[i-4];
                        temp_inner.puzzle[i-4]=t;
                        InsertInDB(temp_inner);
                   }
                   if(i/4!=3 && sub.puzzle[i+4]==0)  //down
                   {
                       //create another clone and increment the moves
                       FifteenPuzzle temp_inner=new FifteenPuzzle(sub.puzzle.clone(),2,sub.g_n+1);
                       //exchange positions
                        int t=temp_inner.puzzle[i];
                        temp_inner.puzzle[i]=temp_inner.puzzle[i+4];
                        temp_inner.puzzle[i+4]=t;
                        InsertInDB(temp_inner);

                  }
             }   
       }



类中的函数insertInDB(FifteenPuzzle fp)是递归函数,首先从主函数调用,其中十五个谜题参数的数组(puzzle是 Class 的整数数组字段FifteenPuzzle)为 - 1,2,3,4,0,6,0,0,0,0,0,0,0,0,0,0(与上面显示的矩阵相同) . 在解释其他功能之前,我将解释什么是静态模式数据库;简要地(因为下面的评论)

什么是 15-Puzzle 的 (5-5-5) 静态模式数据库?

模式数据库是用于解决 15 个谜题的启发式算法(可以是任何谜题。但这里我只讨论 15 个谜题)。启发式是用于确定接下来要扩展哪个状态的数字。我就像每个州的成本。这里的状态是 15-Puzzle 的排列。对于像 8-Puzzle 这样的简单谜题,启发式可以是manhattan distance。它为每个错放的瓷砖提供了最少的移动次数,以达到目标位置。然后将所有瓷砖的曼哈顿距离相加,得出该瓷砖的成本。曼哈顿距离给出了到达目标状态所需移动次数的估计下限,即您无法通过移动达到目标状态,小于曼哈顿距离。曼哈顿距离不是一个很好的启发式算法,虽然可以接受,因为它不考虑附近的其他瓷砖。如果必须将瓷砖移动到它的目标位置,则附近的瓷砖也必须移动,并且移动次数会增加。因此,显然对于这些谜题,实际成本大多远大于曼哈顿距离。克服
_这(曼哈顿距离)并考虑到其他瓷砖,引入了模式数据库。静态模式数据库保存子问题或一组图块的启发式方法,以达到其目标状态。由于您正在计算使这些图块组达到其目标状态的移动次数,因此在移动图块时将考虑该组中的其他图块。因此,这是一种更好的启发式方法,并且通常总是大于曼哈顿距离。
5-5-5静态模式只是静态模式数据库的一种形式,组数为3,其中两个包含5个tile,第三个包含6个(第6个是空白tile)。

其中一组是这个矩阵:

1 2 3 4
0 6 0 0
0 0 0 0
0 0 0 0


我正在计算该组的所有排列的启发式/number_of_moves 以达到上述配置并将它们插入到我的数据库中。
可能的组合总数(也是db中的行数)是

16!/(16-5)! = 524160


现在,其他函数——updateIfNecessary(FifteenPuzzle)这个函数检查传递的FifteenPuzzle 对象的数组是否已经存在于数据库中。如果数据库中已经存在,它会检查当前对象的成本是否小于数据库中的成本。如果是,它将用当前成本替换它,否则什么也不做。该函数 -insert(FifteenPuzzle)插入一个带有成本的新排列。

注意: fifteenuzzle.g_n是拼图的成本。对于表示上述矩阵的初始拼图,成本为0,每一步的成本为incremented by1

对于运行配置中的堆栈大小,我已将堆栈大小设置为 - Xss128m(1024、512 和 256 给出了致命错误)。
目前递归数或深度是7,500,000和计数(的值System.out.println(k++);)。
可能的组合总数是

16!/(16-5)! = 524160


但深度已经达到了七百五十万。这是因为产生了重复状态。目前数据库中的条目数为513423。您可能认为现在只有 10,000 个条目需要填写。但是现在进入的速度已经急剧下降,大约每 30 分钟就有 1 次进入。这永远不会过去。

我需要一个实用的解决方案——有或没有递归。是否可以?

4

5 回答 5

2

重要的部分是第一行:java.lang.StackOverflowError. 递归对堆栈要求很高。

尝试仅递归地执行算法部分,同时将数据库访问置于额外的方法中。

于 2012-11-05T10:08:06.950 回答
2

似乎您正在移动块以获取所有排列。然后,检查每个排列是否存在于 DB 中;如果是,则在必要时更新移动次数。

它会生成一棵树。您正在以 DFS 样式生成它(通过递归调用)。如果您以 BFS 方式进行操作,那么您将始终获得最少的移动次数。稍后生成的重复状态总是需要更大的移动。因此,您不必在数据库中进行比较。

在以下示例中,我们将移动6,然后我们会看到移动的数量。

Priority: Left, Right, Up, Down (as you gave)

DFS 风格

1 2 3 4    1 2 3 4
0 6 0 0    6 0 0 0
0 0 0 0    0 0 0 0
0 0 0 0    0 0 0 0
  (0)        (1)

到达最左边的位置。现在,检查向右移动(从它的来源)。该位置已经在数据库中,所以继续。更何况,也上不去。于是往下走。

1 2 3 4    1 2 3 4
0 0 0 0    0 0 0 0
6 0 0 0    0 0 0 0
0 0 0 0    6 0 0 0
  (2)        (3)

现在,向右走

           State-1    State-2    State-3
1 2 3 4    1 2 3 4    1 2 3 4    1 2 3 4
0 0 0 0    0 0 0 0    0 0 0 0    0 0 0 0
0 0 0 0    0 0 0 0    0 0 0 0    0 0 0 0
6 0 0 0    0 6 0 0    0 0 6 0    0 0 0 6
  (3)        (4)        (5)        (6)

在这里您可以看到,State-1只需 2 步(不是 4 步)即可到达。但这将在稍后公布,我们必须更新数据库。因此,显然这是浪费精力。

BFS 风格

1 2 3 4    1 2 3 4
0 6 0 0    6 0 0 0
0 0 0 0    0 0 0 0
0 0 0 0    0 0 0 0
  (0)        (1)

到达最左边的位置,现在向右走

1 2 3 4    1 2 3 4
0 0 6 0    0 0 0 0
0 0 0 0    0 6 0 0
0 0 0 0    0 0 0 0
  (1)        (1)

您可以将其视为6均匀分布所有面。在这里,我们也会有重复的状态,但这些状态需要比数据库的第一个条目更大的最小移动。

您可以使用一个简单的队列来实现这一点。

伪代码

Initialize no_of_moves by 0
enqueue(startingPosition)
insertIntoDB(startingPattern, no_of_moves)
while((currentPattern = dequeue()) is not null)
    if(currentPattern is not already traversed)
        insertIntoDB(currentPattern);
        list<Pattern> allPatterns = patterns which can be reached from currentPattern in just 1 move
        no_of_moves++
        enqueue(allPatterns, no_of_moves)
    end if
end while

除了从数据库中检查状态之外,还有很多方法可以检查状态是否已经被遍历。我正在考虑散列,但无法提出。

您可以维护从模式字符串(例如 )映射的布尔列表traversed["1234060000000000"] = true or false。我不认为在主内存中存储 524160 个条目会产生任何问题。

于 2012-11-16T15:29:39.923 回答
1

你不应该调用递归。这样做会在您的内存中的堆栈上放置另一个地址,如果这种情况经常发生,您会耗尽内存,这发生在您的情况下:StackOverflowError
尝试创建一个方法,让您输入一次数据,然后在循环中调用该方法,直到所有数据都保存在您的数据库中。

于 2012-11-05T10:07:09.373 回答
1

PreparedStatement在每个方法调用中创建新对象,这里是递归方法。前任。ps=connection.prepareStatement(read_statement);ps=connection.prepareStatement(insert_statement);。为两者创建两个单独的 PreparedStatement 对象,并将它们移出方法并ps.clearParameters();在方法的开头调用(对于两个对象)。有了它,您只需要处理两个对象,在这里您要创建数千个对象。然后在不再需要时关闭资源。(即,之前FifteenPuzzle temp=new FifteenPuzzle(sub.puzzle.clone(),2,sub.g_n);

于 2012-11-05T10:44:43.267 回答
0

我猜你的问题与资源管理不善有关,而不是递归本身。此外,您正在尝试使您的实现变得比您需要的更复杂。我建议如下:

  • 创建一个将数据插入/更新到数据库的方法。在这里管理您的资源。这不必是递归的。
  • 从您的递归方法中,调用此方法将数据插入/更新到您的数据库。

这更干净,因为您知道数据库特定方法会在返回递归之前关闭所有资源。但是,我建议您在调用此方法时重用您的连接对象。

此外,请确保关闭您的语句、结果集甚至您的连接作为 finally 块的一部分。对于初学者来说,我可以看到的一个问题是:

  • 您试图在 try-block 或 if-block 中关闭您的preparedstatement。如果您没有点击 if 块并且在有机会关闭资源之前遇到了异常怎么办?你的 PreparedStatement 和其他资源永远不会关闭。
于 2012-11-05T10:04:42.163 回答