使用相互递归来解决组合游戏

时间:2015-11-09 17:16:53

标签: sml combinatorics game-theory

这是一种典型的公正游戏。两名玩家轮流挑选从1到n标记的棍棒,每当1号棍被挑选时,游戏就结束了。规则很简单:PlayerA倒数到5并选择相应的棒; PlayerA最多可以计算2并选择相应的棒。该程序试图找到一个初始点,以便最后一次选择No.1棒。

我尝试遍历列表并找到哪个初始位置会给出令人满意的结果,但似乎返回值不对。代码有什么问题??

fun play(stick) = 
let 
    val stick_list = n_list(stick) 
    (*n_list(8) will generate an int list [1,2,3,4,5,6,7,8]*)

    fun playerA(x::nil, n) = x
      | playerA(stick_list, n) = 
            let 
                val pos = (n + (5 mod size(stick_list))) mod size(stick_list)
            in
                playerB(delete(stick_list, pos), pos)
            end
    and playerB(x::nil, n) = x
      | playerB(stick_list, n) = 
            let 
                val pos = (n + (~2 mod size(stick_list))) mod size(stick_list)
            in
                playerA(delete(stick_list, pos), pos)
            end
    fun search(n) = if playerA(stick_list, n - 1) = 1 then n + 1 else search(n - 1)
in
    search(stick - 1)
end;

1 个答案:

答案 0 :(得分:0)

以下是一些建议:

  • n_list很容易实现为List.tabulate (stick, fn i => i + 1)
  • 在处理列表时使用length而不是size
  • 考虑记住留下的木棒数量,因此不需要重新计算长度。 E.g。

    fun play nSticks = 
        let val allSticks = List.tabulate (nSticks, fn i => i + 1)
    
            fun playerA ([stick], _, n) = stick
              | playerA (sticks, sticksLeft, n) =
                let val pos = (n + (5 mod sticksLeft)) mod sticksLeft
                in
                  playerB (delete (sticks, pos), sticksLeft - 1, pos)
                end
            ...
    
  • 除非这是相互递归的练习,否则如果将两个播放器功能合并为一个,似乎可以避免重复自己。由

    datatype Player = PlayerA | PlayerB
    fun player (_, stick::_, 0) = stick
      | player (player, sticks, sticksLeft, pos) = ...
        let val (newPos, otherPlayer) =
                case player of
                    PlayerA => ((n + 5) mod sticksLeft, PlayerB)
                  | PlayerB => ((sticksLeft + n - 2) mod sticksLeft, PlayerA)
            val newSticks = delete (sticks, pos)
        in player (otherPlayer, newSticks, sticksLeft - 1, newPos) end