2016-06-15 9 views
2

先日、 "の迷路を解くための一般的なアルゴリズムを概説しました。迷路内の任意の位置にすべてのボールを得ることです迷路には出口がない) "。唯一の規則は、アルゴリズムが効果的(ボールをランダムに動かすよりも良い)でなければならず、発行されたすべてのコマンドがすべてのボールに影響するため、ボールが北に移動するので、ブロックされていなければ他のボールも移動するということです。これを行うにはn個のボールで迷路を解く一般的なアルゴリズム

私は迷路のボールは、ボールが位置に

  • Aを聞かれることができ
  • 互いをブロックすることはできません
  • 完璧/標準である、すなわち

    • ことを、いくつかの仮定をしましたコマンドはボールが壁に当たるまで回転させます
    • コマンドはN/S/E/Wのみになります
    • 迷路はランダムに構成されていますボールは毎回ランダムに分散D
    • 迷路のすべてが迷路のオペレータが一度に観察することができる

    そして、

    • ボールではない私のアルゴリズムの仕事を作るためにリセットされ、同一(例えば
    • )それらの数字か何かを持っている迷路のオペレータが写真のメモリ、この与えられた

    を持って、私は最高のアイデアが

      ランダム
    1. (またはスマートな方法でするだろうと思いました)ボールが2つのボールが目標位置に到達するまでボールを移動させます。
    2. 開始位置から終了位置までのパスを保存します。 (
    3. それらのボールを特定し、彼らがどこから来たのか、そしてそれらのそれぞれのために、すべてのボールが与えられた目標を達成するための方法を持っている場合だろう1.

    この再帰アルゴリズムで「休憩」を行いますO(log(n))recursionsだと思いますか?)

    これは機能しますか?誰かがこれを行うためのより良いアルゴリズムを持っていますか?

    私は、すべてのボールを同じランダムな位置に移動し、それらをすべて1つのボールとして移動するという別の考え方がありましたが、それは悪いアルゴリズムのようでした。

    もう一つの考えは、ボールのすべての安定点がノードであり、移動がエッジになるグラフ(グラフ理論)を生成することですが、それはどうやって見えませんか実行するために多くの無理な力が必要です。

  • +0

    「ボールは衝突できません」とは、他の前提の一部に矛盾しているようです。 – m69

    +0

    あなたは正しいです、それは私の言葉の貧しい言葉です。私は彼らがお互いをブロックすることはできないと言っていた(あるいは、衝突することのできないすべてのゴーストボール)。私はそれを編集します! – Eric

    答えて

    7

    は、私は次のアルゴリズムを示唆している:

    1. は、次の各フリーセル(四角)のために知られている迷路のためのデータ構造を作成します。

      。座標(行、列)
      b。 4つの移動(北、東、南、西)の対象セル
      c。 bの逆:大理石がこのセルに来るところのセル(存在する場合)。

    2. ターゲットセルからBFSを実行し、1つの想像上の大理石で逆方向に移動し、各訪問セルに、そのセルからターゲットセルに到達するために必要な移動回数を最小にします。いくつかの細胞がこのように訪問されないかもしれないことに注意してください。つまり、大理石がそこに置かれれば、合法的な動きをすることによって標的細胞にそれを得る方法がありません。これらのセルは、それらに起因する無限距離(初期値)を取得します。

    3. 特定の大理石構成にコストを割り当てる評価関数を作成します。提案された評価関数は、少なくとも1つの大理石によって占有された各セルの距離の2乗を合計する。正方形を取ることによって、距離が遠くなると比較的高いコストがかかるので、アルゴリズムは最悪の位置にある大理石の位置を改善する動きを優先する。この関数は、複数の大理石によって占有されているセルの数を数えません。そうすれば、大理石が細胞を共有する構成が好まれます。

    4. 開始位置から、評価されたコストで4つの可能な移動を生成します。それらをコストで昇順にソートし、その順序でDFSを実行し、このステップを再帰的に繰り返す。コストがゼロになると、解決策が見つけられ、再帰の即時バックトラッキング中に、移動の「パス」が返されます。コストが無限であれば、検索は停止され、次の移動が試行されます。

    5. 検索中に訪問先のリストを保持します。位置が再び訪れると、評価関数はそれに無限大の値を与えます。その結果、このときサーチがバックトラックします。ここ

    は、上記のアルゴリズムのJavaScript実装である:

    "use strict"; 
     
    function createMaze(mazeStr) { 
     
        var maze, lines, cell, row, ch, id, r, c, n, m; 
     
        
     
        maze = { 
     
         nodesRowCol: [], 
     
         nodes: [], 
     
         target: null, 
     
         marbles: [] 
     
        }; 
     
        id = 0; 
     
        lines = mazeStr.split("\n"); 
     
        for (r = 0; r < lines.length; r++) { 
     
         maze.nodesRowCol[r] = row = []; 
     
         for (c = 0; c < lines[r].length; c++) { 
     
          ch = lines[r].charAt(c); 
     
          if (ch !== '#') { 
     
           maze.nodes[id] = row[c] = cell = { 
     
            row: r, 
     
            col: c, 
     
            id: id++, 
     
            comeFrom: [], 
     
           }; 
     
           // Keep track of target and marbles 
     
           if (ch === '*') maze.target = cell; 
     
           if (ch === '.') maze.marbles.push(cell); 
     
          } 
     
         } 
     
        } 
     
        // Add neighbours 
     
        for (n = 0; n < maze.nodes.length; n++) { 
     
         cell = maze.nodes[n]; 
     
         cell.neighbours = [ 
     
          maze.nodesRowCol[cell.row-1][cell.col], /* north */ 
     
          maze.nodesRowCol[cell.row][cell.col+1], /* east */ 
     
          maze.nodesRowCol[cell.row+1][cell.col], /* south */ 
     
          maze.nodesRowCol[cell.row][cell.col-1] /* west */ 
     
         ]; 
     
        } 
     
        // Add marble moves in two steps 
     
        for (n = 0; n < maze.nodes.length; n++) { 
     
         cell = maze.nodes[n]; 
     
         cell.moves = [ 
     
          cell.neighbours[0] ? cell.neighbours[0].moves[0] : cell, /* north */ 
     
          null, 
     
          null, 
     
          cell.neighbours[3] ? cell.neighbours[3].moves[3] : cell, /* west */ 
     
         ]; 
     
        } 
     
        for (n = maze.nodes.length - 1; n >= 0; n--) { 
     
         cell = maze.nodes[n]; 
     
         cell.moves[1] = cell.neighbours[1] ? cell.neighbours[1].moves[1] : cell; /* west */ 
     
         cell.moves[2] = cell.neighbours[2] ? cell.neighbours[2].moves[2] : cell; /* south */ 
     
        } 
     
        // add reverse-move ("marble can come from") data 
     
        for (n = maze.nodes.length - 1; n >= 0; n--) { 
     
         cell = maze.nodes[n]; 
     
         for (m = 0; m < 4; m++) { 
     
          if (cell.moves[m] !== cell) cell.moves[m].comeFrom.push(cell); 
     
         } 
     
        } 
     
        return maze; 
     
    } 
     
    
     
    function setDistances(maze) { 
     
        var n, cell, distance, stack, newStack, i; 
     
        // clear distance information 
     
        for (n = 0; n < maze.nodes.length; n++) { 
     
         maze.nodes[n].distance = Number.POSITIVE_INFINITY; 
     
        } 
     
        // set initial distance 
     
        cell = maze.target; 
     
        cell.distance = distance = 0; 
     
        // BSF loop to set the distance for each cell that can be reached 
     
        stack = cell.comeFrom.slice(); 
     
        while (stack.length) { 
     
         distance++; 
     
         newStack = []; 
     
         for (i = 0; i < stack.length; i++) { 
     
          cell = stack[i]; 
     
          if (distance < cell.distance) { 
     
           cell.distance = distance; 
     
           newStack = newStack.concat(cell.comeFrom); 
     
          } 
     
         } 
     
         stack = newStack; 
     
        } 
     
    } 
     
    
     
    function evaluatedPosition(position, visited) { 
     
        // Assign heurstic cost to position 
     
        var m, ids; 
     
        
     
        position.cost = 0; 
     
        ids = []; // keep track of marble positions 
     
        for (m = 0; m < position.marbles.length; m++) { 
     
         // If mulitple marbles are at same cell, only account for that cell once. 
     
         // This will favour such positions: 
     
         if (ids[position.marbles[m].id] === undefined) { 
     
          // Make higher distances cost a lot, so that insentive 
     
          // is to improve the position of the worst placed marble 
     
          position.cost += Math.pow(position.marbles[m].distance, 2); 
     
          ids[position.marbles[m].id] = position.marbles[m].id; 
     
         } 
     
        } 
     
        // Assign some unique string, identifying the marble configuration 
     
        position.id = ids.join(','); 
     
        // If position was already visited before, give it the maximum cost 
     
        if (visited[position.id]) position.cost = Number.POSITIVE_INFINITY; 
     
        // Mark position as visited 
     
        visited[position.id] = 1; 
     
        return position; 
     
    } 
     
    
     
    function createMove(dir, marbles, visited) { 
     
        var m, movedMarbles; 
     
        
     
        movedMarbles = []; 
     
        for (m = 0; m < marbles.length; m++) { 
     
         movedMarbles[m] = marbles[m].moves[dir]; 
     
        } 
     
        return evaluatedPosition({ 
     
         dir: dir, 
     
         marbles: movedMarbles, 
     
        }, visited); 
     
    } 
     
    
     
    function solve(maze) { 
     
        var visited = {}; // nothing visited yet 
     
        
     
        function recurse (position) { 
     
         var ids, m, moves, i, path; 
     
         if (position.cost == 0) return []; // marbles are all on target spot. 
     
         if (!isFinite(position.cost)) return false; // no solution 
     
         // get move list 
     
         moves = []; 
     
         for (i = 0; i < 4; i++) { 
     
          moves[i] = createMove(i, position.marbles, visited); 
     
         } 
     
         // apply heuristic: sort the 4 moves by ascending cost 
     
         moves.sort(function (a,b) { return a.cost - b.cost }); 
     
         for (i = 0; i < 4; i++) { 
     
          //console.log('=== move === ' + moves[i].dir); 
     
          path = recurse(moves[i]); 
     
          if (path !== false) return [moves[i].dir].concat(path); 
     
         } 
     
         return false; // no solution found 
     
        } 
     
        // Enrich initial position with cost, and start recursive search 
     
        return recurse(evaluatedPosition({ 
     
         marbles: maze.marbles 
     
        }, visited)); 
     
    } 
     
    
     
    
     
    // # = wall 
     
    // * = target 
     
    // . = marble 
     
    
     
    var mazeStr = ` 
     
    ########### 
     
    # # #*# 
     
    # # #.# .# 
     
    # #. #.# # 
     
    # # # ### # 
     
    # #  # 
     
    ########### 
     
    `.trim(); 
     
    
     
    var maze = createMaze(mazeStr); 
     
    setDistances(maze); 
     
    console.log('#=wall, .=marble, *=target\n\n' + mazeStr); 
     
    
     
    var moves = solve(maze); 
     
    console.log('moves (0=north,1=east,2=south,3=west): ' + moves);

    見出さ溶液は必ずしも最適ではありません。深度1の評価を実行します。より良い解決策のために、アルゴリズムはより深いところで評価を行うことができます。

    +2

    この回答は、私の答えのバットを非常に多くの点でキックしています。私はこれがどのように解決されたのか、そしてなぜそれがより深いことをするのが良いのかを知っていますが、より大きなコンピューティングパワー、 – Eric

    +0

    もちろん、評価には時間がかかりすぎるため、奥行きは制限され、任意に大きくするべきではありません。深度が1で増加すると、見る位置の数が3倍になります。しかし、評価の検索ツリーでいくつかの分岐を切り取るために他のヒューリスティックを使うこともできます。ポジションの評価では、他のものと比較してコストが高すぎます。最適なソリューションが "丘"(一時的な高コスト)を通過する必要があるかもしれないので、誤って良いブランチを切り取る可能性があります。 – trincot

    +0

    私は遅れてとても申し訳ありません、私は仕事で忙しかったです:/答えは本当に良かったし、複雑さが増していくにつれてあなたが理解していることを理解しました(剪定に関するかなりの数のYouTubeクリップの後)。これをクリアしてくれてありがとう! – Eric