八皇后题目的JavaScript解法

关于八皇后题目的 JavaScript 解法,总觉得是须要进修一下算法的,哪天要用到的时刻发明真不会就为难了

背景

八皇后题目是一个以国际象棋为背景的题目:怎样能够在 8×8 的国际象棋棋盘上安排八个皇后,使得任何一个皇后都没法直接吃掉其他的皇后?为了到达此目的,任两个皇后都不能处于统一条横行、纵行或斜线上

八皇后题目能够推广为更平常的n皇后摆放题目:这时候棋盘的大小变成 n×n ,而皇后个数也变成n 。当且仅当n = 1n ≥ 4时题目有解

自觉标罗列算法

经由过程N重轮回,罗列满足约束条件的解(八重轮回代码很多,这里举行四重轮回),找到四个皇后的一切能够位置,然后再全部棋盘里推断这四个皇后是不是会直接吃掉相互,顺序头脑比较简朴

function check1(arr, n) {
    for(var i = 0; i < n; i++) {
        for(var j = i + 1; j < n; j++) {
            if((arr[i] == arr[j]) || Math.abs(arr[i] - arr[j]) == j - i) {
                return false;
            }
        }
    }
    return true;
}
function queen1() {
    var arr = [];

    for(arr[0] = 1; arr[0] <= 4; arr[0]++) {
        for(arr[1] = 1; arr[1] <= 4; arr[1]++) {
            for(arr[2] = 1; arr[2] <= 4; arr[2]++) {
                for(arr[3] = 1; arr[3] <= 4; arr[3]++) {
                    if(!check1(arr, 4)) {
                        continue;
                    } else {
                        console.log(arr);
                    }
                }
            }
        }
    }
}

queen1();
//[ 2, 4, 1, 3 ]
//[ 3, 1, 4, 2 ]

关于效果,在 4*4 的棋盘里,四个皇后都不多是在一排, arr[0] 到 arr[3] 离别对应四个皇后,数组的下标与下标对应的值即皇后在棋盘中的位置

回溯法

『走不通,就转头』,在恰当节点推断是不是相符,不相符就不再举行这条支路上的探究

function check2(arr, n) {
    for(var i = 0; i <= n - 1; i++) {
        if((Math.abs(arr[i] - arr[n]) == n - i) || (arr[i] == arr[n])) {
            return false;
        }
    }
    return true;
}

function queen2() {
    var arr = [];

    for(arr[0] = 1; arr[0] <= 4; arr[0]++) {
        for(arr[1] = 1; arr[1] <= 4; arr[1]++) {
            if(!check2(arr, 1)) continue; //摆两个皇后发生争执的状况
            for(arr[2] = 1; arr[2] <= 4; arr[2]++) {
                if(!check2(arr, 2)) continue; //摆三个皇后发生争执的状况
                for(arr[3] = 1; arr[3] <= 4; arr[3]++) {
                    if(!check2(arr, 3)) {
                        continue;
                    } else {
                        console.log(arr);
                    }
                }
            }
        }
    }
}

queen2();
//[ 2, 4, 1, 3 ]
//[ 3, 1, 4, 2 ]

非递归回溯法

算法框架

while(k > 0 『有路可走』 and 『未到达目的』) { // k > 0 有路可走
    if(k > n) { // 搜刮到恭弘=叶 恭弘子节点
        // 搜刮到一个解,输出
    } else {
        //a[k]第一个能够的值
        while(『a[k]在不满足约束条件且在搜刮空间内』) {
            // a[k]下一个能够的值
        }
        if(『a[k]在搜刮空间内』) {
            // 标示占用的资本
            // k = k + 1;
        } else {
            // 清算所占的状况空间
            // k = k - 1;
        }
    }
}

详细代码以下,最外层while下面包括两部份,一部份是对当前皇后能够值的遍历,另一部份是决定是进入下一层照样回溯上一层

function backdate(n) {
    var arr = [];

    var k = 1; // 第n的皇后
    arr[0] = 1;

    while(k > 0) {

        arr[k-1] = arr[k-1] + 1;
        while((arr[k-1] <= n) && (!check2(arr, k-1))) {
            arr[k-1] = arr[k-1] + 1;
        }
        // 这个皇后满足了约束条件,举行下一步推断

        if(arr[k-1] <= n) {
            if(k == n) { // 第n个皇后
                console.log(arr);
            } else {
                k = k + 1; // 下一个皇后
                arr[k-1] = 0;
            }
        } else {
            k = k - 1; // 回溯,上一个皇后
        }
    }
}

backdate(4);
//[ 2, 4, 1, 3 ]
//[ 3, 1, 4, 2 ]

递归回溯法

递归挪用大大减少了代码量,也增加了顺序的可读性

var arr = [], n = 4;
function backtrack(k) {
    if(k > n) {
        console.log(arr);
    } else {
        for(var i = 1;i <= n; i++) {
            arr[k-1] = i;
            if(check2(arr, k-1)) {
                backtrack(k + 1);
            }
        }
    }
}

backtrack(1);
//[ 2, 4, 1, 3 ]
//[ 3, 1, 4, 2 ]

虚有其表的amb

什么是 amb ?给它一个数据列表,它能返回满足约束条件的胜利状况的一种体式格局,没有胜利状况就会失利,固然,它能够返回一切的胜利状况。笔者写了上面那么多的重点,就是为了在这里引荐这个amb算法,它合适处置惩罚简朴的回溯场景,很风趣,让我们来看看它是怎样事情的

起首来处置惩罚一个小题目,寻觅相邻字符串:拿到几组字符串数组,每一个数组拿出一个字符串,前一个字符串的末位字符与后一个字符串的首位字符雷同,满足条件则输出这组新取出来的字符串

ambRun(function(amb, fail) {

    // 约束条件要领
    function linked(s1, s2) {
        return s1.slice(-1) == s2.slice(0, 1);
    }

    // 注入数据列表
    var w1 = amb(["the", "that", "a"]);
    var w2 = amb(["frog", "elephant", "thing"]);
    var w3 = amb(["walked", "treaded", "grows"]);
    var w4 = amb(["slowly", "quickly"]);

    // 实行顺序
    if (!(linked(w1, w2) && linked(w2, w3) && linked(w3, w4))) fail();

    console.log([w1, w2, w3, w4].join(' '));
    // "that thing grows slowly"
});

看起来超等简约有无!不过运用的条件是,你不在乎机能,它真的是很浪费时间!

下面是它的 javascript 完成,有兴致能够研讨研讨它是怎样把回溯抽出来的

function ambRun(func) {
    var choices = [];
    var index;

    function amb(values) {
        if (values.length == 0) {
            fail();
        }
        if (index == choices.length) {
            choices.push({i: 0,
                count: values.length});
        }
        var choice = choices[index++];
        return values[choice.i];
    }

    function fail() { throw fail; }

    while (true) {
        try {
            index = 0;
            return func(amb, fail);
        } catch (e) {
            if (e != fail) {
                throw e;
            }
            var choice;

            while ((choice = choices.pop()) && ++choice.i == choice.count) {}
            if (choice == undefined) {
                return undefined;
            }
            choices.push(choice);
        }
    }
}

以及运用 amb 完成的八皇后题目的详细代码

ambRun(function(amb, fail){
    var N = 4;
    var arr = [];
    var turn = [];
    for(var n = 0; n < N; n++) {
        turn[turn.length] = n + 1;
    }
    while(n--) {
        arr[arr.length] = amb(turn);
    }
    for (var i = 0; i < N; ++i) {
        for (var j = i + 1; j < N; ++j) {
            var a = arr[i], b = arr[j];
            if (a == b || Math.abs(a - b) == j - i) fail();
        }
    }
    console.log(arr);
    fail();
});

八皇后题目的JavaScript解法

这是八皇后题目的JavaScript解法,全部顺序都没用for轮回,都是靠递返来完成的,充分运用了Array对象的map, reduce, filter, concat, slice要领

'use strict';
var queens = function (boarderSize) {
  // 用递归天生一个start到end的Array
  var interval = function (start, end) {
    if (start > end) { return []; }
    return interval(start, end - 1).concat(end);
  };
  // 搜检一个组合是不是有用
  var isValid = function (queenCol) {
    // 搜检两个位置是不是有争执
    var isSafe = function (pointA, pointB) {
      var slope = (pointA.row - pointB.row) / (pointA.col - pointB.col);
      if ((0 === slope) || (1 === slope) || (-1 === slope)) { return false; }
      return true;
    };
    var len = queenCol.length;
    var pointToCompare = {
      row: queenCol[len - 1],
      col: len
    };
    // 先slice出除了末了一列的数组,然后顺次测试每列的点和待测点是不是有争执,末了兼并测试效果
    return queenCol
      .slice(0, len - 1)
      .map(function (row, index) {
        return isSafe({row: row, col: index + 1}, pointToCompare);
      })
      .reduce(function (a, b) {
        return a && b;
      });
  };
  // 递归地去一列一列天生相符划定规矩的组合
  var queenCols = function (size) {
    if (1 === size) {
      return interval(1, boarderSize).map(function (i) { return [i]; });
    }
    // 先把之前一切相符划定规矩的列构成的鸠合再扩大一列,然后用reduce降维,末了用isValid过滤掉不相符划定规矩的组合
    return queenCols(size - 1)
      .map(function (queenCol) {
        return interval(1, boarderSize).map(function (row) {
          return queenCol.concat(row);
        });
      })
      .reduce(function (a, b) {
        return a.concat(b);
      })
      .filter(isValid);
  };
  // queens函数进口
  return queenCols(boarderSize);
};

console.log(queens(8));
// 输出效果:
// [ [ 1, 5, 8, 6, 3, 7, 2, 4 ],
//   [ 1, 6, 8, 3, 7, 4, 2, 5 ],
//   ...
//   [ 8, 3, 1, 6, 2, 5, 7, 4 ],
//   [ 8, 4, 1, 3, 6, 2, 7, 5 ] ]

总结

回溯算法是很经常使用的基础算法,仔细控制是没有错的,笔者也是一边进修一边写下本篇,进修内容泉源

八皇后题目
五大经常使用算法之四:回溯法
回溯法——八皇后题目
Amb() in JavaScript
八皇后题目的 JavaScript 解法

文章转载自笔者个人博客 Gaoxuefeng’s Blog

    原文作者:ggsoul
    原文地址: https://segmentfault.com/a/1190000005120353
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞