八皇后问题(C+python)

最近继续自学python,看到了八皇后问题,觉得是个有点意思的问题。

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。

首先得说一下什么是回溯算法,实际是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种优选搜索法,按优选条件向前搜索,以达到目标。

该问题是在8x8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、后一列、或同一斜线上有多少种摆法。

通过C语言解决

因为是在python递归、迭代和生成器时碰到的这些问题,看了好久,还是有点懵,所以先用C语言的思想去理解一下。

首先我们得明确我们的思想,在这个8x8的棋盘上,我们如何去部署我们的棋子,我们已y轴为我们定义的数组下标范围,让x轴为数组内表示的元素所在的格子位置。

#include <stdio.h>
#include <stdlib.h>

static int board[8] = {};
int board_size = sizeof(board)/sizeof(int);

接着需要去思考如何使用数学的语言来表述斜线上重叠的皇后。对于棋盘上任意的一点,都要在[0,7]这个范围内,那么这个关系就是目标格子(a,b)和当前所在格子(x,y)等价于|a-x|==|b-y|

所以首先进行一个判定

int check(int *board,int row){
    int i = 0;
    while(i < row){
        if(board[i] == board[row] || row - i == board[row] - board[i] || row - i == board[i] - board[row]){
            return 0;
        }
        i++;
    }
    return 1;
}

我们从第一行开始给每一行的皇后确定一个位置。每来到新的一行时,对本行的所有可能位置(皇后放在这个位置和前面所有已放置的皇后无冲突)分别进行递归地深入;若某一行可能放置地位置为0,那么说明这是一个死路,此时需要返回到上一层去重新选择。若此时八行都结束后,那么说明这是一个可行的方法。

int eight_queen(int *board,int row){
    if(row == 8){
        printf_board(board);
        return 1;
    }
    board[row] = 0;//每次当row行跳转到下一行时,需要重新初始化一下
    while(1){
        if(check(board,row) && eight_queen(board,row + 1)){
            return 1;
        }
        else{
            if(++board[row] >= 8){//当上面的判断条件失败之后,将row行的值向后+1看是否满足了全部落完棋子的情况
                break;
            }
        }
    }
    return 0}

这里需要解释一下这个递归,在第一个if语句中,进行了两个函数的判断。首先是check函数,当第一个棋子部署后,进行第二个部署并且此时还要提前检查是否在第一个棋子的周围。如果不符合,那么board[row]+1,在进行判断,看此时是否符合要求。如果这时已经符合要求了,那么eight_queen函数也返回1,那么此时第二个就可以部署,以此类推,第三个也按照如此去部署。

当数组board[row]知道每个确切的值之后,我们将其数组打印,并且,打印出一个大概的图像

void print_board(int *board){
    int i;
    int size = board_size;
    for(i = 0;i < size; ++i){
        printf("%d",board[i]);
    }
    printf("\n");
    i = 0;//局部变量此时值不相同
    while(i < size){
        int j;
        for(j = 0;j < size; ++j){
            if(j == board[i]){
                printf("%s","x");
            }else{
             	printf("%s","0");   
            }
        }
        printf("\n");
        i++;
    }
}

代码比较不好理解的就是,在递归的过程中,何时board[row]被赋值,知道具体哪个位置可以被部署皇后。所以建议利用调试器去看!

通过python解决

其实早都该写完这篇博客,有点懒了,也是有点小受挫,总觉得python简单。现在不会再轻敌了。

关于问题的本身跟用C语言来分析是大概一样的,那么就是应该怎么样利用去python去想这个问题

还是先定义一个判定的方法,当我们去放置皇后时,判定是否可以去落棋子,这里称为检测冲突

def conflict(state, nextX):#这里的state将其定义为元组,它表示皇后落在每一行的第几个位置上
    nextY = len(state)
    for i in range(nextY):#检查每一个已摆放的棋子和当前行摆放的棋子的位置是否冲突
        if abs(state[i] - nextX) in (0,nextY - i):
            return True
        return False
        

主函数的思想则是利用python的生成器和递归的思想

将问题拆解,如果一个已经布置好了,那么接下来就是有限制的七皇后问题。那么每一次剩下的皇后没有放好,就遍历所有可能的位置,并放回那些不会引发冲突的位置。

def eight_queen(num=8,state=()):#num为皇后数目,state为元组
    for pos in range(num):#pos此时可以看成当前所在的行数
        if not conflict(state, pos):#此时是冲突判断,not是与函数内的true和false相反
            if len(state) == num - 1#当没有冲突时,元组内记录的皇后数-1,这样就代表继续向下部署
            	yield(pos,)#yield函数存在,此时已经成为了一个生成器,每次从上次pos结束的位置进行判断
            else:
                for result in queens(num,state + (pos,)):#此时将已得到的数据存入元组
                    yield(pos,) + result    

当主函数没问题了,我们也会想知道总共有多少方法

len(list(eight_queen(8)))

这样就可以知道有多少方法了。

最后,这个问题让我想了好久好久啊,感觉如果学习一门新的语言出现疑惑时,不妨利用C语言去思考一下,这样有助于更好去发现自己的盲点存在那里。

猜你喜欢

转载自blog.csdn.net/skrskr66/article/details/86687903
今日推荐