<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>A* Archives - Huahua&#039;s Tech Road</title>
	<atom:link href="https://zxi.mytechroad.com/blog/tag/a/feed/" rel="self" type="application/rss+xml" />
	<link>https://zxi.mytechroad.com/blog/tag/a/</link>
	<description></description>
	<lastBuildDate>Thu, 02 May 2019 04:47:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.8</generator>

<image>
	<url>https://zxi.mytechroad.com/blog/wp-content/uploads/2017/09/cropped-photo-32x32.jpg</url>
	<title>A* Archives - Huahua&#039;s Tech Road</title>
	<link>https://zxi.mytechroad.com/blog/tag/a/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>花花酱 8 Puzzles &#8211; Bidirectional A* vs Bidirectional BFS</title>
		<link>https://zxi.mytechroad.com/blog/searching/8-puzzles-bidirectional-astar-vs-bidirectional-bfs/</link>
					<comments>https://zxi.mytechroad.com/blog/searching/8-puzzles-bidirectional-astar-vs-bidirectional-bfs/#respond</comments>
		
		<dc:creator><![CDATA[zxi]]></dc:creator>
		<pubDate>Tue, 30 Apr 2019 08:57:23 +0000</pubDate>
				<category><![CDATA[Search]]></category>
		<category><![CDATA[A*]]></category>
		<category><![CDATA[AStar]]></category>
		<category><![CDATA[BFS]]></category>
		<category><![CDATA[Bidirectional BFS]]></category>
		<category><![CDATA[search]]></category>
		<guid isPermaLink="false">https://zxi.mytechroad.com/blog/?p=5129</guid>

					<description><![CDATA[<p>8 Puzzles # nodes expended of 1000 solvable instances Conclusion: Nodes expended: BiDirectional A* &#60;&#60; A* (Manhattan) &#60;= Bidirectional BFS &#60; A* Hamming &#60;&#60; BFSRunning&#8230;</p>
<p>The post <a rel="nofollow" href="https://zxi.mytechroad.com/blog/searching/8-puzzles-bidirectional-astar-vs-bidirectional-bfs/">花花酱 8 Puzzles &#8211; Bidirectional A* vs Bidirectional BFS</a> appeared first on <a rel="nofollow" href="https://zxi.mytechroad.com/blog">Huahua&#039;s Tech Road</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image"><img width="861" height="591" src="https://zxi.mytechroad.com/blog/wp-content/uploads/2019/04/nodes.png" alt="" class="wp-image-5133" srcset="https://zxi.mytechroad.com/blog/wp-content/uploads/2019/04/nodes.png 861w, https://zxi.mytechroad.com/blog/wp-content/uploads/2019/04/nodes-300x206.png 300w, https://zxi.mytechroad.com/blog/wp-content/uploads/2019/04/nodes-768x527.png 768w" sizes="(max-width: 861px) 100vw, 861px" /></figure>



<p style="text-align:center">8 Puzzles # nodes expended of 1000 solvable instances</p>



<figure class="wp-block-image"><img width="861" height="591" src="https://zxi.mytechroad.com/blog/wp-content/uploads/2019/05/time.png" alt="" class="wp-image-5134" srcset="https://zxi.mytechroad.com/blog/wp-content/uploads/2019/05/time.png 861w, https://zxi.mytechroad.com/blog/wp-content/uploads/2019/05/time-300x206.png 300w, https://zxi.mytechroad.com/blog/wp-content/uploads/2019/05/time-768x527.png 768w" sizes="(max-width: 861px) 100vw, 861px" /></figure>



<p>Conclusion: </p>



<p>Nodes expended: BiDirectional A* &lt;&lt; A* (Manhattan) &lt;= Bidirectional BFS &lt; A* Hamming &lt;&lt; BFS<br>Running time:  BiDirectional A* &lt;  Bidirectional BFS &lt;=  A* (Manhattan)  &lt; A* Hamming &lt;&lt; BFS </p>



<p>Code:</p>



<pre class="crayon-plain-tag"># Author: Huahua

from collections import deque
import heapq
import numpy as np
import random
import sys

N = 3

def Manhattan(s1, s2):
  dist = 0
  for i1, d in enumerate(s1):
    i2 = s2.index(d)
    x1 = i1 % N
    y1 = i1 // N
    x2 = i2 % N
    y2 = i2 // N
    dist += abs(x1 - x2) + abs(y1 - y2)
  return dist

def Hamming(s1, s2):
  return sum(x != y for x, y in zip(s1, s2))

class Node:
  dirs = [0, -1, 0, 1, 0]
  def __init__(self, state, parent = None, h = 0):
    self.state = state
    self.parent = parent
    self.g = parent.g + 1 if parent else 0
    self.h = h
    self.f = self.g + self.h

  def getMoves(self):
    moves = []
    index = self.state.index(0)
    x = index % N
    y = index // N
    for i in range(4):
      tx = x + Node.dirs[i]
      ty = y + Node.dirs[i + 1]
      if tx &lt; 0 or ty &lt; 0 or tx == N or ty == N:
        continue
      i = ty * N + tx
      move = list(self.state)
      move[index] = move[i]
      move[i] = 0
      moves.append(tuple(move))
    return moves

  def print(self):
    print(np.reshape(self.state, (N, N)))

  def __lt__(self, other):
    return self.f &lt; other.f

def AStarSearch(start_state, end_state, heuristic):
  def h(s):
    return heuristic(s, end_state)
  q = []
  s = Node(start_state, h=h(start_state))
  heapq.heappush(q, s)
  opened = {s.state : s.f}
  closed = dict()
  while q:
    n = heapq.heappop(q)
    if n.state == end_state:
      return n, len(opened), len(closed)
    if n.state in closed: continue
    closed[n.state] = n.f
    for move in n.getMoves():
      node = Node(move, n, h(move))
      if move in opened and opened[move] &lt;= node.f: continue
      opened[node.state] = node.f
      heapq.heappush(q, node)
  return None, len(opened), len(closed)

def BFS(start_state, end_state):
  q = deque()
  q.append(Node(start_state))
  opened = set(start_state)
  closed = 0
  while q:
    n = q.popleft()
    if n.state == end_state:
      return n, len(opened), closed
    closed += 1
    for move in n.getMoves():
      if move in opened: continue
      opened.add(move)
      q.append(Node(move, n))
  return None, len(opened), closed

def getRootNode(n):
  return getRootNode(n.parent) if n.parent else n

def BidirectionalBFS(start_state, end_state):
  def constructPath(p, o):
    while o: 
      t = o.parent
      o.parent = p
      p, o = o, t
    return p
  ns = Node(start_state)
  ne = Node(end_state)
  q = [deque([ns]), deque([ne])]
  opened = [{start_state : ns}, {end_state: ne}]
  closed = [0, 0]
  while q[0]:
    l = len(q[0])
    while l &gt; 0:
      p = q[0].popleft()
      closed[0] += 1
      for move in p.getMoves():
        n = Node(move, p)
        if move in opened[1]:
          o = opened[1][move]
          if getRootNode(n).state == end_state:
            o, n = n, o
          n = constructPath(n, o.parent)
          return n, len(opened[0]) + len(opened[1]), closed[0] + closed[1]
        if move in opened[0]: continue
        opened[0][move] = n
        q[0].append(n)
      l -= 1
    q.reverse()
    opened.reverse()
    closed.reverse()
  return None, len(opened[0]) + len(opened[1]), closed[0] + closed[1]


def print_path(n):
  if not n: return
  print_path(n.parent)
  n.print()

def solvable(state):
  inv = 0
  for i in range(N*N):
    for j in range(i + 1, N*N):
      if all((state[i] &gt; 0, state[j] &gt; 0, state[i] &gt; state[j])):
        inv += 1
  return inv % 2 == 0

if __name__ == '__main__':
  s = [1, 2, 3, 4, 5, 6, 7, 8, 0]
  e = [1, 2, 3, 4, 5, 6, 7, 8, 0]
  i = 0
  while True:
    random.shuffle(s)
    if not solvable(s): continue
    n1, opened1, closed1 = AStarSearch(tuple(s), tuple(e), heuristic=Manhattan)
    n2, opened2, closed2 = AStarSearch(tuple(s), tuple(e), heuristic=Hamming)
    n3, opened3, closed3 = BFS(tuple(s), tuple(e))
    n4, opened4, closed4 = BidirectionalBFS(tuple(s), tuple(e))
    print(&quot;%d\t%d\t%d\t%d&quot; % (closed1, closed2, closed3, closed4))
    sys.stdout.flush()

    # print(&quot;A*&quot;)
    # print_path(n1)
    # print('---------------')
    # print(&quot;BFS&quot;)
    # print_path(n3)
    # print('---------------')
    # print(&quot;Bi-BFS&quot;)
    # print_path(n4)
    # print('---------------')

    i += 1
    if i == 1000: break</pre>



<p>C++ Version</p>



<pre class="crayon-plain-tag">#include &lt;algorithm&gt;
#include &lt;array&gt;
#include &lt;chrono&gt;
#include &lt;deque&gt;
#include &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;queue&gt;
#include &lt;unordered_map&gt;
#include &lt;unordered_set&gt;
#include &lt;vector&gt;

using namespace std;
using chrono::high_resolution_clock;

constexpr int N = 3;
constexpr int dirs[] = {0, 1, 0, -1, 0};

struct Node;
typedef string State;
typedef shared_ptr&lt;Node&gt; NodePtr;
typedef function&lt;int(const State&amp; s, const State&amp; t)&gt; Heuristic;

struct Node {
  Node(State s, NodePtr p = nullptr, int h = 0)
      : s(s), p(p), h(h), g(p ? p-&gt;g + 1 : 0), f(this-&gt;h + g) {}

  vector&lt;State&gt; GetNextStates() const {
    vector&lt;State&gt; states;
    int index = find(s.begin(), s.end(), '0') - s.begin();
    int x = index % N;
    int y = index / N;
    for (int i = 0; i &lt; 4; ++i) {
      int tx = x + dirs[i];
      int ty = y + dirs[i + 1];
      if (tx &lt; 0 || ty &lt; 0 || tx == N || ty == N) continue;
      int new_index = ty * N + tx;
      State next(s);
      next[index] = s[new_index];
      next[new_index] = '0';
      states.push_back(move(next));
    }
    return states;
  }

  NodePtr p;
  int h;
  int g;
  int f;
  State s;
};

bool Sovlable(const State&amp; s) {
  int inv_count = 0;
  for (int i = 0; i &lt; N * N; ++i) {
    for (int j = i + 1; j &lt; N * N; ++j) {
      if (s[i] != '0' &amp;&amp; s[j] != '0' &amp;&amp; s[i] &gt; s[j]) {
        ++inv_count;
      }
    }
  }
  return inv_count % 2 == 0;
}

NodePtr GetRoot(NodePtr n) { return n-&gt;p ? GetRoot(n-&gt;p) : n; }
NodePtr WirePath(NodePtr p, NodePtr n) {
  while (n) {
    NodePtr t = n-&gt;p;
    n-&gt;p = p;
    p = n;
    n = t;
  }
  return p;
}

void ConstructPath(NodePtr node, vector&lt;State&gt;* path) {
  while (node) {
    path-&gt;push_back(node-&gt;s);
    node = node-&gt;p;
  }
  reverse(path-&gt;begin(), path-&gt;end());
}

class Solver {
 public:
  virtual bool Solve(State start, State target, vector&lt;State&gt;* path,
                     int* opened, int* closed) = 0;
};

class BFSSolver : public Solver {
 public:
  bool Solve(State start, State target, vector&lt;State&gt;* path, int* opened,
             int* closed) override {
    int expended = 0;
    unordered_set&lt;string&gt; seen;
    deque&lt;NodePtr&gt; q{make_shared&lt;Node&gt;(start)};
    while (!q.empty()) {
      auto cur = q.front();
      q.pop_front();
      ++expended;
      if (cur-&gt;s == target) {
        ConstructPath(cur, path);
        *opened = seen.size();
        *closed = expended;
        return true;
      }
      for (const auto&amp; s : cur-&gt;GetNextStates()) {
        auto r = seen.insert(s);
        if (!r.second) continue;
        q.push_back(make_shared&lt;Node&gt;(s, cur));
      }
    }
    return false;
  }
};

class BidirectionalBFSSolver : public Solver {
 public:
  bool Solve(State start, State target, vector&lt;State&gt;* path, int* opened,
             int* closed) override {
    auto start_node = make_shared&lt;Node&gt;(start);
    auto target_node = make_shared&lt;Node&gt;(target);
    unordered_map&lt;string, NodePtr&gt; seen0{{start, start_node}};
    unordered_map&lt;string, NodePtr&gt; seen1{{target, target_node}};
    deque&lt;NodePtr&gt; q0{start_node};
    deque&lt;NodePtr&gt; q1{target_node};
    int expended = 0;
    while (!q0.empty()) {
      size_t size = q0.size();
      while (size--) {
        auto cur = q0.front();
        q0.pop_front();
        ++expended;

        for (const auto&amp; s : cur-&gt;GetNextStates()) {
          auto n = make_shared&lt;Node&gt;(s, cur);
          auto it = seen1.find(s);
          if (it != seen1.end()) {
            auto p = it-&gt;second;
            if (GetRoot(n)-&gt;s == target) swap(n, p);
            n = WirePath(n, p-&gt;p);
            ConstructPath(n, path);
            *opened = seen0.size() + seen1.size();
            *closed = expended;
            return true;
          }

          if (seen0.count(s)) continue;
          seen0[s] = n;
          q0.push_back(n);
        }
      }

      if (q1.size() &lt; q0.size()) {
        swap(q0, q1);
        swap(seen0, seen1);
      }
    }
    return false;
  }

 private:
};

struct NodeCompare : public binary_function&lt;NodePtr, NodePtr, bool&gt; {
  bool operator()(const NodePtr&amp; x, const NodePtr&amp; y) const {
    return x-&gt;f &gt; y-&gt;f;
  }
};

int ManhattanDistance(const State&amp; s, const State&amp; t) {
  int h = 0;
  for (int i1 = 0; i1 &lt; N * N; ++i1) {
    int i2 = t.find(s[i1]);
    int x1 = i1 % N;
    int y1 = i1 / N;
    int x2 = i2 % N;
    int y2 = i2 / N;
    h += abs(x1 - x2) + abs(y1 - y2);
  }
  return h;
}

int HammingDistance(const State&amp; s, const State&amp; t) {
  int h = 0;
  for (size_t i = 0; i &lt; s.size(); ++i) {
    if (s[i] != t[i]) ++h;
  }
  return h;
}

class AStarSolver : public Solver {
 public:
  explicit AStarSolver(Heuristic heuristic) : heuristic_(heuristic) {}
  bool Solve(State start, State target, vector&lt;State&gt;* path, int* opened,
             int* closed) override {
    unordered_map&lt;string, NodePtr&gt; o;
    unordered_set&lt;string&gt; c;
    priority_queue&lt;NodePtr, vector&lt;NodePtr&gt;, NodeCompare&gt; q;
    q.emplace(new Node(start, nullptr, heuristic_(start, target)));
    while (!q.empty()) {
      auto cur = q.top();
      q.pop();
      if (cur-&gt;s == target) {
        ConstructPath(cur, path);
        *opened = o.size();
        *closed = c.size();
        return true;
      }
      if (!c.insert(cur-&gt;s).second) continue;
      for (const auto&amp; s : cur-&gt;GetNextStates()) {
        auto it = o.find(s);
        auto n = make_shared&lt;Node&gt;(s, cur, heuristic_(s, target));
        if (it != o.end() &amp;&amp; n-&gt;f &gt;= it-&gt;second-&gt;f) continue;
        o[s] = n;
        q.push(n);
      }
    }
    return false;
  }

 private:
  Heuristic heuristic_;
};

class BiAStarSolver : public Solver {
 public:
  explicit BiAStarSolver(Heuristic heuristic) : heuristic_(heuristic) {}
  bool Solve(State start, State target, vector&lt;State&gt;* path, int* opened,
             int* closed) override {
    unordered_map&lt;string, NodePtr&gt; o0, o1;
    unordered_map&lt;string, NodePtr&gt; c0, c1;
    priority_queue&lt;NodePtr, vector&lt;NodePtr&gt;, NodeCompare&gt; q0, q1;
    q0.emplace(new Node(start, nullptr, heuristic_(start, target)));
    q1.emplace(new Node(target, nullptr, heuristic_(target, start)));
    bool farward = true;
    while (!q0.empty()) {
      auto size = q0.size();
      while (size--) {
        auto cur = q0.top();
        q0.pop();
        if (c0.count(cur-&gt;s)) continue;
        c0[cur-&gt;s] = cur;
        if (c1.count(cur-&gt;s)) {
          auto p = c1[cur-&gt;s];
          if (GetRoot(cur)-&gt;s == target) swap(cur, p);
          cur = WirePath(cur, p-&gt;p);
          ConstructPath(cur, path);
          *opened = o0.size() + o1.size();
          *closed = c0.size() + c1.size();
          return true;
        }
        for (const auto&amp; s : cur-&gt;GetNextStates()) {
          auto it = o0.find(s);
          auto n = make_shared&lt;Node&gt;(s, cur,
                                     heuristic_(s, farward ? target : start));
          if (it != o0.end() &amp;&amp; n-&gt;f &gt;= it-&gt;second-&gt;f) continue;
          o0[s] = n;
          q0.push(n);
        }
      }
      if (q1.size() &lt; q0.size()) {
        swap(q0, q1);
        swap(c0, c1);
        swap(o0, o1);
        farward = !farward;
      }
    }
    return false;
  }

 private:
  Heuristic heuristic_;
};

bool VerifyPath(const vector&lt;State&gt;&amp; path, const State&amp; s, const State&amp; t) {
  if (path.empty()) return false;
  if (path.front() != s || path.back() != t) return false;
  for (size_t i = 1; i &lt; path.size(); ++i) {
    Node n(path[i - 1]);
    auto states = n.GetNextStates();
    if (find(begin(states), end(states), path[i]) == end(states)) {
      return false;
    }
  }
  return true;
}

void StatisticsMode() {
  State s{&quot;123456780&quot;};
  State t{&quot;123456780&quot;};

  vector&lt;unique_ptr&lt;Solver&gt;&gt; solvers;
  solvers.emplace_back(new BFSSolver);
  solvers.emplace_back(new AStarSolver(HammingDistance));
  solvers.emplace_back(new AStarSolver(ManhattanDistance));
  solvers.emplace_back(new BidirectionalBFSSolver);
  solvers.emplace_back(new BiAStarSolver(ManhattanDistance));

  for (int i = 0; i &lt; 1000;) {
    random_shuffle(s.begin(), s.end());
    if (!Sovlable(s)) continue;
    ++i;
    for (auto&amp; solver : solvers) {
      vector&lt;State&gt; path;
      int opened;
      int closed;
      auto t0 = high_resolution_clock::now();
      bool sovlable = solver-&gt;Solve(s, t, &amp;path, &amp;opened, &amp;closed);
      auto t1 = high_resolution_clock::now();

      if (!VerifyPath(path, s, t)) {
        cerr &lt;&lt; &quot;Invalid path!&quot; &lt;&lt; endl;
        return;
      }

      auto time_span = chrono::duration_cast&lt;chrono::duration&lt;double&gt;&gt;(t1 - t0);

      cout &lt;&lt; closed &lt;&lt; &quot;\t&quot; &lt;&lt; time_span.count() * 1000 &lt;&lt; &quot;\t&quot;;
    }
    cout &lt;&lt; endl;
  }
}

int main() { StatisticsMode(); }</pre>
<p>The post <a rel="nofollow" href="https://zxi.mytechroad.com/blog/searching/8-puzzles-bidirectional-astar-vs-bidirectional-bfs/">花花酱 8 Puzzles &#8211; Bidirectional A* vs Bidirectional BFS</a> appeared first on <a rel="nofollow" href="https://zxi.mytechroad.com/blog">Huahua&#039;s Tech Road</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://zxi.mytechroad.com/blog/searching/8-puzzles-bidirectional-astar-vs-bidirectional-bfs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
