åå 倩çå°äžäžªè§é¢ïŒæç¥éåæ¥ A* äžè¿æ¯èåäºäžäžã广床äŒå æ玢ãïŒBreadth First SearchïŒ&ã深床äŒå æ玢ãïŒDepth First SearchïŒïŒèå·²ð±ãäžç§æ玢ç®æ³ç»æéœæ¯çžäŒŒçïŒ
- æåŸ æ玢çç¶ææŸè¿æ䞪容åšâ±ïžïŒ
- 以æç§æ¹åŒïŒéåãæ ãäŒå éåïŒååºå ¶äžäžäžªç¶æïŒ
- æ£æ¥æ¯åŠæ¯ç»ç¹ðïŒ
- 以æ€åŸªç¯âŠâŠ
èœç¶äžæ³æ·æ玢ðè¿åŒæµæ°ŽïŒäœè¿æ¯å¿äžäœãæåäžå£æ°ãã
æ¥äžªç®åé¢ç»ç»æå§ïŒåªå
讞äžç§è¿ç® â 2
+ 5
-3
ïŒé®åŠäœç± 4
åŸå° 56
ïŒäžæ¯å»è§£äžå
äžæ¬¡äžå®æ¹çšïŒïŒåŠïŒâµå顺åºæå
³ðã
BFS ç¯
ç®äžç©ºè¡ïŒ66 è¡æå®ãç®æ³äžéŸç解ïŒäœåð³ïžè¿æ¯è®å€çïŒè°äºäžæäžðª²ã
class BreadthFirstSearch
def initialize(s, e)
@queue = ãã
@visited = ãã
@edge_from = Hash.new
@s = s
@e = e
end
def enqueue(v)
unless @visited.include? v
@queue << v
@visited << v
end
end
def dequeue
@queue.shift
end
def get_next_states(v)
[
[v * 2, "â2"],
[v + 5, "+5"],
[v - 3, "-3"]
]
end
def bfs
return @e if @s == @e
enqueue(@s)
until @queue.empty?
if @queue.include? @e
return @e
else
v = dequeue()
get_next_states(v).each do |u|
vv, op = u
@edge_from[vv] = [v, op] unless @visited.include? vv
enqueue(vv)
end
end
end
end
def path_to(s, e)
if s == e
return s
else
return [path_to(s, @edge_from[e].first), @edge_from[e].last]
end
end
def find_the_path
path = path_to(@s, @e).join(" ")
return "ð¯: #{path} â¡ïž #{@e}"
end
end
# *2 +5 -3, 4 â¡ïž 56
b = BreadthFirstSearch.new(4, 56)
b.bfs
puts b.find_the_path # ð¯: 4 â2 â2 +5 +5 -3 +5 â2 â¡ïž 56
è· benchmark
åç°ïŒäžäº 10,0000
å°±è·äžåšäºã
Benchmark.bm do |m|
5.times do |n|
big_n = 10**n
m.report(big_n) do
b = BreadthFirstSearch.new(4, big_n)
b.bfs
b.find_the_path
end
end
end
n | user | system | total | real |
---|---|---|---|---|
1 | 0.000027 | 0.000003 | 0.000030 | 0.000025 |
10 | 0.000030 | 0.000001 | 0.000031 | 0.000030 |
100 | 0.000486 | 0.000047 | 0.000533 | 0.000583 |
1000 | 0.005104 | 0.000152 | 0.005256 | 0.005571 |
10000 | 1.429062 | 0.023612 | 1.452674 | 1.497075 |
100000 | 172.746792 | 0.288834 | 173.035626 | 173.562669 |
A* ç¯
æäº bfs
çæ¡æ¶ä¹åïŒå°æ¹å°±å¯ä»¥åŸå° A*
ãåŒå
¥ class PriorityQueue
ïŒ36 è¡ïŒïŒå
± 107 è¡ã
class PriorityQueue
def initialize()
@queue = ãã
end
def add(priority, item)
@queue << {priority: priority, item: item}
@queue.sort_by! { |e| e[:priority] }
self
end
def <<(pritem)
add(*pritem)
end
def empty?
@queue.empty?
end
def include?(v)
@queue.map { |e| return true if e[:item] == v }
return false
end
def deq
@queue.shift
end
def to_s
@queue.to_s
end
def size
@queue.size
end
end
class AstarSearch
def initialize(s, e)
@s = s
@e = e
@pqueue = PriorityQueue.new
@visited = ãã
@edge_from = Hash.new
end
def estimate(v)
(@e - v).abs
end
def enqueue(v)
unless @visited.include? v
@pqueue << [estimate(v), v]
@visited << v
end
end
def dequeue
@pqueue.deq
end
def get_next_states(v)
[
[v * 2, "â2"],
[v + 5, "+5"],
[v - 3, "-3"]
]
end
def bfs
return @e if @s == @e
enqueue(@s)
until @pqueue.empty?
if @pqueue.include? @e
return @e
else
v = dequeue()[:item]
get_next_states(v).each do |u|
vv, op = u
@edge_from[vv] = [v, op] unless @visited.include? vv
enqueue(vv)
end
end
end
end
def path_to(s, e)
if s == e
return s
else
return [path_to(s, @edge_from[e].first), @edge_from[e].last]
end
end
def find_the_path
path = path_to(@s, @e).join(" ")
return "ð¯{#{@depth} steps}: #{path} â¡ïž #{@e}"
end
end
b = AstarSearch.new(4, 56)
b.bfs
puts b.find_the_path # ð¯{8 steps}: 4 â2 â2 +5 +5 -3 +5 â2 â¡ïž 56
n | user | system | total | real |
---|---|---|---|---|
1 | 0.000051 | 0.000006 | 0.000057 | 0.000053 |
10 | 0.000080 | 0.000006 | 0.000086 | 0.000086 |
100 | 0.000666 | 0.000088 | 0.000754 | 0.000784 |
1000 | 0.007072 | 0.000265 | 0.007337 | 0.007460 |
10000 | 0.031566 | 0.001131 | 0.032697 | 0.032835 |
100000 | 32.630846 | 0.800102 | 33.430948 | 33.487605 |
ç¡®å®æ¯ bfs
奜äžç¹ïŒäœä¹è°äžäžç¥åšðªã
奜ææåååŒå§ð¥³
A*
ççæ¯ bfs
奜åïŒæ们æ¥ççç® 4 â¡ïž 1,0000
ïŒ
-
A*
èæ¶ 0.036sïŒ -
bfs
èæ¶ 1.39s ã
ç䌌 A*
å®èïŒäœæ们åæ¥ççæ¥æ°[1]ïŒ
![](https://img.haomeiwen.com/i22586/d144d819e19244f1.png)
A*
ç䌌ç®åŸå¿«ïŒåŽéèŠ 172 æ¥ïŒè¿äžæ¯æäŒè§£ïŒè bfs
åªçš 15 æ¥ïŒèœæ
¢äœåãå¯è§ïŒå¯ååŒç®æ³ä¹äžæ¯é¶åŒ¹ðãå¯ååŒè§å没é奜ïŒè¿äŒåèŸåèŸã
䞺ä»ä¹ (@e - v).abs
è¿æ¡è§åäžéåæ¬é¢ïŒè§å¯äžäžå°±å¯ä»¥åŸå°çæ¡ãæå¿«çæ¥æ°åºè¯¥æ¯æäžäº â2
ç䟿蜊ïŒèæ们éçšçè¿æ¡å¯ååŒè§åèœç¶åé¢è·åŸæ
¢ïŒäœåé¢âµæ¥é¿è¿å€§ïŒåªèœçšçº¿æ§èµ°ïŒå æ€åèåæ
¢ãé£ä¹åŠäœæ¹è¿è¿æ¡å¯ååŒè§åå¢ïŒ
è¯è¯è¿æ¡å¢ïŒè®© v
å°œéé è¿ç©¿è¿ @e
ç 2 çå¹çº¿ïŒ
def estimate(v)
if v < 1 or v > @e
(@e - v).abs
else
(@e - v * (2 ** Math.log2(@e / v.to_f).floor)).abs
end
end
ç®æ³ | æ¥æ° | èæ¶ |
---|---|---|
A*(1,0000) | 19 | 0.001 |
bfs(1,0000) | 14 | 1.254 |
å¯è§ïŒA*
é床é£æ¯æ²¡åŸè¯ŽïŒäœå®äžååïŒâŽè¿æ¯éåé£äºç²ŸåºŠèŠæ±äžé«çå°æ¹ã
æ们æ¥èå¯äžåä¹çãå¯èœãæçè·¯åŸé¿åºŠïŒ
â³ïžèµ·ç¹ | ðç»ç¹ | ãå¯èœãæçè·¯åŸ |
---|---|---|
0 | 3 | 1 +5 -3 -3 |
1 | 0 | 1 |
2 | 1 | 1 â2 |
3 | 2 | 1 +5 -3 |
4 | 2 | 1 â2 â2 |
5 | 4 | 1 â2 â2 â2 -3 |
6 | 1 | 1 +5 |
7 | 2 | 1 â2 +5 |
8 | 3 | 1 â2 â2 â2 |
9 | 3 | 1 â2 â2 +5 |
å ä¹æ²¡æä»ä¹è¶å¿ïŒè¿å°±æ¯äžºä»ä¹æ¬é¢çå¯ååŒéŸåçåå å§ã
æ们çè³å¯ä»¥å°è¯çšãå¯èœæçè·¯åŸçæ°åŠè§£ãäœäžºè¯äŒ°åœæ°ïŒäœææä»ç¶äžçæ³ïŒåèæäœäºé床ïŒè¿æ¯äžäž bfs
ïŒïŒ
ç®æ³ | æ¥æ° | èæ¶ |
---|---|---|
A*(1,0000) | 18 | 2.06 |
bfs(1,0000) | 14 | 1.24 |
èäžè¯¯å·®å€§å°äžèœçïŒ
![](https://img.haomeiwen.com/i22586/a3fe43428a649d93.png)
def exgcd(a, b)
# solve a * x + b * y = gcd(a, b)
return [1, 0] if b == 0
x, y = exgcd(b, a % b)
[y, x - (a / b) * y]
end
def solve_lde_positive(a, b, c)
# solve the linear Diophantine equation: a * x + b * y = c
# s.t. min{ x + y }
gcd = a.gcd(b)
x_0, y_0 = exgcd(a, b)
x_0 *= (c / gcd)
y_0 *= (c / gcd)
# x >= 0 and y >= 0
t = (gcd * y_0 / a.to_f).ceil
x = - x_0 - t * b / gcd
y = - y_0 + t * a / gcd
[x, y]
end
def estimate(v)
if v < 1 or v > @e
(@e - v).abs
else
steps_2n = Math.log2(@e / v.to_f).floor
p_2n = @e / 2 ** steps_2n
steps_linear_v2p = solve_lde_positive(5, -3, p_2n - v).sum
steps_linear_s2v = solve_lde_positive(5, -3, v - @s).sum
# v -> p_2n -> @e"
steps_linear_s2v + steps_linear_v2p + steps_2n
end
end
å¯ä»¥ç¬ç®åïŒ
é®é¢äžïŒå¯ä»¥éåä»»äžæ°åïŒ
çäžïŒå¯ä»¥çãåªèŠ äžæ»¡è¶³
ãå
·äœçè¯ææåŸåäºãç¶åå¯ä»¥çšãæ©å±èŸèœ¬çžé€æ³ã解è¿äžªäºå
äžæ¬¡äžå®æ¹çšãåªèŠæ»¡è¶³
å°±è¡äºãæ¥çéèŠå»èŽŽè¿ç©¿è¿ç»ç¹ðç 2 次å¹çº¿ãäœè¿ç§è§£æ³ç猺ç¹æ¯äžèœä¿è¯è·¯åŸæçã
é®é¢äºïŒåŠæå 讞çšæ¬å·æ¹å计ç®é¡ºåºïŒåŠäœåŸå°æçè·¯åŸïŒ
çäºïŒæ³äžåºæ¥ðã
æèð€
äžåŒå§ @panð³ ç»æåºè¿é¢çæ¶åïŒæ¬äžä»¥äžºæïŒç»æå€çèµ·æ¥è¿æºæåŒå²ãæµ ïŒå¯ä»¥ååºæ¥ãäžçéŸåºŠïŒå¯ä»¥è¯è¯æ玢ç®æ³ & æ°æ®å¯è§åãæ·±ïŒå¯ä»¥å°è¯çšæ°è®ºå»è§£ãæéèŠçæ¯ïŒè®©æéæ°è®€è¯äºå¯ååŒç®æ³çäŒçŒºç¹ & éçšåºæ¯ã
莎䞪垊 debug ççæ¬å§ïŒå¯ä»¥éè¿ DEBUG
åŒå
³è°è¯ä¿¡æ¯ïŒïŒ
require "benchmark"
DEBUG = true
class PriorityQueue
def initialize()
@queue = ãã
end
def add(priority, item)
@queue << {priority: priority, item: item}
@queue.sort_by! { |e| e[:priority] }
self
end
def <<(pritem)
add(*pritem)
end
def empty?
@queue.empty?
end
def include?(v)
@queue.map { |e| return true if e[:item] == v }
return false
end
def deq
@queue.shift
end
def to_s
@queue.to_s
end
def size
@queue.size
end
end
class AstarSearch
def initialize(s, e)
@s = s
@e = e
@pqueue = PriorityQueue.new
@visited = ãã
@edge_from = Hash.new
@depth = 0 # ðæ£æ¥éåœæ·±åºŠçšã
end
def estimate(v)
(@e - v).abs
end
def enqueue(v)
unless @visited.include? v
@pqueue << [estimate(v), v]
@visited << v
end
end
def dequeue
@pqueue.deq
end
def get_next_states(v)
DEBUG && puts("q â¬
<#{v} â 2 = ð§©#{v * 2} >, <#{v} + 5 = ð§©#{v + 5} >, <#{v} - 3 = ð§©#{v - 3} >")
[
[v * 2, "â2"],
[v + 5, "+5"],
[v - 3, "-3"]
]
end
def bfs
DEBUG && puts("ð: #{@pqueue}")
DEBUG && puts("ð©: #{@visited}")
DEBUG && puts("ðº: #{@edge_from}")
DEBUG && puts
return @e if @s == @e
enqueue(@s)
until @pqueue.empty?
DEBUG && puts("ð: #{@pqueue}")
DEBUG && puts("ð©: #{@visited}")
DEBUG && puts("ðº: #{@edge_from}")
DEBUG && puts
if @pqueue.include? @e
return @e
else
v = dequeue()[:item]
get_next_states(v).each do |u|
vv, op = u
@edge_from[vv] = [v, op] unless @visited.include? vv
enqueue(vv)
end
end
end
end
def path_to(s, e)
@depth += 1
DEBUG && puts("#{'ðª' * @depth}\t<#{s}, #{e}>")
if s == e
return s
else
return [path_to(s, @edge_from[e].first), @edge_from[e].last]
end
end
def find_the_path
path = path_to(@s, @e).join(" ")
DEBUG && puts
DEBUG && puts("#ð: #{@pqueue.size}")
DEBUG && puts("#ð©: #{@visited.size}")
DEBUG && puts("#ðº: #{@edge_from.size}")
DEBUG && puts("#{'ðª' * @depth}")
return "ð¯{#{@depth} steps}: #{path} â¡ïž #{@e}"
end
end
# *2 +5 -3, 4 â¡ïž 56
b = AstarSearch.new(4, 56)
b.bfs
puts b.find_the_path # ð¯{8 steps}: 4 â2 â2 +5 +5 -3 +5 â2 â¡ïž 56
æå莎䞪å°åŸðºç A* ïŒæåèŸåºå®åšè§£å³äžäºïŒç¡¬çŒç äºäžð ïŒïŒ
require "open-uri"
class PriorityQueue
def initialize()
@queue = ãã
end
def add(priority, item)
@queue << {priority: priority, item: item}
@queue.sort_by! { |e| e[:priority] }
self
end
def <<(pritem)
add(*pritem)
end
def empty?
@queue.empty?
end
def include?(v)
@queue.map { |e| return true if e[:item] == v }
return false
end
def deq
@queue.shift
end
def to_s
@queue.to_s
end
def size
@queue.size
end
end
class Point
attr_accessor :x, :y
def initialize(ary)
@x, @y = ary # Should be [x, y] .
end
def ==â
self.x == p.x &&
self.y == p.y
end
def to_a
[@x, @y]
end
# def to_s
# "ð<#{@x}, #{@y}>"
# end
# def inspect
# "ð<#{@x}, #{@y}>"
# end
end
class AstarSearch
def initialize(map2d, s, e)
@map2d = map2d
@s = s
@e = e
@pqueue = PriorityQueue.new
@visited = ãã
@edge_from = Hash.new
end
def estimate(v)
[(@e.x - v.x).abs, (@e.y - v.y).abs].max + @map2d[v.x][v.y]
end
def enqueue(v)
unless @visited.include? v
@pqueue << [estimate(v), v]
@visited << v
end
end
def dequeue
@pqueue.deq
end
def get_next_states(v)
x, y = v.x, v.y
[
Point.new([x - 1, y - 1]), Point.new([x - 1, y]), Point.new([x - 1, y + 1]),
Point.new([x, y - 1]), Point.new([x, y + 1]),
Point.new([x + 1, y - 1]), Point.new([x + 1, y]), Point.new([x + 1, y + 1])
]
end
def bfs
return @e if @s == @e
enqueue(@s)
until @pqueue.empty?
if @pqueue.include? @e
return @e
else
v = dequeue()[:item]
get_next_states(v).each do |vv|
@edge_from[vv] = v unless @visited.include? vv
enqueue(vv)
end
end
end
end
def path_to(h, s, e)
if s == h[e]
return [s]
else
return path_to(h, s, h[e]) << h[e]
end
end
def find_the_path
h = {}
@edge_from.map { |k, v| h[k.to_a] = v.to_a }
path = path_to(h, @s.to_a, @e.to_a) << @e.to_a
return path
end
end
# Movement Cost for Terrain:
# Non-walkable:
# N/A = Water (~ð)
# Walkable:
# 1 = Flatlands (. or @ or X )
# 2 = Forest (*ð²)
# 3 = Mountain (^â°ïž)
# Test Map:
# @*^^^ @ = User start â³ïž
# ~~*~. X = The goal tile ð°
# **...
# ^..*~
# ~~*~X
# â³ïžâïžð²âïžâïž
# âïžâïžðâïžâïž
# âïžâïžâ°ïžâïžð°
#
# â¬ïž
#
#=> ð©ð©ð©âïžâïž
#=> âïžâïžðð©âïž
#=> âïžâïžâ°ïžâïžð©
# m = <<~EOF
# @.*..
# ..~..
# ..^.X
# EOF
m = open("http://www.rubyquiz.com/large_map.txt").read
ASCII2INT = {
"@" => 1,
"X" => 1,
"." => 1,
"*" => 2,
"^" => 3,
"~" => 9999
}
INT2EMOJI = {
1 => "âïž",
2 => "ð²",
3 => "â°ïž",
9999 => "ð"
}
def preprocess_map(m)
map2d = ãã
# ååšæ³šæ°Žðððð
map2d << Array.new(m.split(/\n/).first.size + 2, ASCII2INT["~"])
start, goal = nil
m.each_line.with_index do |line, I|
ary = [ASCII2INT["~"]]
line.chomp.each_char.with_index do |e, j|
ary << ASCII2INT[e]
start = [i + 1, j + 1] if e == "@"
goal = [i + 1, j + 1] if e == "X"
end
ary << ASCII2INT["~"]
map2d << ary
end
map2d << Array.new(m.split(/\n/).first.size + 2, ASCII2INT["~"])
[map2d, start, goal]
end
map2d, start, goal = preprocess_map(m)
b = AstarSearch.new(map2d, Point.new(start), Point.new(goal))
b.bfs
emoji_map = map2d.map { |row| row.map { |v| INT2EMOJI[v] } }
b.find_the_path.each { |u| v = Point.new(u); emoji_map[v.x][v.y] = "ð©" }
s = Point.new(start)
e = Point.new(goal)
emoji_map[s.x][s.y] = "â³ïž"
emoji_map[e.x][e.y] = "ð°"
emoji_map.each do |row|
puts row.join
end
ðððððððððððððððððððððððððððððððððððððððððððððððððððð
ðâ³ïžâ°ïžâïžâ°ïžðð²ð²ð²ð²â°ïžð²ððâïžðâ°ïžðâïžâïžðððâ°ïžðâïžâ°ïžðâïžð²ðð²ð²âïžâ°ïžâ°ïžð²â°ïžð²â°ïžðâïžâïžð²â°ïžâ°ïžâïžâïžðâ°ïžâïžð
ðð²ð©ð²ðð²âïžðâ°ïžâ°ïžâ°ïžððâïžð²ðð²âïžð²ð²âïžððâ°ïžð²â°ïžð²ð²âïžâ°ïžðð²â°ïžâ°ïžâïžð²â°ïžâïžâïžâïžâ°ïžâïžâïžâ°ïžâïžð²ð²âïžðâ°ïžð²ð
ðâ°ïžâ°ïžð©ð²ð²ðâïžð²â°ïžð²â°ïžâïžâïžâ°ïžð²ð²âïžðððâïžðð²âïžðâ°ïžâ°ïžðâ°ïžðâ°ïžâïžâ°ïžðâ°ïžð²ðð²ð²âïžðð²â°ïžâïžâ°ïžð²ð²âïžð²âïžð
ðð²ððð©ðâ°ïžâïžðð²ðâ°ïžðâ°ïžâ°ïžð²â°ïžð²ð²âïžðâïžð²â°ïžâ°ïžð²ððâ°ïžâ°ïžð²ðâïžâ°ïžâïžð²â°ïžâ°ïžð²â°ïžâïžâ°ïžâïžððâ°ïžâ°ïžâ°ïžð²ðâïžð
ðð²âïžâ°ïžðð©â°ïžâïžðâ°ïžâïžâ°ïžâïžâ°ïžâ°ïžð²ðâ°ïžððð²â°ïžâïžâïžâ°ïžðâ°ïžððâ°ïžâïžð²â°ïžâ°ïžâïžâïžð²ð²âïžð²ð²ðâïžððâ°ïžðð²ðð²ð²ð
ðâ°ïžâïžâ°ïžâ°ïžðð©âïžð²ðð²ð²âïžð²ðð²â°ïžð²ðâ°ïžð²ððâ°ïžâïžâ°ïžâïžðâïžðâ°ïžð²ð²ðâïžâ°ïžâ°ïžâ°ïžâ°ïžð²âïžðâïžðððâ°ïžâ°ïžâïžâ°ïžâïžð
ðð²ððâïžð²âïžð©ðððâ°ïžð²âïžâ°ïžð²ðððð²â°ïžâïžâ°ïžð²ð²â°ïžð²â°ïžâïžâ°ïžâïžâ°ïžððð²ð²ð²â°ïžâ°ïžð²â°ïžâïžðâ°ïžâ°ïžâïžâ°ïžâ°ïžâïžðâïžð
ðâïžðâïžð²ððâ°ïžð©âïžð²ð²âïžðâ°ïžâ°ïžðð²ð²â°ïžâïžâ°ïžâïžâ°ïžðððâ°ïžâïžðâïžâ°ïžððâ°ïžâ°ïžâïžðâ°ïžâ°ïžâïžâ°ïžâ°ïžðâïžðâïžðâïžð²â°ïžð
ðâïžâ°ïžâ°ïžâ°ïžððð²ðð©â°ïžâïžðâïžð²ðâïžððâïžâïžðð²ðâ°ïžâïžðð²ð²ðâïžâïžâ°ïžð²ð²ð²ð²ðâïžð²ðâ°ïžððð²ðð²ð²â°ïžðâ°ïžð
ðâ°ïžâ°ïžâ°ïžð²â°ïžâ°ïžð²ð²âïžð©ð©ð²âïžâ°ïžâ°ïžð²â°ïžâ°ïžâ°ïžâïžð²ðð²ðâ°ïžâ°ïžðð²ððâïžðð²ð²ð²ð²ððððð²ð²ð²âïžâ°ïžâ°ïžððâ°ïžðð
ðâïžâïžð²â°ïžâ°ïžâ°ïžâ°ïžâ°ïžâïžâ°ïžðð©â°ïžðâïžâ°ïžâïžâ°ïžâ°ïžðð²ðâ°ïžð²ðð²ð²â°ïžð²â°ïžâïžðððð²â°ïžâïžâ°ïžâ°ïžðð²ð²ðð²ðâïžâïžâïžâïžâïžð
ðð²ð²âïžâ°ïžðððâ°ïžðð²ðð©ð²ð²ð²âïžð²ð²â°ïžðâïžâ°ïžâïžð²â°ïžâ°ïžâ°ïžðâïžâ°ïžâïžâïžâïžðâïžð²ð²â°ïžâ°ïžâ°ïžâ°ïžðâ°ïžâïžðâ°ïžðâïžðð²ð
ðâ°ïžð²ðâïžð²âïžðð²â°ïžâïžðð©â°ïžâ°ïžâ°ïžâ°ïžâ°ïžð²ðâïžð²ð²ðâ°ïžâïžð²â°ïžð²âïžððâïžâïžâ°ïžðððð²ðð²â°ïžâïžððâ°ïžð²â°ïžð²â°ïžâ°ïžð
ððð²â°ïžâïžâ°ïžâïžâïžð²ð²ðð²ð²ð©ð©â°ïžðð²ð²ð²â°ïžððð²â°ïžð²âïžð²ðâïžâïžðâ°ïžâ°ïžð²ð²ð²â°ïžâïžð²ðâïžâ°ïžð²â°ïžâ°ïžâ°ïžâ°ïžâïžðâïžð
ðð²ðâ°ïžðâ°ïžð²ð²â°ïžâ°ïžð²â°ïžâ°ïžðâ°ïžð©ðâ°ïžâ°ïžð²ðððð²â°ïžð²ððððð²ðâ°ïžâ°ïžâ°ïžð²ðâïžâïžðððððâïžð²ðâ°ïžðð²âïžð
ðâ°ïžâïžð²âïžâ°ïžð²â°ïžð²ð²â°ïžâ°ïžâ°ïžðð²ð²ð©ð²âïžðâ°ïžðððâ°ïžâïžâïžðððð²ððâ°ïžð²ðâïžâïžâ°ïžâ°ïžâïžâ°ïžðð²âïžâ°ïžðâ°ïžðð²ð²ð
ðâïžð²ð²ð²â°ïžâïžâïžð²â°ïžððððâ°ïžðâïžð©â°ïžððâïžð²âïžðâ°ïžâïžâ°ïžâ°ïžð²âïžð²ð²ð²â°ïžðâ°ïžâïžð²âïžâïžâïžâïžðâïžâ°ïžâïžð²ðâ°ïžâ°ïžð
ðâ°ïžâ°ïžððððâïžð²ð²âïžð²âïžâ°ïžâ°ïžð²ð©â°ïžððâ°ïžâïžâïžâïžâïžð²ðð²â°ïžðð²â°ïžâ°ïžâïžâ°ïžðððâ°ïžð²âïžðâ°ïžâ°ïžâ°ïžðð²ð²â°ïžððð
ðâ°ïžððð²â°ïžð²âïžðâïžð²â°ïžâ°ïžâ°ïžð²â°ïžâïžð©ð©âïžðð²âïžâïžâïžðð²ð²â°ïžâïžâ°ïžâ°ïžðâïžâ°ïžâ°ïžâïžâ°ïžâïžâïžâ°ïžâïžâ°ïžð²ð²âïžâ°ïžâ°ïžâïžâïžð²ð
ððâ°ïžð²ð²ð²ðâ°ïžâïžðâïžðâ°ïžâ°ïžâ°ïžð²ðððð©ð²âïžâïžâ°ïžðâ°ïžâ°ïžâïžðâ°ïžâïžðâïžð²ð²âïžâïžâïžðâ°ïžðð²ð²ðâ°ïžððð²ð²â°ïžðð
ððððâ°ïžðâïžâ°ïžð²âïžâ°ïžð²âïžðð²âïžð²ððâ°ïžð©âïžðð²â°ïžâ°ïžððð²ð²âïžð²âïžð²âïžðâ°ïžâ°ïžâ°ïžâïžâïžâ°ïžâïžðâ°ïžâïžð²âïžâ°ïžððð
ðâ°ïžâ°ïžðâïžâïžâïžâ°ïžâïžð²ðâïžâ°ïžâ°ïžð²ð²ðâ°ïžðð²âïžð©â°ïžðâ°ïžðð²âïžâïžâïžâïžâ°ïžâïžâ°ïžâ°ïžð²ð²ðâïžð²âïžâ°ïžâ°ïžð²âïžâïžðð²ðâïžð²ð
ðâ°ïžâïžâïžâ°ïžâïžâïžâïžðâïžðâïžð²âïžðð²ð²ð²ðð²ððð©ð©âïžðð²ðâ°ïžâ°ïžððâ°ïžðð²ð²â°ïžððâ°ïžð²â°ïžâ°ïžðð²âïžðð²â°ïžð²â°ïžð
ðð²â°ïžâïžð²ð²â°ïžðð²ð²ð²ð²ð²âïžðððâïžâïžðâ°ïžðâïžð²ð©â°ïžð²ðâ°ïžâïžâ°ïžâ°ïžð²â°ïžâïžâïžð²ðâ°ïžâïžâ°ïžâïžâ°ïžð²âïžâ°ïžâïžðâ°ïžâ°ïžð²ð
ððâïžâïžâïžðð²â°ïžâïžâïžâïžâïžð²â°ïžâïžð²â°ïžâ°ïžð²âïžâïžâïžâ°ïžð©ððâïžâïžâ°ïžâïžð²ðâïžð²ðâïžð²â°ïžâïžâ°ïžð²â°ïžð²â°ïžâïžð²ð²ð²ð²â°ïžðð
ðâ°ïžâïžâïžðð²ð²ð²â°ïžâïžð²â°ïžððâïžð²ð²ð²ð²âïžðð²â°ïžð©ðð©â°ïžðð²âïžðâ°ïžð²â°ïžðâ°ïžâïžð²ð²ð²ð²ðâïžâïžðð²âïžâïžð²â°ïžðð
ðâïžðâ°ïžðð²ð²ðâ°ïžâ°ïžâïžâïžðððâ°ïžâïžâïžð²ðð²âïžâ°ïžð²ð©ðð©âïžâ°ïžâ°ïžð²âïžðâïžð²âïžâïžð²ððð²ðâïžâ°ïžðâïžðð²ððâïžð
ðâïžð²ð²ð²ðâïžâïžðð²ð²â°ïžâïžðâïžðâïžðð²âïžððð²ð²âïžâïžâ°ïžð©â°ïžðâïžâïžðð²ððððð²âïžð²ð²ððâ°ïžâïžâïžâ°ïžâïžâ°ïžâ°ïžð
ððâïžð²âïžðâ°ïžð²â°ïžð²ðâ°ïžâïžðð²âïžâïžð²ðâ°ïžððâïžð²âïžð²âïžâïžð©âïžð²âïžâïžâ°ïžðâïžð²ðâ°ïžâ°ïžð²âïžðâ°ïžâïžâ°ïžðâ°ïžð²ð²â°ïžð
ðâïžð²â°ïžðâ°ïžâ°ïžð²âïžâ°ïžð²âïžðð²âïžâïžâïžð²ðððð²âïžð²ð²âïžðâïžâïžð©ð²âïžð²â°ïžâïžâ°ïžð²âïžâ°ïžð²ðð²âïžâ°ïžðâ°ïžð²ð²â°ïžð²âïžð
ðâïžðâïžâïžð²âïžâïžâïžâïžðâïžâïžðâïžð²ð²ð²ðâïžâïžðâ°ïžâïžâïžððâ°ïžð²â°ïžð©â°ïžðâ°ïžâ°ïžððð²ð²â°ïžâïžð²ðð²ð²â°ïžð²ð²â°ïžâ°ïžð²ð
ðâ°ïžâïžð²âïžðâïžâ°ïžâïžð²ð²â°ïžð²â°ïžâïžðâ°ïžâ°ïžð²âïžðâïžâïžð²â°ïžðð²â°ïžð²ð²ð²ð©ðð²ð²âïžâïžâ°ïžâïžððð²âïžâïžâ°ïžð²â°ïžðð²âïžðð
ðâ°ïžððâ°ïžððð²âïžðâ°ïžâ°ïžððâ°ïžð²ð²âïžâ°ïžâïžâ°ïžâ°ïžâ°ïžð²â°ïžâïžð²â°ïžâ°ïžððð©â°ïžð©ð²ðâ°ïžð²â°ïžâ°ïžâïžâïžð²ð²âïžâïžð²ð²âïžâïžâ°ïžð
ðâ°ïžâïžâ°ïžð²âïžð²â°ïžâïžâïžð²âïžððâïžâ°ïžâ°ïžð²ð²ð²â°ïžâïžð²ðâïžâïžðâïžð²ð²â°ïžð²ð©ðð©âïžâïžâïžâ°ïžâ°ïžðð²âïžðâ°ïžðâ°ïžâïžâïžððð
ððâ°ïžâ°ïžâïžâ°ïžâïžâïžð²â°ïžâ°ïžâïžð²ð²âïžððâ°ïžð²ðð²â°ïžðð²ðâ°ïžððâïžðð²âïžâ°ïžðð©ðð²â°ïžðð²âïžð²âïžâïžâ°ïžâïžâ°ïžâ°ïžð²â°ïžð²ð
ðâ°ïžâ°ïžâïžðð²â°ïžððð²âïžððð²ðâïžâ°ïžâïžâïžðð²âïžâ°ïžâïžðð²ð²âïžâ°ïžð²â°ïžâïžâ°ïžðâïžð©ð©âïžðð²âïžðâïžâïžâïžððâïžâïžð²âïžð
ðâïžðâïžâïžâ°ïžâïžðâïžðâïžâ°ïžð²âïžðâ°ïžð²ðâïžðâïžð²â°ïžð²ððð²âïžâ°ïžâïžð²ð²ð²ð²ð²ð²ðð©ððð²â°ïžðððâ°ïžâïžððð²ðð
ððâïžð²âïžâïžâ°ïžâ°ïžð²â°ïžðð²âïžððâïžðâïžâ°ïžðâïžâïžðâïžðâ°ïžâ°ïžâ°ïžâïžð²ðð²âïžð²ð²ðâ°ïžð©ðð²â°ïžð²â°ïžâ°ïžðâïžâïžâ°ïžðâ°ïžâ°ïžð
ðð²âïžâ°ïžð²â°ïžð²ð²â°ïžð²âïžâ°ïžâ°ïžð²âïžâïžâïžâïžð²ð²âïžâïžðâ°ïžâ°ïžðâïžâ°ïžð²â°ïžâïžð²ðð²â°ïžð²ð²â°ïžð©ð©ð©ð²âïžâ°ïžâ°ïžð²â°ïžâ°ïžâ°ïžâïžðð
ðâ°ïžð²â°ïžðâ°ïžâ°ïžð²âïžðð²ðâ°ïžð²ðâ°ïžððâïžð²âïžâ°ïžð²â°ïžðð²â°ïžâ°ïžðâïžð²ðâïžð²ðâïžð²ð²âïžð²ðð©ððâïžðð²â°ïžððð²ð
ðððð²ð²ðð²âïžâ°ïžâïžð²ðâïžâïžððâ°ïžâ°ïžâ°ïžðâ°ïžâ°ïžâ°ïžâïžðð²ð²ð²â°ïžð²âïžâ°ïžð²ðâ°ïžð²ðâ°ïžðð²ðð©ð²âïžâïžâïžðâïžâïžðâïžð
ðð²ðð²ð²â°ïžððâïžâ°ïžâïžð²âïžðâ°ïžð²ð²ðð²â°ïžâ°ïžâïžð²â°ïžð²âïžâ°ïžððð²ðððð²â°ïžâïžð²âïžâïžððâ°ïžð©â°ïžâïžð²â°ïžâïžâ°ïžâïžð²ð
ðâïžð²â°ïžðâïžâïžð²ðâïžð²â°ïžâ°ïžâ°ïžâ°ïžâ°ïžððâ°ïžâ°ïžð²âïžðð²âïžðððâïžð²ð²ð²ðâ°ïžâïžð²âïžâïžððð²ð²ð²ð©ð©ð²ððâ°ïžð²âïžð
ðâïžâ°ïžâ°ïžâïžâïžâ°ïžâïžâ°ïžð²â°ïžðâ°ïžâïžðð²âïžâïžâïžð²ð²ðððâïžð²ð²â°ïžð²ðððð²â°ïžð²ð²ð²ðâ°ïžð²â°ïžðâ°ïžððð©â°ïžâ°ïžð²âïžð²ð
ðð²ð²âïžð²ð²ðâïžð²ð²ðâïžð²âïžð²â°ïžððâïžðâ°ïžâïžð²â°ïžâïžððð²âïžâ°ïžð²âïžðâïžð²ð²ð²ðð²â°ïžâ°ïžâïžâïžðð²ð©ðð²â°ïžð²â°ïžð
ððâïžâïžâ°ïžâïžð²ð²ð²ð²â°ïžâïžð²ð²ð²ð²ðâ°ïžðð²ð²ð²âïžâïžâ°ïžâ°ïžâ°ïžâ°ïžð²â°ïžâïžðð²â°ïžâ°ïžð²âïžððâïžâ°ïžð²ð²â°ïžð²ðð©ð²âïžððð
ðâ°ïžð²ðâ°ïžâïžðð²âïžâïžâïžâ°ïžâ°ïžâïžâ°ïžðâïžâ°ïžâ°ïžâïžâïžðâ°ïžâïžâïžâïžð²ð²âïžâïžâïžâ°ïžð²ð²ð²ð²â°ïžð²ðð²ðð²â°ïžâïžâïžðâ°ïžð©ðâïžðð
ðâïžâïžâïžâ°ïžðâ°ïžâïžðâ°ïžâ°ïžððððâïžð²â°ïžðâïžâ°ïžðð²ð²ð²ððâ°ïžâ°ïžâ°ïžâïžð²â°ïžâ°ïžâïžðâïžâ°ïžð²ðð²ðð²ð²â°ïžð²ð²ð©â°ïžðâ°ïžð
ðâïžðððððâ°ïžâ°ïžâïžððð²â°ïžâïžðâ°ïžâ°ïžâ°ïžð²ð²ðâ°ïžðâïžâ°ïžðâ°ïžðð²âïžâïžâ°ïžð²ðâ°ïžâ°ïžð²âïžâïžð²ðâ°ïžðð²ð²âïžð²ð©ð©âïžð
ððâ°ïžð²ð²âïžðð²ð²âïžâïžð²â°ïžðâ°ïžâ°ïžâ°ïžâïžâ°ïžð²â°ïžðâ°ïžðð²â°ïžâïžðð²ðâïžâ°ïžâïžð²ð²âïžâ°ïžâïžâ°ïžâ°ïžâïžðð²ð²â°ïžððâ°ïžðâ°ïžð°ð
ðððððððððððððððððððððððððððððððððððððððððððððððððððð
Ref:
- äžçŸè¡ Ruby å䞪 A* ïŒ
- æ©å±æ¬§å éåŸç®æ³ïŒExtended Euclidean AlgorithmïŒïŒ
- äºå äžæ¬¡äžå®æ¹çšïŒ
-
ãçšæ£ç¹åŸæ¥è§å¯ãè¿äžªè£èªå±äº @panð³ ãèœç¶
A*
æ¬èº«å°±æ¯ä»¥ç®å°åŸðºæçè·¯åŸé»åïŒäœæå°±æ¯æ²¡æèæ³å°ðãæç»åžžè¯Ž @panð³ æ¯éè·¯åïŒäœäžåŸäžæ¿è®€ææ§æ¯ççé«ð±ã â©
çœåè¯è®º