Podczas kursu matematyki obliczeniowej zetknęliśmy się z zadaniem: mając dany zestaw punktów na płaszczyźnie:
![]() |
znaleźć prostą taką, że
![]() |
Było to po prostu inne sformułowanie liniowego zadania najmniejszych kwadratów na współczynniki . Zwróćmy jednak uwagę, że takie sformułowanie zadania nierówno traktuje zmienne
oraz
.
W wielu zastosowaniach, na przykład: w analizie obrazów, w energetyce, czy w komunikacji, mamy do czynienia z zadaniem minimalizacji średniej odległości prostej od zestawu punktów na płaszczyźnie. Rzeczywiście, jest to matematyczne sformułowanie praktycznego zadania w rodzaju: Jak poprowadzić (prostą) autostradę tak, by dojazd do niej z pobliskich miejscowości był możliwie szybki? Albo: Jak, na podstawie nieco rozmytego konturu budynku na zdjęciu, zdefiniować jego brzeg?
Przez analogię do zadania najmniejszych kwadratów określimy zadanie geometrycznego dopasowania prostej do punktów, polegające na wskazaniu takiej prostej , dla której
![]() |
przy czym odległość będziemy mierzyć w normie euklidesowej.
Powyższe zadanie jest najprostszą wersją całej klasy zadań geometrycznego dopasowania, w których krzywą określonego typu chcemy dopasować do zadanego zestawu punktów
tak, by
![]() |
W przypadku budynków na przykład, szukalibyśmy prostokąta spełniającego powyższy warunek (por. uwagę na końcu wykładu). W niniejszym rozdziale zajmiemy się na początek, w oparciu o [6], geometrycznym dopasowaniem prostej, a w dalszej części wykładu, korzystając z materiałów zawartych w [1] — dopasowaniem okręgu do zadanych punktów.
Liczba punktów pomiarowych w praktyce nie jest bardzo duża, dlatego będziemy mieli do czynienia z zadaniami niezbyt intensywnymi obliczeniowo: głównym problemem będzie tutaj właściwe sformułowanie zadania w taki sposób, by można było je skutecznie zaatakować dostępnymi metodami.
Nietrudno zauważyć, że reprezentowanie prostej w postaci
nie jest zbyt zręczne, bo na przykład czasem najlepsze dopasowanie może zachodzić dla prostej
, której nie jesteśmy w stanie tak przedstawić. Dlatego korzystniej będzie rozpatrzyć ogólne równanie prostej
postaci
![]() |
oczywiście uzupełnione jakimś warunkiem normującym (współczynniki są dane z dokładnością do mnożnika). Tutaj najrozsądniej będzie przyjąć, że
: wektor
jest wektorem normalnym do prostej
.
Wtedy odległość punktu
od
jest dana równością
![]() |
zatem mamy znaleźć takie, że
oraz wyrażenie
![]() |
przyjmuje najmniejszą wartość.
Ponieważ na parametr nie ma ograniczeń, możemy łatwo go wyeliminować z rozważań przyrównując do zera pochodną
![]() |
gdzie i analogicznie
.
W ten sposób zadanie zredukowało się do znalezienia wektora takiego, że
![]() |
gdzie
![]() |
Jest to więc nic innego, jak zadanie wyznaczenia wektora szczególnego15Więcej na temat wektorów i wartości szczególnych można dowiedzieć się np. w wykładzie z Matematyki Obliczeniowej II, w rozdziale dotyczącym rozkładu SVD macierzy. macierzy , odpowiadającego najmniejszej wartości szczególnej. Rzeczywiście, jeśli
i macierze
są ortogonalne, a
![]() |
przy czym dla ustalenia uwagi , to
![]() |
a równość zachodzi gdy , czyli dla
.
Ponieważ w Octave znajduje się gotowa funkcja wyznaczająca pełny rozkład SVD zadanej macierzy, możemy pokusić się o implementację funkcji wyznaczenia na podstawie punktów, których współrzędne na osiach
i
podane są, odpowiednio, w talicach
i
:
function ABC = linefit(x,y) x = x(:); y = y(:); ABC = NaN(3,1); xm = mean(x); ym = mean(y); [U, S, V] = svd([x-xm, y-ym], 0); % ekonomiczny rozkład SVD ABC(1:2) = V(:,end); % [A, B] ABC(3) = - ABC(1)*xm - ABC(2)*ym; end
Aby przetestować działanie naszego kodu, zaburzymy losowo punkty prostej i sprawdzimy, co wyjdzie. Porównamy także nasz (geometryczny) fit z dopasowaniem na podstawie standardowej metody najmniejszych kwadratów.
W celu wygodnego rysowania prostej zadanej w postaci uwikłanej, naprędce opracujemy funkcję lineplot
, której listing zamieszczamy poniżej.
function lineplot(ABC, X, Y, color) if nargin < 4 color = ''; end xmin = X(1); xmax = X(2); ymin = Y(1); ymax = Y(2); A = ABC(1); B = ABC(2); C = ABC(3); if abs(A/B) <= 1 plot(X, (-C-A*X)/B, ['-',color]); else plot((-C-B*Y)/A, Y, ['-',color]); end end
Teraz możemy rozpocząć testy, korzystając na przykład z kodu poniższej postaci:
a = 5; b = 6; x = linspace(1,3,300); y = a*x + b; x = x+0.6*(2*rand(size(x))-1); y = y+0.6*(2*rand(size(y))-1); % fit geometryczny ABC = linefit(x,y); % fit LZNK ba = [ones(size(x)); x]'\y'; plot(x,y,'or'); hold on; lineplot(ABC,[0,4],[min(y)-1,max(y)+1],'r'); lineplot([ba(2),-1,ba(1)],[0,4],[min(y)-1,max(y)+1],'k'); lineplot([a,-1,b],[0,4],[min(y)-1,max(y)+1],'g'); hold off; legend('zaburzone dane','fit geometryczny','fit LZNK','oryginalna prosta'); grid on;
Analogicznie, można rozważyć zadanie geometrycznego dopasowania okręgu (lub, w większej ogólności, elipsy) do danych punktów na płaszczyźnie. Zastosowania mogą dotyczyć [1] medycyny (zwłaszcza — okulistyki), kontroli jakości wykonania obrotowych części mechanicznych, wyznaczania torów cząstek elementarnych, robotyki, a nawet — archeologii (wyznaczenie średnicy stadionu na podstawie zachowanych fragmentów). W wymienionych przypadkach zazwyczaj mamy do czynienia z zestawem punktów znajdujących się na łuku stanowiącym jedynie niewielki fragment okręgu. Z powodu zaburzeń danych, spowodowanych na przykład błędami pomiaru, zadane punkty nie układają się idealnie na poszukiwanej krzywej.
Jeśli okrąg reprezentować przez środek
oraz promień
:
![]() |
to odległość zadanego punktu
od
jest równa, jak łatwo sprawdzić,
![]() |
W takim razie, musimy zminimalizować wartość funkcjonału
![]() |
Ponieważ zadanie minimalizacji tym razem dotyczy bardziej skomplikowanego funkcjonału nieliniowego, użyjemy standardowej procedury optymalizacji nieliniowej w Octave, sqp
.
function d = residgeom(X,x,y) % a = X(1); b = X(2); r = X(3); d = sumsq( sqrt((x-X(1)).^2 + (y-X(2)).^2) - X(3)); end function [S,dist,info] = fitcircle1(S0,x,y) % S = (a,b,r) [S,dist,info] = sqp(S0,@(X)residgeom(X,x,y)); end
Funkcja residgeom
wyznacza wartość funkcjonału , przy czym przekazujemy jej wszystkie parametry funkcjonału w pierwszym argumencie, w postaci wektora
X
. Musimy tak zrobić, bo sqp
spodziewa się funkcji jednego argumentu — taką tworzymy w postaci funkcji anonimowej
@(X)residgeom(X,x,y)przekazywanej do
sqp
. Ponieważ w zadaniu nieliniowym wymagany jest sensowny punkt startowy fitcircle1
. Zadaniem użytkownika jest właściwy dobór Jakkolwiek dokonany przez nas wybór parametrów zadania: , jest niewątpliwie kuszący, to jednak, gdy punkty
są prawie współliniowe, wyznaczany promień
może być bardzo duży w porównaniu do współrzędnych środka
. Ponadto małe zaburzenia położenia punktów
mogą prowadzić do dużych skoków wielkości
(a także — położenia środka okręgu), dlatego warto zadanie sformułować inaczej.
Okrąg możemy opisać za pomocą standardowego równania krzywej drugiego stopnia,
![]() |
Ponieważ wynika stąd, że
![]() |
Jak podaje [1], Pratt zasugerował warunek normalizacyjny
![]() |
Ponadto wówczas odległość punktu
od okręgu jest równa
![]() |
zatem musimy minimalizować funkcjonał
![]() |
z ograniczeniem
![]() |
To zadanie po raz kolejny zrealizujemy za pomocą funkcji sqp
:
function d = residgeom2(X,x,y) A = X(1); B = X(2); C = X(3); D = X(4); P = A*(x.^2 +y.^2) + B*x + C*y + D; d = sumsq(2*(P./(1+sqrt(1+4*A*P)))); end function d = constr(X) d = X(2)^2 + X(3)^2 - 4*X(1)*X(4) - 1; end function [S,dist,info] = fitcircle2(S0,x,y) % S = (A,B,C,D) c = zeros(4,1); c(1) = 1/(2*S0(3)); c(2:3) = -2*c(1)*S0(1:2); c(4) = (c(2)^2 + c(3)^2 - 1)/(4*c(1)); [c,dist,info] = sqp(c, @(X)residgeom2(X,x(:),y(:)), @constr); S = [-c(2:3)/(2*c(1)); sqrt(c(2)^2 + c(3)^2 - 4*c(1)*c(4))/(2*abs(c(1)))]; end
Jakkolwiek ostatnią linijkę kodu moglibyśmy teoretycznie zastąpić tańszym
S = [-c(2:3); 1]/(2*c(1))
, to jednak bezpieczniej będzie zrobić tak, jak powyżej — bo warunek może nie być idealnie spełniony.
Pozostaje jeszcze pytanie, w jaki sposób będziemy mogli sensownie wyznaczyć początkowe przybliżenie . Nadspodziewanie dobrym kandydatem na
jest (patrz [1]) okrąg spełniający tzw. algebraiczny warunek dopasowania, tzn. minimalizujący, przy ograniczeniu
, sumę kwadratów residuum równania okręgu:
![]() |
gdzie jest zadane przez
.
Jest to więc — bardzo podobnie jak widzieliśmy to w przypadku fitowania prostej — zadanie najmniejszych kwadratów dla
![]() |
gdzie
![]() |
Niestety, macierz nie jest dodatnio określona, co komplikuje algorytm rozwiązywania tego zadania.
Funkcja Lagrange'a jest postaci
![]() |
zatem koniecznym warunkiem dla ekstremum jest
![]() |
Ponieważ , to pierwszy warunek oznacza po prostu, że para
jest parą własną powyższego uogólnionego zadania własnego,
![]() |
(7.1) |
Wprowadzając (ekonomiczny) rozkład QR macierzy ,
![]() |
mamy równoważnie i stąd
musi być parą własną macierzy
.
Ponieważ macierz
ma wartości własne
, to macierz
(na mocy tw. Sylvestera) także ma dokładnie jedną wartość własną ujemną, a pozostałe trzy — dodatnie.
Mnożąc stronami (7.1) przez dostajemy
![]() |
na mocy warunku . Ponieważ
, interesująca nas
nie może być ujemna. Ze względu na to, że poszukiwane przez nas
musi minimalizować
, znaczy to, że minimum zostanie przyjęte równe najmniejszej dodatniej wartości własnej
. W takim razie szukana przez nas para własna to para odpowiadająca najmniejszej dodatniej wartości własnej macierzy
.
Stąd jednym ze sposobów implementacji powyższego algorytmu wyznaczenia — a tym samym okręgu spełniającego wymieniony na początku tej sekcji algebraiczny warunek dopasowania — może być funkcja:
function [S,dist,info] = fitcircle3(x,y) x = x(:); y = y(:); invB = [0 0 0 -0.5; 0 1 0 0; 0 0 1 0; -0.5 0 0 0]; % $B^{-1}$ explicite X = [x.^2+y.^2, x, y, ones(size(x))]; [Q R] = qr(X,0); [V L] = eig(R*invB*R'); [L i] = sort(diag(L)); c = R \ V(:,i(2)); % interesuje nas druga najmniejsza w.wl, a raczej: wektor S = [-c(2:3)/(2*c(1)); sqrt(c(2)^2 + c(3)^2 - 4*c(1)*c(4))/(2*abs(c(1)))]; dist = residgeom(S,x,y); info = 1; end
Przeredaguj funkcje fitcircle1
i fitcircle2
tak, by jeśli użytkownik nie poda przybliżenia startowego, zostało wybrane przybliżenie wyznaczanie funkcją fitcircle3
.
Musimy zmienić kolejność argumentów tak, by przybliżenie początkowe było ostatnim. Wtedy początek funkcji, np. fitcircle1
, należałoby zapisać np. w takiej formie:
function [S,dist,info] = fitcircle1(x,y,S0) if nargin < 3 S0 = fitcircle3(x,y); end % ... tu dalsze instrukcje ... end
Poniżej przytaczamy przykładowy skrypt dopasowujący okrąg do zadanych punktów trzema opisanymi wcześniej metodami.
t = linspace(0,0.2*pi,100)'; X = 3*sin(t)+0.2*rand(size(t)); Y = 3.35*cos(t)+0.1*rand(size(t)); XY = [X,Y]; e = fitcircle3(X,Y); c = fitcircle1(e,X,Y); d = fitcircle2(e,X,Y); [e c d] t = linspace(0,2*pi,300); plot(X,Y,'ro'); axis('square'); hold on; plot(c(1) + c(3)*sin(t),c(2)+c(3)*cos(t),'g-'); plot(d(1) + d(3)*sin(t),d(2)+d(3)*cos(t),'r-'); plot(e(1) + e(3)*sin(t),e(2)+e(3)*cos(t),'k-'); plot(X,Y,'ro'); hold off; legend('dane', 'fc1', 'fc2', 'fc3'); [residgeom(c,X,Y), residgeom(d,X,Y), residgeom(e,X,Y)]
Treść automatycznie generowana z plików źródłowych LaTeXa za pomocą oprogramowania wykorzystującego LaTeXML.
strona główna | webmaster | o portalu | pomoc
© Wydział Matematyki, Informatyki i
Mechaniki UW, 2009-2010. Niniejsze materiały są udostępnione bezpłatnie na licencji Creative Commons Uznanie autorstwa-Użycie niekomercyjne-Bez utworów zależnych 3.0 Polska.
Projekt współfinansowany przez Unię Europejską w ramach
Europejskiego Funduszu Społecznego.
Projekt współfinansowany przez Ministerstwo Nauki i Szkolnictwa Wyższego i przez Uniwersytet Warszawski.