您的位置:首页 > 编程语言

趣味编程:用LINQ求解八皇后问题

2010-04-18 03:26 190 查看
C#源码摘自CSDN论坛.NET技术贴:从n皇后问题看Linq的对算法思想的清晰表达力,原作者sp1234。
注:变量名及程序逻辑稍有改动,求解部分加上了注释。

C#代码

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    public class Queen
    {
        public int row;
        public int col;
    }

    class Program
    {
        static IEnumerable<IEnumerable<Queen>> Queens(int r, int maxCol)
        {
            return r < 1 ?
                //递归的终止条件:第0行没有解
                new Queen[][] { new Queen[0] } :
                //外层循环:qs为一维数组,表示单个解,其中存放了前r-1行中各个皇后的位置
                from qs in Queens(r - 1, maxCol)
                //内层循环:c表示棋盘的各个纵列
                from c in Enumerable.Range(1, maxCol)
                //过滤条件:第r行的皇后不能与前r-1行中的任何一个皇后处在同一列、同一条斜线上
                where !qs.Any(q => q.col == c || Math.Abs(r - q.row) == Math.Abs(c - q.col))
                //将过滤后第r行的合法位置(r,c)加入到已有的解qs当中
                select qs.Concat(new [] { new Queen { row = r, col = c } });
        }

        static IEnumerable<IEnumerable<Queen>> Queens(int boardSize)
        {
            return Queens(boardSize, boardSize);
        }

        static void Main(string[] args)
        {
            Queens(8)
                .ToList()
                .ForEach(result =>
                {
                    result.ToList().ForEach(q => { Console.Write("{0} ", q.col); });
                    Console.WriteLine();
                });
        }
    }
}

//输出:共92个解(以下每一行代表一个解,数据的含义为各行中皇后所处的纵列)
//1 5 8 6 3 7 2 4
//1 6 8 3 7 4 2 5
//1 7 4 6 8 2 5 3
//1 7 5 8 2 4 6 3
//...
//...
//...
//8 2 4 1 7 5 3 6
//8 2 5 3 1 7 4 6
//8 3 1 6 2 5 7 4
//8 4 1 3 6 2 7 5

代码说明

代码中带两个参数的Queens函数(第16~29行)是求解八皇后问题的核心函数。
函数采用递归方式求解,其主体部分只包含一句LINQ语句,非常简洁。
参数r表示当前调用被用于寻找第r行皇后的位置。(任意两个皇后都不能处在同一行中,因此每一行最多只能有一个皇后)
参数maxCol表示棋盘的宽度。
函数返回一个两维数组,数组第一维表示各个解,而第二维则用于存放单个解中各个皇后的位置。

采用LINQ求解,解法固然清晰简洁,但同时也存在着不可调试、不易理解的缺点,为此以下特提供

可调试的Queens函数(完全不用LINQ的“传统”版本)

static IEnumerable<IEnumerable<Queen>> Queens(int r, int maxCol)
        {
            var result = new List<List<Queen>>();
            if (r < 1)
                //递归的终止条件:第0行没有解
                result.Add(new List<Queen>());
            else
                //外层循环:qs为一维数组,表示单个解,其中存放了前r-1行中各个皇后的位置
                foreach (var qs in Queens(r - 1, maxCol))
                    //内层循环:c表示棋盘的各个纵列
                    for (var c = 1; c <= maxCol; c++)
                    {
                        //过滤条件:第r行的皇后不能与前r-1行中的任何一个皇后处在同一列、同一条斜线上
                        foreach (var q in qs)
                            if (q.col == c || Math.Abs(r - q.row) == Math.Abs(c - q.col))
                                goto next_c;
                        //将过滤后第r行的合法位置(r,c)加入到已有的解qs当中
                        var qs2 = new List<Queen>(qs);
                        qs2.Add(new Queen { row = r, col = c });
                        result.Add(qs2);
                    next_c: ;
                    }
            return result;
        }
以备参考。

补记:F#版本

open System

let rec queens r maxCol =
    if r < 1 then
        //递归的终止条件:第0行没有解
        [[]]
    else
        //外层循环:qs为一维数组,表示单个解,其中存放了前r-1行中各个皇后的位置
        queens (r-1) maxCol |> List.map (fun qs -> 
            //内层循环:c表示棋盘的各个纵列
            [1..maxCol] |> List.choose (fun c -> 
                //过滤条件:第r行的皇后不能与前r-1行中的任何一个皇后处在同一列、同一条斜线上
                if qs |> List.exists (fun (qr : int, qc : int) ->   
                    qc = c || Math.Abs(r - qr) = Math.Abs(c - qc))   
                then None
                //将过滤后第r行的合法位置(r,c)加入到已有的解qs当中
                else Some(qs @ [(r,c)])
            )
        ) |> List.concat

let printQueens boardSize =
    queens boardSize boardSize |> List.iter (fun result ->
        result |> List.iter (fun (_, c) -> printf "%d " c) 
        printfn ""
    )

printQueens 8
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: