如何提出正确的递归解决方案

时间:2015-05-14 20:55:25

标签: javascript algorithm recursion

这是一个场景,我在接受采访时被问到以下问题并且我设法解决了部分问题,但我在问题的第二部分遇到了一些麻烦(我甚至不知道我是否解决了问题第一部分正确,我只想出了在这种情况下我能编写得最好的东西)。那么让我来介绍一下这个问题:

考虑以下2人游戏,在一串破折号和加号上进行游戏。如果在你的回合开始时,该字符串不包含一对相邻的破折号,那么你就赢了!否则,请在字符串中选择任意一对相邻的短划线“--”,并将其替换为“++”。我们轮流直到有人获胜。 示例游戏:-+-----+ - > -+-++--+ - > -+-+++++(游戏结束)。

编写一个函数listMoves(),它将位置字符串作为参数,并返回所有有效移动的列表。 例子:

listMoves("") == []
listMoves("--+---+") == ["+++---+", "--+++-+", "--+-+++"] 

我对此的解决方案(在JavaScript中)是:

var listMoves = function(arg) { 
    if (arg === null) return;

    if (arg === "") {
        return [];
    }

    var result = [];
    var temp = '';
    var string = [];
    for (var i=0; i<arg.length; i++) {
        if (temp == '-' && arg[i] == '-') {
            string = arg.split(''); 
            string[i-1] = '+';
            string[i] = '+';
            console.log(string);
            result.push(string);
        } else if (arg[i] == '-') {
            temp = arg[i];
        } else if (arg[i] == '+' && temp == '-') {
            temp = '';
        }
     }

     return result;
}

问题的第二部分是:

在最好的比赛中,每个位置都是玩家移动的胜负。编写一个函数isWin(position),当该位置是玩家移动的胜利时,该函数返回true。 例子:

isWin("") == true
isWin("---+") == false
isWin("----") == true
isWin("--+----+--+---++--") == ???

我设法理解我需要一个递归算法,并且我可以使用我为问题#1创建的函数(因此我将它包括在内)。

但是,我无法将我的想法付诸代码。

为了将来参考,有人可以告诉我他们将如何解决这样的问题吗?

编辑#1(在面试中添加了我的尝试):

var isWin = function (position) {

    if (position === null) return;
    if (position === "") return true;

    var possibleMoves = listMoves(position); 

    var win;

    if (possibleMoves.length < 1) {
        win = true;
    } else if (possibleMoves.length == 1) {
        win = false;
    } else {
        for (move in possibleMoves) {
            isWin(move);
        } 
    }

    return win;
}

1 个答案:

答案 0 :(得分:4)

通过递归调用结果listMoves,您可以获得所有可能结果的树。

例如------

enter image description here

所有终端节点都是游戏的最终状态。然而,我们试图从任何起始位置弄清楚,当球员发挥最佳状态时,如果该状态是获胜状态。

这可以通过检查是否存在一系列动作导致另一场比赛被迫选择一个动作导致首发球员获得胜利条件状态来确定:

因此,对于我们之前的示例,起始玩家有5种选择:

  1. 选择1会给下一个玩家3个选择:

    • 其中一个选择导致首发球员在轮到他们时获胜。
    • 另外两个选择导致首发球员再次转弯。在那个回合中,首发球员只有选择导致他们失败。与下一位选手一样聪明,他会选择其中一种选择。
  2. 选择2会给下一个玩家2个选择。下一个玩家的选择都会导致首发玩家在轮到他们的时候获胜。

  3. 选择3会给下一个玩家2个选择。下一个玩家的选择都会导致首发玩家通过一个选择轮到他们。这个单一选择导致下一个玩家在回头时获胜。

  4. 与选择2相同。

  5. 与选择1相同。

  6. 因为选择2和4存在,所以起始状态是起始玩家的胜利。

    enter image description here

    minimax是一个递归函数,它使用该逻辑来查找起始位置是否为起始玩家的胜负。

    对于我们的问题,我们可以将起始播放器player1标记为True。另一位玩家player2False

    如果为某个州minimax调用s,则该州没有可能的移动。然后minimax将返回被调用的播放器。

    minimaxsplayer1调用player == True时,如果有可能的移动,则会返回任何移动这导致minimax(move, player2)返回player1。 (如果有player1个结果,则播放器会选择该结果。

    minimaxsplayer2调用player == False时,如果有可能的移动,则会返回{strong>所有 { {1}}返回minimax(move, player1)。 (如果没有返回player1的结果,player2必须选择导致player2的移动,否则player1会选择导致player2获胜的移动

    使用Javascript:

    player2
    function listMoves(s) {
      var moves = [];
      for (var i = 0; i < s.length - 1; i++) {
        if (s.substring(i, i + 2) === '--') {
          moves.push(s.substring(0, i) + '++' + s.substring(i + 2));
        }
      }
      return moves;
    }
    
    function minimax(s, player) {
      var moves = listMoves(s);
      if (moves.length === 0) {
        return player;
      }
      if (player) {
        return moves.some(function(move) {
          return minimax(move, !player)
        });
      } else {
        return moves.every(function(move) {
          return minimax(move, !player);
        });
      }
    }
    
    function isWin(s) {
      return minimax(s, true);
    }
    
    document.write("<pre>" + isWin("--+----+--+---++--"), '"--+----+--+---++--"' + "</pre>");
    
    // From http://stackoverflow.com/a/12628791/635411
    function cartesianProductOf() {
      return _.reduce(arguments, function(a, b) {
        return _.flatten(_.map(a, function(x) {
          return _.map(b, function(y) {
            return x.concat([y]);
          });
        }), true);
      }, [[]]);
    };
    
    var res = {}
    for (var i = 1; i <= 6; i++) {
      var s = Array.apply(null, new Array(i)).map(String.prototype.valueOf, "-+");
      res[i] = {};
      cartesianProductOf.apply(null, s).forEach(function(state) {
        res[i][state] = isWin(state);
      });
    }
    
    document.write("<pre>" + JSON.stringify(res, null, 4) + "</pre>");

    的Python:

    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.8.0/lodash.min.js"></script>
    def list_moves(s):
        moves = []
        for i in range(len(s) - 1):
            if s[i] == "-" and s[i + 1] == "-":
                moves.append(s[:i] + "++" + s[i + 2:])
        return moves
    
    def minimax(s, player=True):
        moves = list_moves(s)
        if not moves:
            return player
        n = (minimax(move, not player) for move in moves)
        return any(n) if player else all(n)
    
    def is_win(s):
        return minimax(s)
    
    print is_win(""), '""'
    print
    print is_win("-"), '"-"'
    print is_win("+"), '"+"'
    print
    print is_win("--"), '"--"'
    print is_win("+-"), '"+-"'
    print is_win("-+"), '"-+"'
    print is_win("++"), '"++"'
    print
    print is_win("----"), '"----"'
    print is_win("+---"), '"+---"'
    print is_win("-+--"), '"-+--"'
    print is_win("--+-"), '"--+-"'
    print is_win("---+"), '"---+"'
    print is_win("++--"), '"++--"'
    print is_win("-++-"), '"-++-"'
    print is_win("--++"), '"--++"'
    print is_win("-+-+"), '"-+-+"'
    print is_win("+-+-"), '"+-+-"'
    print is_win("+--+"), '"+--+"'
    print is_win("+++-"), '"+++-"'
    print is_win("-+++"), '"-+++"'
    print is_win("++++"), '"++++"'
    print
    print is_win("-----"), '"-----"'
    print is_win("------"), '"------"'
    print is_win("-------"), '"-------"'
    print
    print is_win("--+----+--+---++--"), '"--+----+--+---++--"'