Dijkstra's algorithm: Difference between revisions
Content added Content deleted
Line 755: | Line 755: | ||
This implementation is based on suggestions from |
This implementation is based on suggestions from |
||
[https://en.wikipedia.org/w/index.php?title=Dijkstra%27s_algorithm&oldid=1117533081#Using_a_priority_queue a Wikipedia article about Dijkstra's algorithm]. |
[https://en.wikipedia.org/w/index.php?title=Dijkstra%27s_algorithm&oldid=1117533081#Using_a_priority_queue a Wikipedia article about Dijkstra's algorithm], although I use a different method to determine whether a queue entry is obsolete. |
||
I prove that the algorithm terminates. |
|||
<syntaxhighlight lang="ats"> |
<syntaxhighlight lang="ats"> |
||
Line 1,006: | Line 1,006: | ||
fn |
fn |
||
fprint_vertex_path |
fprint_vertex_path |
||
{n : int} |
{n : int} |
||
(outf : FILEref, |
(outf : FILEref, |
||
vertex_arr : arrayref (string, n), |
vertex_arr : arrayref (string, n), |
||
path : List ([i : nat | i < n] size_t i) |
path : List ([i : nat | i < n] size_t i), |
||
cost_opt : Option flt, |
|||
cost_column_no : size_t) |
|||
: void = |
: void = |
||
let |
let |
||
Line 1,015: | Line 1,017: | ||
loop {m : nat} |
loop {m : nat} |
||
.<m>. |
.<m>. |
||
(path : list ([i : nat | i < n] size_t i, m) |
(path : list ([i : nat | i < n] size_t i, m), |
||
column_no : size_t) |
|||
: size_t = |
|||
case+ path of |
case+ path of |
||
| NIL => |
| NIL => column_no |
||
| i :: NIL => |
| i :: NIL => |
||
begin |
|||
fprint! (outf, vertex_arr[i]); |
|||
column_no + strlen vertex_arr[i] |
|||
end |
|||
| i :: tail => |
| i :: tail => |
||
begin |
begin |
||
fprint! (outf, vertex_arr[i], " -> "); |
fprint! (outf, vertex_arr[i], " -> "); |
||
loop tail |
loop (tail, column_no + strlen vertex_arr[i] + i2sz 4) |
||
end |
end |
||
prval () = lemma_list_param path |
prval () = lemma_list_param path |
||
val column_no = loop (path, i2sz 1) |
|||
in |
in |
||
case+ cost_opt of |
|||
| None () => () |
|||
| Some cost => |
|||
let |
|||
var i : size_t = column_no |
|||
in |
|||
while (i < cost_column_no) |
|||
begin |
|||
fprint! (outf, " "); |
|||
i := succ i |
|||
end; |
|||
fprint! (outf, "(cost = ", cost, ")") |
|||
end |
|||
end |
end |
||
Line 1,042: | Line 1,062: | ||
source : size_t source) |
source : size_t source) |
||
(* Returns total-costs and previous-hops arrays. *) |
(* Returns total-costs and previous-hops arrays. *) |
||
:<!refwrt |
:<!refwrt> @(arrayref (flt, n), |
||
arrayref ([i : nat | i <= n] size_t i, n)) = |
|||
let |
let |
||
prval () = lemma_matrixref_param adj_matrix |
prval () = lemma_matrixref_param adj_matrix |
||
Line 1,062: | Line 1,082: | ||
val active = arrayref_make_elt<bool> (n, true) |
val active = arrayref_make_elt<bool> (n, true) |
||
var num_active : [i : nat | i <= n] size_t i = n |
|||
fun |
fun |
||
Line 1,076: | Line 1,097: | ||
fun |
fun |
||
extract_min |
|||
main_loop (pq : &pqueue_t >> _) |
|||
{m0 : nat} |
|||
:<!refwrt,!ntm> void = |
|||
.<m0>. |
|||
(pq : &pqueue_t m0 >> pqueue_t m1) |
|||
:<!refwrt> #[m1 : nat | m1 <= m0] |
|||
Option @(flt, defined_index_t) = |
|||
if pqueue_is_empty pq then |
|||
None () |
|||
else |
|||
let |
let |
||
val @(priority, |
val @(priority, vertex) = |
||
pqueue_extract_min<flt><defined_index_t> pq |
pqueue_extract_min<flt><defined_index_t> pq |
||
in |
in |
||
if ~active[vertex] then |
|||
(* Ignore any queue entries that might raise the cost. *) |
|||
extract_min pq |
|||
main_loop pq |
|||
else |
else |
||
Some @(priority, vertex) |
|||
end |
|||
val () = active[u] := false |
|||
fun |
|||
main_loop {num_active0 : nat | num_active0 <= n} |
|||
loop_over_neighbors |
|||
.<num_active0>. |
|||
(pq : &pqueue_t >> pqueue_t, |
|||
num_active : &size_t num_active0 >> size_t num_active1) |
|||
:<!refwrt> #[num_active1 : nat | num_active1 <= num_active0] |
|||
v : size_t v) |
|||
void = |
|||
if num_active <> i2sz 0 then |
|||
case+ extract_min pq of |
|||
| None () => |
|||
(* This branch should never be reached, but the |
|||
corresponding sanity check is done further below. Thus |
|||
the proof of termination of this loop does not depend on |
|||
an exception. *) |
|||
val alternative = cost[u] + adj_matrix[u, n, v] |
|||
() |
|||
| Some @(priority, u) => |
|||
if alternative < cost[v] then |
|||
let |
|||
val () = active[u] := false |
|||
and () = num_active := pred num_active |
|||
fun |
|||
(* Rather than lower the priority of v, this |
|||
loop_over_neighbors |
|||
implementation inserts a new entry for v |
|||
{v : nat | v <= n} |
|||
.<n - v>. |
|||
(pq : &pqueue_t >> pqueue_t, |
|||
v : size_t v) |
|||
:<!refwrt> void = |
|||
if v = n then |
|||
() |
|||
else if ~active[v] then |
|||
loop_over_neighbors (pq, |
loop_over_neighbors (pq, succ v) |
||
else |
|||
let |
|||
val alternative = cost[u] + adj_matrix[u, n, v] |
|||
end |
|||
in |
|||
if alternative < cost[v] then |
|||
begin |
|||
cost[v] := alternative; |
|||
prev[v] := u; |
|||
(* Rather than lower the priority of v, this |
|||
implementation inserts a new entry for v and |
|||
ignores obsolete queue entries. Queue entries |
|||
are obsolete if the vertex's entry in the |
|||
"active" array is false. *) |
|||
pqueue_insert<flt><defined_index_t> |
|||
(pq, alternative, v) |
|||
end; |
|||
loop_over_neighbors (pq, succ v) |
|||
end |
|||
in |
|||
loop_over_neighbors (pq, i2sz 0); |
|||
main_loop {num_active0 - 1} (pq, num_active) |
|||
end |
|||
val () = fill_pq (pq, i2sz 0) |
val () = fill_pq (pq, i2sz 0) |
||
val () = main_loop pq |
val () = main_loop {n} (pq, num_active) |
||
(* Sanity check. *) |
|||
val- true = iseqz num_active |
|||
in |
in |
||
@(cost, prev) |
@(cost, prev) |
||
Line 1,199: | Line 1,249: | ||
val- Some path_a_to_e = least_cost_path {n} (a, prev, n, e) |
val- Some path_a_to_e = least_cost_path {n} (a, prev, n, e) |
||
val- Some path_a_to_f = least_cost_path {n} (a, prev, n, f) |
val- Some path_a_to_f = least_cost_path {n} (a, prev, n, f) |
||
var u : [i : nat | i <= n] size_t i |
|||
val cost_column_no = i2sz 20 |
|||
in |
in |
||
println! ("The requested paths:"); |
|||
fprint_vertex_path (stdout_ref, vertex_arr, path_a_to_e); |
|||
fprint_vertex_path (stdout_ref, vertex_arr, path_a_to_e, |
|||
println! (" (cost = ", cost[e], ")"); |
|||
Some cost[e], cost_column_no); |
|||
fprint_vertex_path (stdout_ref, vertex_arr, path_a_to_f); |
|||
println! ( |
println! (); |
||
fprint_vertex_path (stdout_ref, vertex_arr, path_a_to_f, |
|||
Some cost[f], cost_column_no); |
|||
println! (); |
|||
println! (); |
|||
println! ("All paths (in no particular order):"); |
|||
for (u := i2sz 0; u <> n; u := succ u) |
|||
case+ least_cost_path {n} (a, prev, n, u) of |
|||
| None () => |
|||
println! ("There is no path from ", vertex_arr[a], " to ", |
|||
vertex_arr[u], ".") |
|||
| Some path => |
|||
begin |
|||
fprint_vertex_path (stdout_ref, vertex_arr, path, |
|||
Some cost[u], cost_column_no); |
|||
println! () |
|||
end |
|||
end |
end |
||
Line 1,211: | Line 1,280: | ||
{{out}} |
{{out}} |
||
<pre>$ patscc -DATS_MEMALLOC_GCBDW dijkstra-algorithm.dats -lgc && ./a.out |
<pre>$ patscc -DATS_MEMALLOC_GCBDW dijkstra-algorithm.dats -lgc && ./a.out |
||
The requested paths: |
|||
a -> c -> d -> e (cost = 26.000000) |
|||
a -> c -> |
a -> c -> d -> e (cost = 26.000000) |
||
a -> c -> f (cost = 11.000000) |
|||
All paths (in no particular order): |
|||
a -> c -> d -> e (cost = 26.000000) |
|||
a -> c -> d (cost = 20.000000) |
|||
a -> c -> f (cost = 11.000000) |
|||
a -> c (cost = 9.000000) |
|||
a -> b (cost = 7.000000) |
|||
a (cost = 0.000000) |
|||
</pre> |
|||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |