n个皇后的快速启发式算法(n> 1000)

时间:2014-12-30 00:17:54

标签: c++ algorithm chess heuristics n-queens

我写了两个程序:

  1. 在棋盘上放置n个皇后,没有任何回溯算法的威胁。但这对于大n来说非常沉重。最后你可以为100个皇后运行。
  2. 在国际象棋棋盘上放置了n个皇后,没有任何希尔登山算法的威胁。这个算法比以前的解决方案更好,但是300个皇后需要2分钟,而且这个时间呈指数增长!
  3. 但我没有任何想法快速做到这一点!我想要算法更快地做到这一点。

    我希望以更快的方式尽快解决1000个皇后的问题。

    这是我登山的代码:

    // N queen - Reset Repair Hill Climbing.cpp
    // open-mind.ir
    
    #include "stdafx.h"
    #include <vector>
    #include <iostream>
    #include <fstream>
    #include <time.h>
    #include <iomanip>
    
    
    using namespace std;
    
    //print solution in console
    void printBoardinTerminal(int *board, int len)
    {
        for (int i = 0; i < len; i++)
        {
            for (int j = 0; j < len; j++)
            {
                if (j == board[i])
                {
                    cout << 1 << " ";
                }
                else
                {
                    cout << 0 << " ";
                }
            }
            cout << endl;
        }
    }
    
    //print solution in File
    void printBoardinFile(int *board, int len)
    {
        ofstream fp("output.txt", ios::out);
    
        fp << "Answer for " << len << " queen: \n \n";
    
        for (int i = 0; i < len; i++)
        {
            for (int j = 0; j < len; j++)
            {
                fp << "----";
            }
            fp << "\n|";
    
            for (int j = 0; j < len; j++)
            {
                if (j == board[i])
                {
                    fp << setw(4) << "* |" ;
                }
                else
                {
                    fp << setw(4) << "  |";
                }
            }
            fp << "\n";
        }
    }
    
    //The number of queens couples who are threatened themself
    int evaluate(int *board, int len)
    {
        int score = 0;
        for (int i = 0; i < len - 1; i++)
        {
            for (int j = i + 1; j < len; j++)
            {
                if (board[i] == board[j])
                {
                    score++;
                    continue;
                }
                if (board[i] - board[j] == i - j)
                {
                    score++;
                    continue;
                }
                if (board[i] - board[j] ==  j - i)
                {
                    score++;
                    continue;
                }
            }
        }
        return score;
    }
    
    //generate new state from current state 
    int* generateBoard(int *board,int len)
    {
        vector <int> choice;
    
        int temp;
        int score;
        int eval = evaluate(board, len);
        int k;
    
        int *boardOut;
        boardOut = new int [len];
    
    
        for (int i = 0; i < len; i++)
        {
                boardOut[i] = board[i];
        }
    
        for (int i = 0; i < len; i++)
        {
            choice.clear();
    
            choice.push_back(boardOut[i]);
            temp = boardOut[i];
    
            for (int j = 0; j < len; j++)
            {
                boardOut[i] = j;
    
                k = evaluate(boardOut, len);
    
                if (k == eval)
                {
                    choice.push_back(j);
                }
    
                if (k < eval)
                {
                    choice.clear();
                    choice.push_back(j);
                    eval = k;
                }
            }
            boardOut[i] = choice[rand() % choice.size()];
        }
    
        return boardOut;
    }
    
    //in this function , genarate new state by pervious function and if it has better value then replaces that by current state
    bool findNextState(int *board, int len)
    {
        int maineval = evaluate(board, len);
    
        int *tempBoard;
    
        tempBoard = generateBoard(board, len);
    
        if (evaluate(tempBoard, len) < maineval)
        {
            for (int p = 0; p < len; p++)
            {
                board[p] = tempBoard[p];
            }
    
            return  true;
        }
    
        return false;
    }
    
    // make random initial state , put one queen in each row
    void initialRandomBoard(int * board, int len)
    {
        bool access;
        int col;
    
        for (int i = 0; i < len; i++)
        {
            board[i] = rand() % len;
        }
    }
    
    //this function include a loop that call findNextState function , and do that until reach solution
    //if findNextState function return NULL then we reset current state
    void SolveNQueen(int len)
    {
        cout << "The program is under process! wait!" << endl;
    
        int *board;
        board = new int[len];
    
    
        initialRandomBoard(board, len);
    
        while (evaluate(board, len) != 0)
        {
            if (!findNextState(board, len))
            {
                initialRandomBoard(board, len);
            }
        }
    
    
        //
        cout << endl << "Anwser for " << len << " queens: "<< endl << endl;
        printBoardinTerminal(board, len);
        printBoardinFile(board, len);
        //
    }
    
    
    int main()
    {
        int n;
        srand(time(NULL));
    
        cout << "Enter  number \'N\', \'N\' indicate numbers of queens in \"N * N\" chess board: " << endl;
        cin >> n;
    
        if (n < 4)
        {
            cout << "\'n\' must be uper than 3!" << endl;
            exit(1);
        }
    
        SolveNQueen(n);
    
        cout << endl << "As well , you can see result in \"output.txt\"." << endl << endl;
    
        return 0;
    }
    

1 个答案:

答案 0 :(得分:8)

注意:此答案假设您有兴趣找到一个有效解决方案。如果您需要找到所有解决方案,这对您没有帮助。

Artificial Intelligence: A Modern Approach,Russell&amp ;; Norvig在第5章中有一个表:约束满足问题(第143页),比较各种约束满足问题算法的各种任务。 (最新版本是第三版,看起来约束满意度问题现在是第6章。)

根据他们的结果,在 n -Queens问题上测试的算法中,最小冲突局部搜索启发式得分最高,需要平均4K检查与> 40,000K检查回溯相比和前瞻性检查。

算法非常简单:

  • 选择皇后的初始(随机或预选)分配
  • 虽然有受到威胁的女王(或者直到你厌倦了尝试......值得将它放在for循环中以限制尝试次数):
    • 选择一名随机受威胁的女王
    • 将选定的女王移动到最小化冲突的方格

在最后一步中,我假设每个女王都被约束到她的列,所以她只能更改列中的行。如果有几行可以最大限度地减少当前女王的冲突,您可以在其中随机选择。

就是这样。它完全随机,效果很好。

编辑:

我在这里有一个注意事项,当我实现这个算法时,我不记得我有多高 n ,说我知道我已经超过100了。我没找到我的旧代码,但是我无论如何决定扔东西。事实证明,这种方法远比我记忆中的有效。以下是10个皇后的结果:

Starting Configuration:
14  0  2  13  12  17  10  14  14  2  9  8  11  10  6  16  0  7  10  8  
Solution found
Ending Configuration:
17  2  6  12  19  5  0  14  16  7  9  3  1  15  11  18  4  13  8  10  
Elapsed time (sec): 0.00167
Number of moves: 227

在没有尝试优化代码的情况下,以下是针对不同问题大小的大致时序:

Queens      ~Time(sec)
======      ==========
  100           0.03
  200           0.12
  500           1.42
 1000           9.76
 2000          72.32
 5000        1062.39

我只运行了5000个皇后的最后一个,但是在18分钟内找到解决方案的速度比我预期的要快。