【问题标题】:How to parallelize Sudoku solver using Grand Central Dispatch?如何使用 Grand Central Dispatch 并行化数独求解器?
【发布时间】:2010-06-21 23:08:45
【问题描述】:

作为一个编程练习,我刚刚完成了一个使用回溯算法的数独求解器(请参阅Wikipedia 以获取用 C 编写的简单示例)。

为了更进一步,我想使用 Snow Leopard 的 GCD 将其并行化,以便它在我机器的所有内核上运行。有人可以给我一些关于我应该如何去做以及我应该做哪些代码更改的指示吗?谢谢!

马特

【问题讨论】:

    标签: multithreading osx-snow-leopard parallel-processing grand-central-dispatch


    【解决方案1】:

    如果您最终使用它,请告诉我。它是标准的 ANSI C,所以应该在所有东西上运行。用法见其他帖子。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    short sudoku[9][9];
    unsigned long long cubeSolutions=0;
    void* cubeValues[10];
    const unsigned char oneLookup[64] = {0x8b, 0x80, 0, 0x80, 0, 0, 0, 0x80, 0, 0,0,0,0,0,0, 0x80, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80,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};
    
    int ifOne(int val) {
      if ( oneLookup[(val-1) >> 3] & (1 << ((val-1) & 0x7))  )
        return val;
      return 0;
    }
    
    
    void init_sudoku() {
      int i,j;
      for (i=0; i<9; i++)
        for (j=0; j<9; j++)
          sudoku[i][j]=0x1ff;
    }
    
    void set_sudoku( char* initialValues) {
      int i;
      if ( strlen (initialValues) !=  81 ) {
        printf("Error: inputString should have length=81, length is %2.2d\n", strlen(initialValues) );
        exit (-12);
      }
      for (i=0; i < 81; i++)
        if ((initialValues[i] > 0x30) && (initialValues[i] <= 0x3a))
          sudoku[i/9][i%9] = 1 << (initialValues[i] - 0x31) ;
    }
    
    void print_sudoku ( int style ) {
      int i, j, k;
      for (i=0; i < 9; i++) {
        for (j=0; j < 9; j++) {
          if ( ifOne(sudoku[i][j]) || !style) {
            for (k=0; k < 9; k++)
              if (sudoku[i][j] & 1<<k)
                printf("%d", k+1);
          } else
            printf("*");
          if ( !((j+1)%3) )
            printf("\t");
          else
            printf(",");
        }
        printf("\n");
        if (!((i+1) % 3) )
          printf("\n");
      }
    }
    
    void print_HTML_sudoku () {
      int i, j, k, l, m;
      printf("<TABLE>\n");
      for (i=0; i<3; i++) {
        printf("  <TR>\n");
        for (j=0; j<3; j++) {
          printf("    <TD><TABLE>\n");
          for (l=0; l<3; l++) { printf("      <TR>"); for (m=0; m<3; m++) { printf("<TD>"); for (k=0; k < 9; k++)  { if (sudoku[i*3+l][j*3+m] & 1<<k)
                printf("%d", k+1);
              }
              printf("</TD>");
            }
            printf("</TR>\n");
          }
        printf("    </TABLE></TD>\n");
        }
        printf("  </TR>\n");
      }
      printf("</TABLE>");
    }
    
    
    
    int doRow () {
      int count=0, new_value, row_value, i, j;
      for (i=0; i<9; i++) {
        row_value=0x1ff;
        for (j=0; j<9; j++)
          row_value&=~ifOne(sudoku[i][j]);
        for (j=0; j<9; j++) {
          new_value=sudoku[i][j] & row_value;
          if (new_value && (new_value != sudoku[i][j]) ) {
            count++;
            sudoku[i][j] = new_value;
          }
        }
      }
      return count;
    }
    
    int doCol () {
      int count=0, new_value, col_value, i, j;
      for (i=0; i<9; i++) {
        col_value=0x1ff;
        for (j=0; j<9; j++)
          col_value&=~ifOne(sudoku[j][i]);
        for (j=0; j<9; j++) {
          new_value=sudoku[j][i] & col_value;
          if (new_value && (new_value != sudoku[j][i]) ) {
            count++;
            sudoku[j][i] = new_value;
          }
        }
      }
      return count;
    }
    
    int doCube () {
      int count=0, new_value, cube_value, i, j, l, m;
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          cube_value=0x1ff;
          for (l=0; l<3; l++)
            for (m=0; m<3; m++)
              cube_value&=~ifOne(sudoku[i*3+l][j*3+m]);
          for (l=0; l<3; l++)
            for (m=0; m<3; m++) {
              new_value=sudoku[i*3+l][j*3+m] & cube_value;
              if (new_value && (new_value != sudoku[i*3+l][j*3+m]) ) {
                count++;
                sudoku[i*3+l][j*3+m] = new_value;
              }
            }
        }
      return count;
    }
    
    #define FALSE -1
    #define TRUE 1
    #define INCOMPLETE 0
    
    int validCube () {
      int i, j, l, m, r, c;
      int pigeon;
      int solved=TRUE;
    
      //check horizontal
      for (i=0; i<9; i++) {
        pigeon=0;
        for (j=0; j<9; j++)
          if (ifOne(sudoku[i][j])) {
            if (pigeon & sudoku[i][j]) return FALSE;
            pigeon |= sudoku[i][j];
          } else {
            solved=INCOMPLETE;
          }
      }
    
      //check vertical
      for (i=0; i<9; i++) {
        pigeon=0;
        for (j=0; j<9; j++)
          if (ifOne(sudoku[j][i])) {
            if (pigeon & sudoku[j][i]) return FALSE;
            pigeon |= sudoku[j][i];
          }
          else {
            solved=INCOMPLETE;
          }
      }
    
      //check cube
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          pigeon=0;
          r=j*3; c=i*3;
          for (l=0; l<3; l++)
            for (m=0; m<3; m++)
            if (ifOne(sudoku[r+l][c+m])) {
              if (pigeon & sudoku[r+l][c+m]) return FALSE;
              pigeon |= sudoku[r+l][c+m];
            }
            else {
              solved=INCOMPLETE;
            }
        }
    
      return solved;
    }
    
    int solveSudoku(int position ) {
      int status, i, k;
      short oldCube[9][9];
    
      for (i=position; i < 81; i++) {
    
        while ( doCube() + doRow() + doCol() );
    
        status = validCube() ;
        if ((status == TRUE) || (status == FALSE))
          return status;
    
    
        if ((status == INCOMPLETE) && !ifOne(sudoku[i/9][i%9]) ) {
          memcpy( &oldCube, &sudoku, sizeof(short) * 81) ;
          for (k=0; k < 9; k++) {
            if ( sudoku[i/9][i%9] & (1<<k) ) {
              sudoku[i/9][i%9] = 1 << k ;
              if (solveSudoku(i+1) == TRUE ) {
    
                /* return TRUE; */
                /* Or look for entire set of solutions */
    
                if (cubeSolutions < 10) {
                  cubeValues[cubeSolutions] = malloc ( sizeof(short) * 81 ) ;
                  memcpy( cubeValues[cubeSolutions], &sudoku, sizeof(short) * 81) ;
                }
    
                cubeSolutions++;
                if ((cubeSolutions & 0x3ffff) == 0x3ffff ) {
                  printf ("cubeSolutions = %llx\n", cubeSolutions+1 );
                }
    
                //if ( cubeSolutions > 10 ) 
                //    return TRUE;
    
              }
    
              memcpy( &sudoku, &oldCube, sizeof(short) * 81) ;
            }
            if (k==8)
              return FALSE;
          }
    
        }
      }
    
      return FALSE;
    }
    
    
    int main ( int argc, char** argv)  {
      int i;
      if (argc != 2) {
        printf("Error: number of arguments on command line is incorrect\n");
        exit (-12);
      }
    
      init_sudoku();
      set_sudoku(argv[1]);
    
      printf("[----------------------- Input  Data ------------------------]\n\n");
      print_sudoku(1);
    
      solveSudoku(0);
      if ((validCube()==1) && !cubeSolutions)  {
        // If sudoku is effectively already solved, cubeSolutions will not be set
        printf ("\n  This is a trivial sudoku. \n\n");
        print_sudoku(1);
      }
    
    
      if (!cubeSolutions && validCube()!=1)
        printf("Not Solvable\n");
      if (cubeSolutions > 1) {
        if (cubeSolutions >= 10)
          printf("10+ Solutions, returning first 10 (%lld) [%llx] \n", cubeSolutions, cubeSolutions);
        else
          printf("%llx Solutions. \n", cubeSolutions);
      }
    
      for (i=0; (i < cubeSolutions) && (i < 10); i++) {
        memcpy ( &sudoku, cubeValues[i], sizeof(short) * 81 );
        printf("[----------------------- Solution %2.2d ------------------------]\n\n", i+1);
        print_sudoku(0);
        //print_HTML_sudoku();
      }
      return 0;
    }
    

    【讨论】:

      【解决方案2】:

      首先,由于回溯是一种深度优先搜索,它不能直接并行化,因为任何新计算的结果都不能被另一个线程直接使用。相反,您必须尽早划分问题,即线程 #1 从回溯图中节点的第一个组合开始,然后继续搜索该子图的其余部分。线程#2 从第一个可能的组合开始,依此类推。简而言之,对于 n 个线程,在搜索空间的顶层找到 n 个可能的组合(不要“向前跟踪”),然后分配这些 n n 个线程的起点。

      但我认为这个想法存在根本缺陷:许多数独排列只需几千个前向+后退步骤即可解决,并在单个线程上在几毫秒内解决。这实际上是如此之快,以至于在多线程上,即使是几个线程所需的小协调(假设 n 个线程将计算时间减少到原始时间的 1/n)与总运行时间相比,核心/多 CPU 不可忽略,因此它绝不是一个更有效的解决方案。

      【讨论】:

      • 谢谢!我想我会坚持我目前的做法!
      【解决方案3】:

      您确定要这样做吗?比如,你想解决什么问题?如果要使用所有内核,请使用线程。如果你想要一个快速的数独求解器,我可以给你一个我写的,见下面的输出。如果您想为自己工作,请继续使用 GCD ;)。

      更新

      我不认为 GCD 不好,只是它与解决数独的任务没有太大关系。 GCD 是一种将 GUI 事件与代码联系起来的技术。从本质上讲,GCD 解决了两个问题,一个是 MacOS X 如何更新窗口的怪癖,另一个是它提供了一种改进的方法(与线程相比)将代码绑定到 GUI 事件。

      它不适用于这个问题,因为数独的解决速度比人们想象的要快得多(以我的拙见)。话虽如此,如果您的目标是更快地解决数独问题,您会想要使用线程,因为您会想要直接使用多个处理器。

      [bear@bear scripts]$ time ./a.out ..1..4.......6.3.5...9.....8.....7.3.......285...7.6..3...8...6..92......4...1... 
      [----------------------- Input  Data ------------------------]
      
      *,*,1   *,*,4   *,*,*   
      *,*,*   *,6,*   3,*,5   
      *,*,*   9,*,*   *,*,*   
      
      8,*,*   *,*,*   7,*,3   
      *,*,*   *,*,*   *,2,8   
      5,*,*   *,7,*   6,*,*   
      
      3,*,*   *,8,*   *,*,6   
      *,*,9   2,*,*   *,*,*   
      *,4,*   *,*,1   *,*,*   
      
      [----------------------- Solution 01 ------------------------]
      
      7,6,1   3,5,4   2,8,9   
      2,9,8   1,6,7   3,4,5   
      4,5,3   9,2,8   1,6,7   
      
      8,1,2   6,4,9   7,5,3   
      9,7,6   5,1,3   4,2,8   
      5,3,4   8,7,2   6,9,1   
      
      3,2,7   4,8,5   9,1,6   
      1,8,9   2,3,6   5,7,4   
      6,4,5   7,9,1   8,3,2   
      
      
      real    0m0.044s
      user    0m0.041s
      sys 0m0.001s
      

      【讨论】:

      • 足以给我一个负面评价?
      • 那不是我。我需要至少 100 声望点才能给出负面评价...
      • 如果有负面评价,可能是因为您没有真正回答问题,您只是说有更快的方法并在答案中添加了一些输出。为什么不告诉我们为什么 GCD 是一种不好的方式而线程更好呢?你显然对这两者有所了解。
      猜你喜欢
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 2012-11-27
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多