Марш Джарвиса
В вычислительной геометрии марш Джарвиса или алгоритм подарочной упаковки используется для вычисления выпуклой оболочки заданного набора точек. Алгоритм имеет широкий спектр приложений в математике и информатике, практически в распознавании образов, обработке изображений, статистике, географических информационных системах (ГИС) и теории игр.
Выпуклая оболочка или выпуклая оболочка набора точек P на евклидовой плоскости — это наименьшее выпуклое множество, содержащее P. Это похоже на обертывание множества точек P внутри границы с использованием самого внешнего меньшего набора точек P, как показано на иллюстрации. ниже.
Jarvis (S) with pre condition of at least 3 elements in S Left ← find the leftmost point in S P ← Left
Initialize the array of Result to -1
repeat for I from S'First to S'Last loop p ← S(P) q ← S(I) r ← S(Q) if orientation of triplet (p, q, r) = counter clockwise Q ← I end loop
Result(P) ← Q P ← Q until P = Left
Алгоритм марша Джарвиса реализован в Ada 2012 для демонстрации контрактного программирования. В процедуре Jarvis, если предварительное условие не менее 3 элементов в массиве не выполняется, будет возбуждено исключение. Демонстрационная программа перехватывает исключение, выводит ошибку исключения с сообщением «В массиве должно быть не менее 3 точек». а затем выйти изящно.
Спецификация пакета jarvis_march.ads:
pragma Assertion_Policy(Check);
package Jarvis_March is
type Orientation is ( Colinear, Clockwise, Counter_Clockwise );
type Point is record X : Integer; Y : Integer; end record;
type Points is array ( Integer range <> ) of Point; type Result is array ( Integer range <> ) of Integer;
procedure Jarvis ( Ps : in Points; R : out Result ) with Pre => ( Ps'Length > 2 );
private
function Find_Orientation ( P, Q, R : Point ) return Orientation; function Find_Leftmost ( Ps : Points ) return Integer;
end Jarvis_March;
Тело пакета jarvis_march.adb:
package body Jarvis_March is
procedure Jarvis ( Ps : in Points; R : out Result ) is
Size : Natural := Ps'Length;
begin declare Left, P, Q : Integer; begin R := ( others => -1 );
Left := Find_Leftmost ( Ps ); P := Left;
Search_Loop: loop Q := ( ( P + 1 ) mod Size ) + 1;
for I in Ps'First .. Ps'Last loop if ( Find_Orientation ( Ps ( P ), Ps ( I ), Ps ( Q ) ) = Counter_Clockwise ) then Q := I; end if; end loop;
R ( P ) := Q; P := Q;
exit Search_Loop when P = Left;
end loop Search_Loop; end; end Jarvis;
function Find_Leftmost ( Ps : Points ) return Integer is
L : Integer := Ps'First; P, Q : Point;
begin for I in 1 .. Ps'Length loop P := Ps ( I ); Q := Ps ( L );
if ( P.X < Q.X ) then L := I; end if;
end loop;
return L;
end Find_leftmost;
function Find_Orientation ( P, Q, R : Point ) return Orientation is
Direction : Integer;
begin Direction := ( Q.Y - P.Y ) * ( R.X - Q.X ) - ( Q.X - P.X ) * ( R.Y - Q.Y );
if Direction = 0 then return Colinear; end if;
if Direction > 0 then return Clockwise; else return Counter_Clockwise; end if; end Find_Orientation;
end Jarvis_March;
Основная демонстрационная программа jarvistest.adb:
with Ada.Exceptions; use Ada.Exceptions; with Ada.Text_IO; use Ada.Text_IO;
with Jarvis_March; use Jarvis_March;
procedure jarvistest is
-- Comment/uncomment the two lines below to test the pre condition of the -- contract assertion in procedure Jarvis Ps : Points ( 1 .. 7 ) := ( ( 0, 6 ), ( 1, 2 ), ( 1, 1 ), ( 2, 1 ), ( 3, 1 ), ( 0, 0 ), ( 4, 3) ); -- Ps : Points ( 1 .. 2 ) := ( ( 0, 6 ), ( 1, 2 ) ); R : Result ( Ps'Range ); P : Point;
begin
Jarvis ( Ps, R );
for I in R'Range loop
if ( R ( I ) /= -1 ) then
P := Ps ( I ); Put_Line ( "(" & Integer'Image ( P.X ) & ", " & Integer'Image ( P.Y ) & ")" );
end if; end loop;
exception
when Error : others => Put_Line (Ada.Exceptions.Exception_Name ( Error ) & ": Must have at least 3 points in the array.");
end jarvistest;
Полный исходный код доступен на моем GitHub. Если вы найдете это полезным, пожалуйста, напишите мне сообщение.
Первоначально опубликовано на сайте adrianhoe.com 31 декабря 2016 г.