A_Star 算法

浪尽此生 提交于 2020-03-27 09:46:57

3 月,跳不动了?>>>

摘录A*算法的erlang实现

-module(a_star).
-export([main/0]).

astar(Start,Goal) ->
  Closedset = sets:new(), % The set of nodes already evaluated.
  Openset = sets:add_element(Start,sets:new()), %The set of tentative nodes to be evaluated

  Fscore = dict:append(Start, h_score(Start), dict:new()),
  Gscore = dict:append(Start, 0, dict:new()), % Distance from start along optimal path.

  CameFrom = dict:append(Start, none, dict:new()),

  astar_step(Goal, Closedset, Openset, Fscore, Gscore, CameFrom).

astar_step(Goal, Closedset, Openset, Fscore, Gscore, CameFrom) ->
  case sets:size(Openset) of
	0 ->
	  failure;
	_ ->
	  case best_step(sets:to_list(Openset), Fscore, none, infinity) of
		Goal ->
		  reconstruct_path(CameFrom, Goal);
		X ->
		  NextOpen = sets:del_element(X, Openset),
		  NextClosed = sets:add_element(X, Closedset),
		  Neighbours = neighbour_nodes(X),
		  {NewOpen, NewF, NewG, NewFrom} = scan(X, Neighbours, NextOpen, NextClosed, Fscore, Gscore, CameFrom),
		  astar_step(Goal, NextClosed, NewOpen, NewF, NewG, NewFrom)
	  end
  end.

scan(_X, [], Open, _Closed, F, G, From) ->
  {Open, F, G, From};
scan(X, [Y|N], Open, Closed, F, G, From) ->
  case sets:is_element(Y, Closed) of
	true ->
		scan(X, N, Open, Closed, F, G, From);
	false ->
	  [G0] = dict:fetch(X, G),
	  TrialG = G0 + dist_between(X,Y),
	  case sets:is_element(Y, Open) of
		true ->
		  [OldG] = dict:fetch(Y, G),
		  case TrialG < OldG of
			true ->
			  {NewF, NewG, NewFrom} = update(X, Y, F, G, From, TrialG),
			  scan(X, N, Open, Closed, NewF, NewG, NewFrom);
			false ->
				scan(X, N, Open, Closed, F, G, From)
		  end;
		false ->
		  NewOpen = sets:add_element(Y, Open),
		  {NewF, NewG, NewFrom} = update(X, Y, F, G, From, TrialG),
		  scan(X, N, NewOpen, Closed, NewF, NewG, NewFrom)
	  end
  end.

update(X, Y, OldF, OldG, OldFrom, GValue) ->
  KeyF = dict:is_key(Y, OldF),
  KeyG = dict:is_key(Y, OldG),
  KeyFrom = dict:is_key(Y, OldFrom),
  case {KeyF, KeyG, KeyFrom} of
	{true, _, _} ->
	  update(X, Y, dict:erase(Y, OldF), OldG, OldFrom, GValue);
	{_, true, _} ->
	  update(X, Y, OldF, dict:erase(Y, OldG), OldFrom, GValue);
	{_, _, true} ->
	  update(X, Y, OldF, OldG, dict:erase(Y, OldFrom), GValue);
	_ ->
	  NewFrom = dict:append(Y, X, OldFrom),
	  NewG = dict:append(Y, GValue, OldG),
	  NewF = dict:append(Y, GValue + h_score(Y), OldF), % Estimated total distance from start to goal through y.
	  {NewF, NewG, NewFrom}
  end.

reconstruct_path(CameFrom, Node) ->
  case dict:fetch(Node, CameFrom) of
	[none] ->
	  [Node];
	[Value] ->
	  [Node | reconstruct_path(CameFrom, Value)]
  end.

best_step([H|Open], Score, none, _) ->
  [V] = dict:fetch(H, Score),
  best_step(Open, Score, H, V);
best_step([], _Score, Best, _BestValue) ->
  Best;
best_step([H|Open], Score, Best, BestValue) ->
  [Value] = dict:fetch(H, Score),
  case Value < BestValue of
	true ->
	  best_step(Open, Score, H, Value);
	false ->
	  best_step(Open, Score, Best, BestValue)
  end.

%% specialize for the torch-carrier problem
%% bits 0-4 represent torch, 1m, 2m, 5m, 10m
%% set bit = on the near side

%% Every possible crossing of one or two
swaps() ->
  [3,5,9,17, 7, 11,13, 19,21,25].

crossing_time(Swap) ->
  if
	Swap band 16 > 0 ->
	  10;
	Swap band 8 > 0 ->
	  5;
	Swap band 4 > 0 ->
	  2;
	true ->
	  1
  end.

%% presentation form
display(Swap) ->
  if
	Swap band 16 > 0 ->
	  compound(Swap, 16);
	Swap band 8 > 0 ->
	  compound(Swap, 8);
	Swap band 4 > 0 ->
	  compound(Swap, 4);
	Swap band 2 > 0 ->
	  compound(Swap, 2);
	true ->
	  ""
  end.

compound(Swap, Bit) ->
  string:concat( erlang:integer_to_list(crossing_time(Swap)),
	decorate(display(Swap bxor Bit))).

decorate(Value) ->
  case string:len(Value) of
	0 ->
	  Value;
	_ ->
	  string:concat("+", Value)
  end.

neighbour_nodes(X) ->
  Result = [],
  compatible(X, equivalent_point(X), Result, swaps()).

equivalent_point(X) ->
  case X band 1 of
	1 ->
		X;
	0 ->
	  31 bxor X
  end.

compatible(_X, _Y, Outlist, []) ->
  Outlist;
compatible(X, Y, Outlist, [Swap|Inlist]) ->
  case (Y band Swap) of
	Swap ->
	  New = X bxor Swap,
	  compatible(X, Y, [New|Outlist], Inlist);
	_ ->
	  compatible(X, Y, Outlist, Inlist)
  end.

dist_between(X,Y) ->
  Swap = X bxor Y,
  crossing_time(Swap).

h_score(Node) ->
  crossing_time(Node).

main() ->
  [H|Trace] = lists:reverse(astar(31, 0)),
  Time = print_result(Trace, H, 0),
  io:format("Time taken = ~B minutes~n", [Time]) .

print_result([], _Prev, Time) ->
  Time;
print_result([H|Trace], Prev, Time) ->
  Swap = H bxor Prev,
  print_swap(Swap, H band 1),
  Interval = Time + crossing_time(Swap),
  print_result(Trace, H, Interval).

print_swap(Swap, Side) ->
  case Side of
	0 ->
	  io:format(  "    ~s -->;~n", [display(Swap)]);
	1 ->
	  io:format(  "<-- ~s~n", [display(Swap)])
  end.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!