SpecVer/Winograd: Różnice pomiędzy wersjami

Z Lem
Skocz do: nawigacji, wyszukiwania
(Dowód poprawności)
(Uwagi końcowe)
 
(Nie pokazano 28 pośrednich wersji utworzonych przez tego samego użytkownika)
Linia 18: Linia 18:
 
W literaturze proponowane są dwa podejścia:
 
W literaturze proponowane są dwa podejścia:
  
 
 
* udowodnij częściową porawność sprawdzając pewne niezmienniki -- jest to tzw. metoda Floyda-Hoare'a,
 
* udowodnij częściową porawność sprawdzając pewne niezmienniki -- jest to tzw. metoda Floyda-Hoare'a,
 
* wykonaj pewną liczbę obliczeń testowych i zaufaj, że w trakcie eksploatacji algorytmu nie okaże się, że jest on niepoprawny.
 
* wykonaj pewną liczbę obliczeń testowych i zaufaj, że w trakcie eksploatacji algorytmu nie okaże się, że jest on niepoprawny.
 
  
 
W obliczeniach testowych można w trakcie obliczeń sprawdzać wcześniej wstawione warunki -- asercje. Czy rzeczywiście są one pomocne?
 
W obliczeniach testowych można w trakcie obliczeń sprawdzać wcześniej wstawione warunki -- asercje. Czy rzeczywiście są one pomocne?
  
 
W następnym rozdziale pokażemy inną drogę - drogę dowodzenia lematów i w końcu twierdzenia o poprawności (całkowitej) algorytmu Winograda względem warunku początkowego '''Precondition''' i warunku końcowego '''Postcondition'''.
 
W następnym rozdziale pokażemy inną drogę - drogę dowodzenia lematów i w końcu twierdzenia o poprawności (całkowitej) algorytmu Winograda względem warunku początkowego '''Precondition''' i warunku końcowego '''Postcondition'''.
<math>
+
----------------------------------------------------------------------------------
\begin{algorithmic}[1]
+
\STATE $\mathbf{signal}\  Niezgoda;$
+
  
\STATE $\mathbf{unit}\ Winograd:\ \mathbf{procedure}(A,B:   \mathbf{array\_of\ array\_of}\ real;\
+
'''signal''' Niezgoda;
\ \mathbf{output}\ C: \mathbf{array\_of\ array\_of}\ real);
+
 
\REQUIRE  wymagaj by A i B były macierzami kwadratowymi rozmiaru n x n    
+
'''unit''' Winograd: procedure(A,B: arrayof arrayof real;
\ENSURE zapewnij,  że obliczona  macierz C jest produktem macierzy A i B, C = A * B
+
output C: arrayof arrayof real);
\STATE  $\mathbf{var}\ i,j,k,n,m: \mathbf{integer},\
+
::::<span style="color: blue">'''Precondition''': A i B macierzami dwuwymiarowymi o tym samym rozmiarze ''n x n''
      W,V: \mathbf{array\_of\ real},  
+
</span>
      p: \mathbf{boolean},  
+
::::<span style="color: blue">'''Postcondition''': macierz C jest produktem macierzy A i B, C=A x B
      s: \mathbf{real}; $
+
</span>
 +
:: '''var''' i,j,k,n,m: integer, W,V: '''arrayof''' real,
 +
::      p: boolean,
 +
::      s: real;
 
              
 
              
\STATE $\mathbf{begin}$ \\
+
'''begin'''
\COMMENT{ \textit{ustalić czy macierze mogą być mnożone tzn.  
+
:(* ustalic czy macierze moga byc mnozone tzn.  
  czy ilość wierszy w A = ilosc kolumn w B?} } \\
+
czy ilosc wierszy w A = ilosc kolumn w B? *)
\COMMENT{ \textit{ustalić czy n jest parzyste?} } \\
+
\COMMENT{\textit{ obliczyc preprocessing} } \\
+
\hrulefill
+
  
\COMMENT{ dynamiczne sprawdzanie precondition  }
+
:(* ustalic czy n jest parzyste? *)  
\IF {lower(A) $\neq$ lower(B)\ \OR\ lower(A)$\neq$ 1\ \OR\ upper(A)$\neq$ upper(B)\ }  \STATE $raise\ Niezgoda$ \FI;
+
\STATE i := upper(A);
+
\STATE j :=  lower(A);
+
\STATE n := i-j+1;
+
\FOR {$l : = j$ \TO $i$ }
+
      \IF {lower(A(l)) $\neq$ lower(B(l))\ \OR\ lower(A(l))$\neq$ 1\ \OR\ upper(A(l))$\neq$ upper(B(l) \OR upper(A(l)$\neq$upper(A)}
+
      \STATE raise Niezgoda \FI;
+
\OD;
+
\ASSERTION sprawdzono: macierze są kwadratowe, rozmiaru n x n \\
+
\hrulefill
+
  
 
+
:(* obliczyc "preprocessing" *)
\COMMENT { można mnożyć } \\
+
  
%\STATE $n := k+1;$
+
:i := upper(A);
\STATE $p := (n\ mod\ 2) = 0;$ % (* p = n jest parzyste?  *)
+
:j := lower(A);
\STATE $m := n\ div\ 2;$
+
:k := i-j;
\STATE $\mathbf{array}\ W\ \mathbf{dim} (1:n);$
+
:i:= upper(B);
\STATE $\mathbf{array}\ V\ \mathbf{dim} (1:n);$
+
:j:= lower(B);
\STATE $ \mathbf{array} \ C\ \mathbf{dim} (1:n);$
+
:if i-j <> k then raise Niezgoda fi;
\FOR { i := 1 to n}
+
 
%do  
+
:(* mozna mnozyc *)
\STATE $ \mathbf{array}\ C(i)\ \mathbf{dim} (1:n)$
+
 
\OD; \\
+
:n := k+1;
\hrulefill \\
+
:p := (n mod 2) = 0;
%\begin{verbatim}
+
:m := n div 2;
\COMMENT { obliczanie "preprocessingu" }
+
:array W dim (1:n);
\FOR { j:= 1 to n}
+
:array V dim (1:n);
%do  
+
:array C dim (1:n);
\STATE s:=0;  
+
:for i := 1 to n
  \FOR {i := 1 to m }
+
:do
% do  
+
:: array C(i) dim (1:n)
\STATE  $  s := A[j,2*i-1] * A[j,2*i] +s;$
+
:od;
\OD; %od;  
+
:(* obliczanie "preprocessingu" *)
\STATE W[j] := s;  
+
:for j:= 1 to n
\OD; %od;  
+
:do
+
:: s:=0;
<math>\fbox{ \textbf{Assertion 1:} \
+
::  for i := 1 to m  
Dla każdego $j, 1\leq j \leq n,\qquad W_j=\sum\limits_{i=1}^{n \div 2} A_{j,2i-1}*A_{j,2i}$ }
+
:: do
 +
:::  s := A(j,2*i-1) * A(j,2*i) +s;
 +
:: od;
 +
:: W(j) := s;
 +
:od;
 +
 
 +
:::::  <span style="color: blue">'''Assertion 1''': Dla każdego </span><math>\color{blue} j, 1\leq j \leq n,\qquad W_j=\sum\limits_{i=1}^{n \div 2} A_{j,2i-1}*A_{j,2i}
 
</math>
 
</math>
\FOR {j:= 1 to n }
 
%do
 
\STATE  s:=0;
 
  \FOR {i := 1 to m }
 
  %do
 
\STATE  { s := B[2*i-1,j] * B[2*i,j] +s; }
 
  \OD; %od;
 
\STATE V[j] := s;
 
\OD;
 
 
\fbox {\textbf{Assertion 2:} \ \
 
Dla każdego $j, 1 \leq j \leq n,\qquad V_j=\sum\limits_{i=1}^{n \div 2} B_{2i-1,j}*B_{2*i,j}$ }
 
\bigskip \\
 
\hrulefill
 
 
\COMMENT {obliczanie iloczynu macierzy }
 
\FOR {$i := 1$ to $n$}    %\COMMENT { dla każdego  1$\leq$ i $\leq$ n  }
 
  
%do
 
  \FOR {$j := 1$ to $n$}    %{* dla każdego  $1\leq j \leq n$  }
 
  %do
 
\STATE    s:= 0;
 
    \FOR {$k:= 1$ to $m$}
 
  % do
 
  \STATE    s:= (A[i,2*k-1]+B[2*k,j]) * (B[2*k-1,j]+A[i,2*k]) +s;
 
    \OD; \\
 
\fbox{\textbf{Assertion 3:} \
 
$\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}$, \qquad
 
$s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k}) $ }   
 
 
 
  \STATE $ C[i,j] :=s-W[i]-V[j];$
 
  
\fbox{\textbf{Assertion 4:}\  $\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}$ \qquad
+
:for j:= 1 to n
  $C_{i,j} = \sum\limits_{k=1}^{2(n\div2)} (A_{i,k} *B_{k,j} ) $}
+
:do
   
+
:: s:=0;
    \IF {\NOT p}   
+
::  for i := 1 to m
 +
::  do
 +
:::    s := B(2*i-1,j) * B(2*i,j) +s;
 +
:: od;
 +
:  V(j) := s;
 +
:od;
  
    %then
+
:::::<span style="color: blue">'''Assertion 2:''' Dla każdego </span><math>\color{blue} j, 1 \leq j \leq n,\qquad V_j=\sum\limits_{i=1}^{n \div 2} B_{2i-1,j}*B_{2*i,j}</math>
    \STATE  $C[i,j] := C[i,j] + A[i,n]*B[n,j];$
+
\COMMENT {  poprawiamy - gdy n jest nieparzyste  }
+
    \FI; \\
+
  
 +
:(* obliczanie iloczynu macierzy *)
 +
:for i := 1 to n
 +
:do
 +
::  for j := 1 to n
 +
::  do
 +
:::    s:= 0;
 +
:::    for k:= 1 to m
 +
:::    do
 +
::::      s:= (A(i,2*k-1)+B(2*k,j)) * (B(2*k-1,j)+A(i,2*k)) +s;
 +
:::    od;
 +
 +
:::::<span style="color: blue">'''Assertion 3'''  </span><math>\color{blue} \forall_{1\leq i \leq n},\forall_{1\leq j \leq n}, \qquad
 +
s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k}) </math>
 +
 +
:::    C(i,j) :=s-W(i)-V(j);
 +
 +
:::::<span style="color: blue">'''Assertion 4:''' </span><math>\color{blue} \forall_{1\leq i \leq n},\forall_{1\leq j \leq n} \qquad
 +
C_{i,j} = \sum\limits_{k=1}^{2(n\div2)} (A_{i,k} *B_{k,j} ) </math>
 +
 +
:::    '''if''' not p
 +
:::    '''then'''
 +
::::      C(i,j) := C(i,j) + A(i,n)*B(n,j);
 +
:::    '''fi''';
 +
 +
::  '''od''';
 +
:'''od'''; 
 +
 +
:::::<span style="color: blue">'''Assertion 5''':    Dla każdych wartości</span> <math>\color{blue} i,j, 1\leq i \leq n, 1\leq j \leq n,
 +
\qquad C_{i,j} = \sum\limits_{k=1}^{ n } (A_{i,k} *B_{k,j} ) </math>
 +
   
 +
'''end''' Winograd;
 +
 +
-------------------------------------------------------
  
  \OD; \{ j \}
 
\OD;  \{ i \} \\
 
\fbox{\textbf{Assertion 5:} \  Dla każdych wartości $i,j, 1\leq i \leq n, 1\leq j \leq n$,
 
\qquad $C_{i,j} = \sum\limits_{k=1}^{ n } (A_{i,k} *B_{k,j} ) $} \\
 
\STATE $\mathbf{end} Winograd;$
 
\end{algorithmic}
 
</math>
 
 
== Dowód poprawności ==
 
== Dowód poprawności ==
Naszym zadaniem jest wykazać nastepującą implikację
+
Naszym zadaniem jest wykazać prawdziwość następującej implikacji
  
::::<math>\color{blue} \mathrm{Precondition} \Rightarrow [\mathrm{Algorytm\ Winograda}]\mathrm{Postcondition</math>
+
:::: <span style=color:blue> '''Precondition''' <math>\Rightarrow </math> [Algorytm_Winograda]'''Postcondition''' </span>
  
 
co się czyta tak:  ''jeśli dane spełniają warunek wstępny to algorytm Winograda kończy obliczenia nie sygnalizując błędu i wyniki spełniają warunek końcowy''.
 
co się czyta tak:  ''jeśli dane spełniają warunek wstępny to algorytm Winograda kończy obliczenia nie sygnalizując błędu i wyniki spełniają warunek końcowy''.
  
 
Sprawdzenie czy warunek wstępny jest spełniony przez dane może być w części wykonane  przez kompilator. Kompilator może sprawdzić czy parametry aktualne sa tablicami dwuwymiarowymi.
 
Sprawdzenie czy warunek wstępny jest spełniony przez dane może być w części wykonane  przez kompilator. Kompilator może sprawdzić czy parametry aktualne sa tablicami dwuwymiarowymi.
Druga część warunku, że rozmiary tablic są równe <math>n \times n</math> nie może być sprawdzona przed wywołaniem procedury Winograd, nie może też być udowodniona. Wobec tego wykonywanie procedury rozpoczynamy od (dynamicznego) sprawdzania kształtu i rozmiaru tablic\footnote{W loglanie'82 dwuwymiarowej tablicy możemy nadać kształt trójkatny, wstęgowy i oczywiście kształt kwadratowy}.  Można rozważać czy nie dałoby się udowodnić, o programie stosującym procedurę Winograd, że warunek wstępny jest spełniony za każdym razem gdy procedura Winograd jest wywoływana w naszym programie. Ale czy można zagwarantować, że każdy program stosujący algorytm Winograda będzie sprawdzał warunek wstępny? lub go dowodził? Lepiej więc zostawić sprawdzanie warunku wstępnego procedurze Winograd. \\
+
Druga część warunku, że rozmiary tablic są równe <math>n \times n</math> nie może być sprawdzona przed wywołaniem procedury Winograd, nie może też być udowodniona. Wobec tego wykonywanie procedury rozpoczynamy od (dynamicznego) sprawdzania kształtu i rozmiaru tablic<ref>W loglanie'82 dwuwymiarowej tablicy możemy nadać kształt trójkątny, wstęgowy i oczywiście kształt kwadratowy</ref>.  Można rozważać czy nie dałoby się udowodnić, o programie stosującym procedurę Winograd, że warunek wstępny jest spełniony za każdym razem gdy procedura Winograd jest wywoływana w naszym programie. Ale czy można zagwarantować, że każdy program stosujący algorytm Winograda będzie sprawdzał warunek wstępny? lub go dowodził? Lepiej więc zostawić sprawdzanie warunku wstępnego procedurze Winograd.  
 +
 
 +
Do algorytmu Winograd wstawiliśmy asercje. Podstawowa koncepcja asercji jest taka: ''ilekroć podczas wykonywania programu dochodzimy do miejsca wystąpienia asercji, to bieżący stan pamięci spełnia warunek asercji''. To jest tylko postulat, hipoteza. Jak taki postulat sprawdzić? Nie istnieje ogólna metoda, którą można by np. wbudować do kompilatora.
  
Do algorytmu Winograd wstawiliśmy asercje. Mają one za zadanie:
+
Przyjmuje się więc, że asercje mają za zadanie:
 
# umożliwić sygnalizację naruszenia warunku asercji w trakcie wykonywania programu,
 
# umożliwić sygnalizację naruszenia warunku asercji w trakcie wykonywania programu,
 
# ułatwić argumentację na rzecz  tezy o poprawności algorytmu.
 
# ułatwić argumentację na rzecz  tezy o poprawności algorytmu.
W tym przypadku trudno mówić o dynamiczej weryfikacji: ażeby sprawdzić warunek wyliczony w asercji trzeba powtórzyć obliczenia -- to niewiele nam daje. Ponadto, tu uwaga natury ogólnej, zamiana asercji na instrukcję warunkową
+
W naszym przypadku trudno mówić o dynamicznej weryfikacji: ażeby sprawdzić warunek wyliczony w asercji trzeba powtórzyć obliczenia -- a to niewiele nam daje. Ponadto, tu uwaga natury ogólnej, zamiana asercji na instrukcję warunkową
:::<math>\quad \mathbf{if} \ warunek \ asercji \ \mathbf{then} \   wrzuc \ wyjatek \ \mathbf{fi}</math>
+
:::'''if''' '''not'''(warunek asercji) '''then'''   wrzuć wyjątek '''fi'''
zapewnia tylko tyle, że podczas wykonywania algorytmu zostanie zasygnalizowany błąd. Nie mamy nawet gwarancji, że zdarzy się to zawsze gdy program zawiera błędy.  
+
zapewnia tylko tyle, że podczas wykonywania algorytmu może zostać  zasygnalizowany błąd. Nie mamy nawet gwarancji, że zdarzy się to zawsze gdy program zawiera błędy.  
  
Natomiast asercje możemy zastąpić lematami i udowodnić je
+
Proponujemy by asercje zastąpić lematami i udowodnić je, statycznie, raz na zawsze.
  
 
'''Lemat''' 1  
 
'''Lemat''' 1  
 
Dla każdego <math>j, 1\leq j \leq n</math>, i <math>m=n \div 2</math> zachodzi
 
Dla każdego <math>j, 1\leq j \leq n</math>, i <math>m=n \div 2</math> zachodzi
  
<math>\left \{ \color{red}K\color{black}:  
+
:::<math>\left \{ \color{red}K\color{black}:  
 
\begin{array}{l}
 
\begin{array}{l}
 
\quad s:=0; \\
 
\quad s:=0; \\
Linia 177: Linia 168:
 
Dla każdego <math>j, 1\leq j \leq n</math>, i <math>m=n \div 2</math> zachodzi
 
Dla każdego <math>j, 1\leq j \leq n</math>, i <math>m=n \div 2</math> zachodzi
  
<math> \left \{
+
:::<math> \left \{
 
\begin{array}{l}
 
\begin{array}{l}
 
\quad s:=0; \\
 
\quad s:=0; \\
Linia 197: Linia 188:
  
  
<math>\left \{  
+
:::<math>\left \{  
 
\begin{array}{l}  
 
\begin{array}{l}  
\quad  s:= 0; \\
+
    s:= 0; \\
\quad    \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\
+
    \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\
 
     \mathbf{do} \\
 
     \mathbf{do} \\
     \qquad s:= (A[i,2*k-1]+B[2*k,j]) * (B[2*k-1,j]+A[i,2*k]) +s; \\
+
     \quad s:= (A[i,2*k-1]+B[2*k,j]) * (B[2*k-1,j]+A[i,2*k]) +s; \\
\quad    \mathbf{od};
+
      \mathbf{od};
 
\end{array}  
 
\end{array}  
 
\right \}  
 
\right \}  
Linia 212: Linia 203:
 
Przy założeniu, że tablice A i B są macierzami kwadratowymi rozmiaru n x n i że zachodzą lematy 1 oraz 2, prawdziwa jest nastepująca formuła algorytmiczna  
 
Przy założeniu, że tablice A i B są macierzami kwadratowymi rozmiaru n x n i że zachodzą lematy 1 oraz 2, prawdziwa jest nastepująca formuła algorytmiczna  
  
<math>\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}  
+
:::<math>\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}  
 
\left \{ \begin{array}{l}    s:= 0; \\
 
\left \{ \begin{array}{l}    s:= 0; \\
 
     \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\
 
     \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\
 
     \mathbf{do} \\
 
     \mathbf{do} \\
 
     \quad s:= (A[i,2*k-1]+B[2*k,j]) \\
 
     \quad s:= (A[i,2*k-1]+B[2*k,j]) \\
\qquad * (B[2*k-1,j]+A[i,2*k]) +s;\\  
+
\quad * (B[2*k-1,j]+A[i,2*k]) +s;\\  
 
     \mathbf{od};\\
 
     \mathbf{od};\\
 
     C[i,j] := s -W[i] -V[j]; \\
 
     C[i,j] := s -W[i] -V[j]; \\
Linia 226: Linia 217:
 
'''Lemat 5''' Z lematów 1 i 2 wynika, że następująca formuła jest prawdziwa   
 
'''Lemat 5''' Z lematów 1 i 2 wynika, że następująca formuła jest prawdziwa   
 
   
 
   
<math>\left \{ \begin{array}{l}   
+
:::<math>\left \{ \begin{array}{l}   
 
\mathbf{for} \ i:=1\ to \ n \\
 
\mathbf{for} \ i:=1\ to \ n \\
 
\mathbf{do} \\
 
\mathbf{do} \\
\quad \mathbf{for} \ j:=1\ to \ n \\
+
\mathbf{for} \ j:=1\ to \ n \\
\quad \mathbf{do} \\
+
\mathbf{do} \\
\quad \quad s:= 0; \\
+
\quad s:= 0; \\
\quad \quad    \mathbf{for} \ $k:= 1$\ \mathbf{to}\ $m$ \\
+
\quad    \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\
\quad \quad    \mathbf{do} \\
+
\quad    \mathbf{do} \\
\quad \quad    \quad s:= (A[i,2*k-1]+B[2*k,j]) \\
+
\quad    \quad s:= (A[i,2*k-1]+B[2*k,j]) \\
\quad \quad \qquad * (B[2*k-1,j]+A[i,2*k]) +s;\\  
+
\quad \qquad * (B[2*k-1,j]+A[i,2*k]) +s;\\  
\quad \quad    \mathbf{od};\\
+
\quad    \mathbf{od};\\
\quad \quad    C[i,j] := s -W[i] -V[j]; \\
+
\quad    C[i,j] := s -W[i] -V[j]; \\
\quad \quad    \mathbf{if} \ \mathbf{not} \ p \\
+
\quad    \mathbf{if} \ \mathbf{not} \ p \\
\quad \quad  \mathbf{then} \\
+
\quad  \mathbf{then} \\
\quad \quad \qquad C[i,j] :=C[i,j] + A[i,n]*B[n,j]\\
+
\quad \qquad C[i,j] :=C[i,j] + A[i,n]*B[n,j]\\
\quad \quad  \mathbf{fi}; \\
+
\quad  \mathbf{fi}; \\
\quad  \mathbf{od} \\
+
  \mathbf{od} \\
 
\mathbf{od} \\
 
\mathbf{od} \\
 
\end{array} \right \} \  
 
\end{array} \right \} \  
\left (\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}\,C_{i,j} =  
+
\left (\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}\ C_{i,j} =  
 
\sum\limits_{k=1}^{n} A_{i,k} *B_{k,j}  \right )</math>
 
\sum\limits_{k=1}^{n} A_{i,k} *B_{k,j}  \right )</math>
  
Linia 252: Linia 243:
 
W dowodzie wykorzystujemy następującą własność programów '''for''':  
 
W dowodzie wykorzystujemy następującą własność programów '''for''':  
  
niech napis <math>\omega(i)</math> oznacza wyrażenie arytmetyczne (zmienna ,math>i</math> może, ale nie musi, w nim wystepować)
+
niech napis <math>\omega(i)</math> oznacza wyrażenie arytmetyczne (zmienna <math>i</math> może, ale nie musi, w nim wystepować)
<math>\left \{ \begin{array}{l}
+
:::<math>\left \{ \begin{array}{l}
 
             s := 0\\
 
             s := 0\\
 
             \mathbf{for}\ i:= 1\ \mathbf{to}\ n \\
 
             \mathbf{for}\ i:= 1\ \mathbf{to}\ n \\
Linia 261: Linia 252:
 
           \end{array}
 
           \end{array}
 
\right \}\left (s =\sum\limits_{i=0}^{n} \omega(i)  \right ) </math>
 
\right \}\left (s =\sum\limits_{i=0}^{n} \omega(i)  \right ) </math>
 +
 
Pozostaje skorzystać z aksjomatu instrukcji przypisania
 
Pozostaje skorzystać z aksjomatu instrukcji przypisania
<math>\left (s =\sum\limits_{i=0}^{n} \omega(i)  \right )\implies \{W[j] := s \}\left (W(j) =\sum\limits_{i=0}^{n} \omega(i)  \right )   
+
 
\begin{flushright}
+
:::<math>\left (s =\sum\limits_{i=0}^{n} \omega(i)  \right )\implies \{ W[j] := s \}\left (W[j] =\sum\limits_{i=0}^{n} \omega(i)  \right )   
$\Box$      \end{flushright} </math>
+
  </math>
 +
 
 
Dowody lematów 2 i 3 przebiegają podobnie.
 
Dowody lematów 2 i 3 przebiegają podobnie.
 +
 
=== Dowód lematu 4 ===
 
=== Dowód lematu 4 ===
 
Należy udowodnić
 
Należy udowodnić
 +
 
<math>(s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k})\implies (s-W[i]-V[j]=\sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j})  </math>
 
<math>(s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k})\implies (s-W[i]-V[j]=\sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j})  </math>
Rozwińmy mnożenie i zastosujmy rozdzielność mnożenia wzgledem dodawania
+
 
 +
Rozwińmy mnożenie i zastosujmy rozdzielność mnożenia względem dodawania
 +
 
 
<math>\begin{array}{l} \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k}) \\
 
<math>\begin{array}{l} \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k}) \\
 
\quad =  \sum\limits_{k=1}^{n \div 2} [A_{i,2k-1}*B_{2*k-1,j}+  
 
\quad =  \sum\limits_{k=1}^{n \div 2} [A_{i,2k-1}*B_{2*k-1,j}+  
Linia 281: Linia 278:
 
\end{array}  
 
\end{array}  
 
</math>
 
</math>
 +
 
Skorzystamy z lematów 1 oraz 2
 
Skorzystamy z lematów 1 oraz 2
 +
 
<math> \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k})-W[i]-V[j]=
 
<math> \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k})-W[i]-V[j]=
 
  \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+  
 
  \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+  
%\sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*A_{i,2k}+
 
%\sum\limits_{k=1}^{n \div 2} B_{2k,j}*B_{2*k,j}+
 
 
\sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k}
 
\sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k}
 
</math>
 
</math>
 +
 
Wykorzystujemy przemienność mnożenia i łączność dodawania
 
Wykorzystujemy przemienność mnożenia i łączność dodawania
 +
 
<math>  \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+  
 
<math>  \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+  
%\sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*A_{i,2k}+
 
%\sum\limits_{k=1}^{n \div 2} B_{2k,j}*B_{2*k,j}+
 
 
\sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k}=
 
\sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k}=
  \sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j} %+
+
  \sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j}
%\sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*A_{i,2k}+
+
%\sum\limits_{k=1}^{n \div 2} B_{2k,j}*B_{2*k,j}+
+
%\sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k}
+
 
</math>
 
</math>
 
=== Dowód lematu 5 ===
 
=== Dowód lematu 5 ===
 
Trzeba wykazać, że
 
Trzeba wykazać, że
</math>(s= \sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j}  )\implies \left \{ \begin{array}{l}   
+
 
 +
<math>(s= \sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j}  )\implies \left \{ \begin{array}{l}   
 
     \mathbf{if} \ \mathbf{not} \ p \\
 
     \mathbf{if} \ \mathbf{not} \ p \\
 
     \mathbf{then} \\
 
     \mathbf{then} \\
Linia 306: Linia 301:
 
   \mathbf{fi};   
 
   \mathbf{fi};   
 
\end{array} \right \}(s= \sum\limits_{k=1}^{n} A_{i,k}*B_{k,j} )  </math>
 
\end{array} \right \}(s= \sum\limits_{k=1}^{n} A_{i,k}*B_{k,j} )  </math>
Pamiętamy, że p= (n mod 2 =0). Jeżeli n jest liczbą parzystą to <math>2(n \div 2)=n</math>.
+
 
 +
Pamiętamy, że p= (n mod 2 =0). Jeżeli n jest liczbą parzystą to <math>2(n \div 2)=n</math> i obliczona wartość <math>C_{i,j}</math> jest poprawna.
 +
W przypadku gdy n jest liczbą nieparzystą, tzn. gdy '''not''' p, wartością wyrażenia <math>2*(n \div 2)</math> jest <math>n-1</math>. Po wykonaniu instrukcji '''if''' '''not''' p ...
 +
 
 +
zachodzi
 +
 
<math>C_{i,j} =  
 
<math>C_{i,j} =  
\sum\limits_{k=1}^{n} A_{i,k} *B_{k,j}  </math>]
+
\sum\limits_{k=1}^{n} A_{i,k} *B_{k,j}  </math>
W przeciwnym przypadku (tzn. gdy \textbf{not} $p$) sumowanie zakonczyło się dla </math>k=n-1</math>.
+
 
Trzeba więc dodać wartość <math>A[i,n]*B[n,j]</math>
+
::::::::::c.b.d.o
c.b.d.o
+
 
 +
== Uwagi końcowe ==
 +
 
 +
Zauważ, że udowodniliśmy twierdzenie o poprawności całej klasy algorytmów.
 +
 
 +
algorytm ten pozwala mnożyć macierze liczb zespolonych, macierzy, których elementami są macierze, macierzy wielomianów, ...
 +
Zmieńmy nagłówek procedury
 +
 
 +
'''unit''' Winograd_Generic: '''procedure'''( '''type''' T, '''function''' add(x,y:T):T,  '''function''' mult(x,y:T):T,
 +
::'''function''' subt(x,y:T):T, A, B: '''array_of array_of'''' T, output C: '''array_of array_of''' T);
 +
           
 +
 
 +
i odpowiednio w treści procedury zapiszmy add(u,v) zamiast u+v, mult(u,v) zmiast u*v, subt(u,v) zamiast u-v.,
 +
 
 +
Nasz warunek wstępny przybiera teraz inną postać
 +
 
 +
'''Precondition''': Argumenty procedury Winograd_Generic spełniają następujące warunki
 +
# pierwszy argument jest nazwą klasy,
 +
# kolejne trzy argumenty są nazwami funkcji, dwuargumentowych ze zbioru |T| obiektów typu T w zbiór |T|,
 +
# następne dwa argumenty to nazwy tablic kwadratowych jednakowego rozmiaru n x n,
 +
# ostatni argument także jest nazwą tablicy, tablica ta zostanie przekazana wywołującemu procedurę Winograd_Generic,
 +
# zbiór |T| obiektów klasy T wraz z operacjami add, mult i subt jest ''pierścieniem''.
 +
 
 +
 
 +
'''Postcondition''': Algorytm zwraca tablicę C obiektów typu T, taką, że dla każdych <math>i,j, \ 1\leq i \leq n, 1 \leq j \leq n</math>
 +
 
 +
:::::<math> C_{i,j}= \sum\limits_{k=0}^{n}\ A_{i,k}.mult (B_{k,j}) </math>
 +
 
 +
System TeX wymusił na mnie zastosowanie znaku  <math>\sum </math> chociaż wolałbym w tym miejscu znak 'add',  <math>add</math>.
 +
 
 +
Można algorytm  zapisać jeszcze inaczej, w zasięgu deklaracji typu (klasy) T, a jeszcze lepiej w zasięgu interfejsu Pierscien.
 +
Wtedy nagłówki obu procedur będą identyczne.  
 +
 
 +
 
 +
'''unit''' ring: '''class'''
 +
::'''unit virtual''' add: '''function'''(x: ring):ring;
 +
::'''unit virtual''' sub: '''function'''(x: ring):ring;
 +
::'''unit virtual''' mult: '''function'''(x: ring): ring;
 +
::'''unit virtual''' zero: '''function'''(): ring;
 +
::'''unit virtual''' one: '''function'''(): ring;
 +
::(* AXIOMS of ring *)
 +
:::tu wpisać aksjomaty pierścieni
 +
'''end'''ring
 +
 
 +
 
 +
'''unit''' rngeWinograd: '''extends''' ring '''class''';
 +
:: '''unit''' Winograd: '''procedure'''(A, B: '''arrayof arrayof''' ring, '''output''' C: '''arrayof arrayof''' ring);
 +
::{* tu treść procedury *)
 +
::'''end''' Winograd
 +
'''end''' rngeWinograd;
 +
 
 +
Komentarz
 +
 
 +
Zastosowanie algorytmu Winograda w pewnym pierścieniu P wymaga
 +
# rozszerzenia tej klasy i zadeklarowania odpowiednich metod: add, sub, mult,np.
 +
:::'''unit''' MM: '''extends''' rngeWinograd '''class''';
 +
# stworzenia i wypełnienia tablic A i B,
 +
# wywołania procedury Winograd.
 +
 
 +
== Zobacz też ==
 +
* [[pierścień ]]
 +
 
 +
{{Uwagi}}
 +
 
 +
{{Przypisy}}
 +
<references/>

Aktualna wersja na dzień 18:18, 27 lis 2015

Przedstawiamy algorytm Winograda i dowód jego poprawności.

Wprowadzenie

Algorytm Winograda może być przydatny w obliczaniu iloczynu macierzy kwadratowych, szczególnie gdy elementami macierzy są obiekty reprezentujące jakieś ciało inne od ciała liczb rzeczywistych. Tzn. w przypadku gdy dane ciało [math]\mathcal{C}[/math] jest zaimplementowane w programie przez klasę [math]C[/math].

Drugim i ważniejszym powodem do skreślenia tej notatki jest potrzeba zwrócenia uwagi na znikomą przydatność asercji i tzw. programowania przez kontrakt (ang. design by contract). Asercje są przydatne jako notatki, ale nie stanowią rozwiązania problemu zapewnienia poprawności algorytmu.

Zapraszam do czytania, zwłaszcza rozdziału Dowód poprawności.

Algorytm

W tym rozdziale pojawiają się dwa warunki:

  • warunek wstępny -- Precondition,
  • warunek końcowy -- Postcondition, oraz
  • algorytm -- algorytm Winograda.


Jak się upewnić, że algorytm jest poprawny ze względu na warunek początkowy i warunek końcowy? W literaturze proponowane są dwa podejścia:

  • udowodnij częściową porawność sprawdzając pewne niezmienniki -- jest to tzw. metoda Floyda-Hoare'a,
  • wykonaj pewną liczbę obliczeń testowych i zaufaj, że w trakcie eksploatacji algorytmu nie okaże się, że jest on niepoprawny.

W obliczeniach testowych można w trakcie obliczeń sprawdzać wcześniej wstawione warunki -- asercje. Czy rzeczywiście są one pomocne?

W następnym rozdziale pokażemy inną drogę - drogę dowodzenia lematów i w końcu twierdzenia o poprawności (całkowitej) algorytmu Winograda względem warunku początkowego Precondition i warunku końcowego Postcondition.


signal Niezgoda;

unit Winograd: procedure(A,B: arrayof arrayof real; output C: arrayof arrayof real);

Precondition: A i B są macierzami dwuwymiarowymi o tym samym rozmiarze n x n

Postcondition: macierz C jest produktem macierzy A i B, C=A x B

var i,j,k,n,m: integer, W,V: arrayof real,
p: boolean,
s: real;

begin

(* ustalic czy macierze moga byc mnozone tzn.
czy ilosc wierszy w A = ilosc kolumn w B? *)
(* ustalic czy n jest parzyste? *)
(* obliczyc "preprocessing" *)
i := upper(A);
j := lower(A);
k := i-j;
i:= upper(B);
j:= lower(B);
if i-j <> k then raise Niezgoda fi;
(* mozna mnozyc *)
n := k+1;
p := (n mod 2) = 0;
m := n div 2;
array W dim (1:n);
array V dim (1:n);
array C dim (1:n);
for i := 1 to n
do
array C(i) dim (1:n)
od;
(* obliczanie "preprocessingu" *)
for j:= 1 to n
do
s:=0;
for i := 1 to m
do
s := A(j,2*i-1) * A(j,2*i) +s;
od;
W(j) := s;
od;
Assertion 1: Dla każdego [math]\color{blue} j, 1\leq j \leq n,\qquad W_j=\sum\limits_{i=1}^{n \div 2} A_{j,2i-1}*A_{j,2i} [/math]


for j:= 1 to n
do
s:=0;
for i := 1 to m
do
s := B(2*i-1,j) * B(2*i,j) +s;
od;
V(j) := s;
od;
Assertion 2: Dla każdego [math]\color{blue} j, 1 \leq j \leq n,\qquad V_j=\sum\limits_{i=1}^{n \div 2} B_{2i-1,j}*B_{2*i,j}[/math]
(* obliczanie iloczynu macierzy *)
for i := 1 to n
do
for j := 1 to n
do
s:= 0;
for k:= 1 to m
do
s:= (A(i,2*k-1)+B(2*k,j)) * (B(2*k-1,j)+A(i,2*k)) +s;
od;
Assertion 3 [math]\color{blue} \forall_{1\leq i \leq n},\forall_{1\leq j \leq n}, \qquad s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k}) [/math]
C(i,j) :=s-W(i)-V(j);
Assertion 4: [math]\color{blue} \forall_{1\leq i \leq n},\forall_{1\leq j \leq n} \qquad C_{i,j} = \sum\limits_{k=1}^{2(n\div2)} (A_{i,k} *B_{k,j} ) [/math]
if not p
then
C(i,j) := C(i,j) + A(i,n)*B(n,j);
fi;
od;
od;
Assertion 5: Dla każdych wartości [math]\color{blue} i,j, 1\leq i \leq n, 1\leq j \leq n, \qquad C_{i,j} = \sum\limits_{k=1}^{ n } (A_{i,k} *B_{k,j} ) [/math]

end Winograd;


Dowód poprawności

Naszym zadaniem jest wykazać prawdziwość następującej implikacji

Precondition [math]\Rightarrow [/math] [Algorytm_Winograda]Postcondition

co się czyta tak: jeśli dane spełniają warunek wstępny to algorytm Winograda kończy obliczenia nie sygnalizując błędu i wyniki spełniają warunek końcowy.

Sprawdzenie czy warunek wstępny jest spełniony przez dane może być w części wykonane przez kompilator. Kompilator może sprawdzić czy parametry aktualne sa tablicami dwuwymiarowymi. Druga część warunku, że rozmiary tablic są równe [math]n \times n[/math] nie może być sprawdzona przed wywołaniem procedury Winograd, nie może też być udowodniona. Wobec tego wykonywanie procedury rozpoczynamy od (dynamicznego) sprawdzania kształtu i rozmiaru tablic[1]. Można rozważać czy nie dałoby się udowodnić, o programie stosującym procedurę Winograd, że warunek wstępny jest spełniony za każdym razem gdy procedura Winograd jest wywoływana w naszym programie. Ale czy można zagwarantować, że każdy program stosujący algorytm Winograda będzie sprawdzał warunek wstępny? lub go dowodził? Lepiej więc zostawić sprawdzanie warunku wstępnego procedurze Winograd.

Do algorytmu Winograd wstawiliśmy asercje. Podstawowa koncepcja asercji jest taka: ilekroć podczas wykonywania programu dochodzimy do miejsca wystąpienia asercji, to bieżący stan pamięci spełnia warunek asercji. To jest tylko postulat, hipoteza. Jak taki postulat sprawdzić? Nie istnieje ogólna metoda, którą można by np. wbudować do kompilatora.

Przyjmuje się więc, że asercje mają za zadanie:

  1. umożliwić sygnalizację naruszenia warunku asercji w trakcie wykonywania programu,
  2. ułatwić argumentację na rzecz tezy o poprawności algorytmu.

W naszym przypadku trudno mówić o dynamicznej weryfikacji: ażeby sprawdzić warunek wyliczony w asercji trzeba powtórzyć obliczenia -- a to niewiele nam daje. Ponadto, tu uwaga natury ogólnej, zamiana asercji na instrukcję warunkową

if not(warunek asercji) then wrzuć wyjątek fi

zapewnia tylko tyle, że podczas wykonywania algorytmu może zostać zasygnalizowany błąd. Nie mamy nawet gwarancji, że zdarzy się to zawsze gdy program zawiera błędy.

Proponujemy by asercje zastąpić lematami i udowodnić je, statycznie, raz na zawsze.

Lemat 1 Dla każdego [math]j, 1\leq j \leq n[/math], i [math]m=n \div 2[/math] zachodzi

[math]\left \{ \color{red}K\color{black}: \begin{array}{l} \quad s:=0; \\ \quad \mathbf{for}\ i:=1\ \mathbf{to}\ m \\ \quad \mathbf{do} \\ \qquad s := A[j, 2*i-1] * A[j,2*i]+s; \\ \quad \mathbf{od} ; \\ \quad W[j]:=s; \end{array} \right \} \left (W_j=\sum\limits_{i=1}^{n \div 2} A_{j,2i-1}*A_{j,2i}\right ) [/math]


Lemat 2 Dla każdego [math]j, 1\leq j \leq n[/math], i [math]m=n \div 2[/math] zachodzi

[math] \left \{ \begin{array}{l} \quad s:=0; \\ \quad \mathbf{for}\ i:=1\ \mathbf{to}\ m \\ \quad \mathbf{do} \\ \qquad s := B[2*i-1,j] * B[2*i,j]+s; \\ \quad \mathbf{od} ; \\ \quad V[j]:=s; \end{array} \right \} \left (V_j=\sum\limits_{i=1}^{n \div 2} B_{2i-1,j}*B_{2i,j}\right ) [/math]


Kolejny lemat

Lemat 3 Dla każdych i, j


[math]\left \{ \begin{array}{l} s:= 0; \\ \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\ \mathbf{do} \\ \quad s:= (A[i,2*k-1]+B[2*k,j]) * (B[2*k-1,j]+A[i,2*k]) +s; \\ \mathbf{od}; \end{array} \right \} \left (s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2k-1,j}+A_{i,2k}) \right )[/math]


Lemat 4 Przy założeniu, że tablice A i B są macierzami kwadratowymi rozmiaru n x n i że zachodzą lematy 1 oraz 2, prawdziwa jest nastepująca formuła algorytmiczna

[math]\forall_{1\leq i \leq n},\forall_{1\leq j \leq n} \left \{ \begin{array}{l} s:= 0; \\ \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\ \mathbf{do} \\ \quad s:= (A[i,2*k-1]+B[2*k,j]) \\ \quad * (B[2*k-1,j]+A[i,2*k]) +s;\\ \mathbf{od};\\ C[i,j] := s -W[i] -V[j]; \\ \end{array} \right \} \left (C_{i,j} = \sum\limits_{k=1}^{2*(n \div 2)} A_{i,k}*B_{k,j} \right )[/math]


Lemat 5 Z lematów 1 i 2 wynika, że następująca formuła jest prawdziwa

[math]\left \{ \begin{array}{l} \mathbf{for} \ i:=1\ to \ n \\ \mathbf{do} \\ \mathbf{for} \ j:=1\ to \ n \\ \mathbf{do} \\ \quad s:= 0; \\ \quad \mathbf{for} \ k:= 1\ \mathbf{to}\ m \\ \quad \mathbf{do} \\ \quad \quad s:= (A[i,2*k-1]+B[2*k,j]) \\ \quad \qquad * (B[2*k-1,j]+A[i,2*k]) +s;\\ \quad \mathbf{od};\\ \quad C[i,j] := s -W[i] -V[j]; \\ \quad \mathbf{if} \ \mathbf{not} \ p \\ \quad \mathbf{then} \\ \quad \qquad C[i,j] :=C[i,j] + A[i,n]*B[n,j]\\ \quad \mathbf{fi}; \\ \mathbf{od} \\ \mathbf{od} \\ \end{array} \right \} \ \left (\forall_{1\leq i \leq n},\forall_{1\leq j \leq n}\ C_{i,j} = \sum\limits_{k=1}^{n} A_{i,k} *B_{k,j} \right )[/math]

Dowody lematów

Dowód lematu 1

W dowodzie wykorzystujemy następującą własność programów for:

niech napis [math]\omega(i)[/math] oznacza wyrażenie arytmetyczne (zmienna [math]i[/math] może, ale nie musi, w nim wystepować)

[math]\left \{ \begin{array}{l} s := 0\\ \mathbf{for}\ i:= 1\ \mathbf{to}\ n \\ \mathbf{do} \\ \quad s := \omega(i) +s \\ \mathbf{od} \end{array} \right \}\left (s =\sum\limits_{i=0}^{n} \omega(i) \right ) [/math]

Pozostaje skorzystać z aksjomatu instrukcji przypisania

[math]\left (s =\sum\limits_{i=0}^{n} \omega(i) \right )\implies \{ W[j] := s \}\left (W[j] =\sum\limits_{i=0}^{n} \omega(i) \right ) [/math]

Dowody lematów 2 i 3 przebiegają podobnie.

Dowód lematu 4

Należy udowodnić

[math](s = \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k})\implies (s-W[i]-V[j]=\sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j}) [/math]

Rozwińmy mnożenie i zastosujmy rozdzielność mnożenia względem dodawania

[math]\begin{array}{l} \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k}) \\ \quad = \sum\limits_{k=1}^{n \div 2} [A_{i,2k-1}*B_{2*k-1,j}+ A_{i,2k-1}*A_{i,2k}+ B_{2k,j}*B_{2k-1,j}+ B_{2k,j}*A_{i,2k} ] \\ \quad = \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+ \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*A_{i,2k}+ \sum\limits_{k=1}^{n \div 2} B_{2k,j}*B_{2k-1,j}+ \sum\limits_{=1}^{n \div 2} B_{2k,j}*A_{i,2k} \\ \end{array} [/math]

Skorzystamy z lematów 1 oraz 2

[math] \sum\limits_{k=1}^{n \div 2} (A_{i,2k-1}+B_{2k,j})*(B_{2*k-1,j}+A_{i,2k})-W[i]-V[j]= \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+ \sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k} [/math]

Wykorzystujemy przemienność mnożenia i łączność dodawania

[math] \sum\limits_{k=1}^{n \div 2} A_{i,2k-1}*B_{2*k-1,j}+ \sum\limits_{k=1}^{n \div 2} B_{2k,j}*A_{i,2k}= \sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j} [/math]

Dowód lematu 5

Trzeba wykazać, że

[math](s= \sum\limits_{k=1}^{2(n \div 2)} A_{i,k}*B_{k,j} )\implies \left \{ \begin{array}{l} \mathbf{if} \ \mathbf{not} \ p \\ \mathbf{then} \\ \qquad C[i,j] :=C[i,j] + A[i,n]*B[n,j]\\ \mathbf{fi}; \end{array} \right \}(s= \sum\limits_{k=1}^{n} A_{i,k}*B_{k,j} ) [/math]

Pamiętamy, że p= (n mod 2 =0). Jeżeli n jest liczbą parzystą to [math]2(n \div 2)=n[/math] i obliczona wartość [math]C_{i,j}[/math] jest poprawna. W przypadku gdy n jest liczbą nieparzystą, tzn. gdy not p, wartością wyrażenia [math]2*(n \div 2)[/math] jest [math]n-1[/math]. Po wykonaniu instrukcji if not p ...

zachodzi

[math]C_{i,j} = \sum\limits_{k=1}^{n} A_{i,k} *B_{k,j} [/math]

c.b.d.o

Uwagi końcowe

Zauważ, że udowodniliśmy twierdzenie o poprawności całej klasy algorytmów.

algorytm ten pozwala mnożyć macierze liczb zespolonych, macierzy, których elementami są macierze, macierzy wielomianów, ... Zmieńmy nagłówek procedury

unit Winograd_Generic: procedure( type T, function add(x,y:T):T, function mult(x,y:T):T,

function subt(x,y:T):T, A, B: array_of array_of' T, output C: array_of array_of T);


i odpowiednio w treści procedury zapiszmy add(u,v) zamiast u+v, mult(u,v) zmiast u*v, subt(u,v) zamiast u-v.,

Nasz warunek wstępny przybiera teraz inną postać

Precondition: Argumenty procedury Winograd_Generic spełniają następujące warunki

  1. pierwszy argument jest nazwą klasy,
  2. kolejne trzy argumenty są nazwami funkcji, dwuargumentowych ze zbioru |T| obiektów typu T w zbiór |T|,
  3. następne dwa argumenty to nazwy tablic kwadratowych jednakowego rozmiaru n x n,
  4. ostatni argument także jest nazwą tablicy, tablica ta zostanie przekazana wywołującemu procedurę Winograd_Generic,
  5. zbiór |T| obiektów klasy T wraz z operacjami add, mult i subt jest pierścieniem.


Postcondition: Algorytm zwraca tablicę C obiektów typu T, taką, że dla każdych [math]i,j, \ 1\leq i \leq n, 1 \leq j \leq n[/math]

[math] C_{i,j}= \sum\limits_{k=0}^{n}\ A_{i,k}.mult (B_{k,j}) [/math]

System TeX wymusił na mnie zastosowanie znaku [math]\sum [/math] chociaż wolałbym w tym miejscu znak 'add', [math]add[/math].

Można algorytm zapisać jeszcze inaczej, w zasięgu deklaracji typu (klasy) T, a jeszcze lepiej w zasięgu interfejsu Pierscien. Wtedy nagłówki obu procedur będą identyczne.


unit ring: class

unit virtual add: function(x: ring):ring;
unit virtual sub: function(x: ring):ring;
unit virtual mult: function(x: ring): ring;
unit virtual zero: function(): ring;
unit virtual one: function(): ring;
(* AXIOMS of ring *)
tu wpisać aksjomaty pierścieni

endring


unit rngeWinograd: extends ring class;

unit Winograd: procedure(A, B: arrayof arrayof ring, output C: arrayof arrayof ring);
{* tu treść procedury *)
end Winograd

end rngeWinograd;

Komentarz

Zastosowanie algorytmu Winograda w pewnym pierścieniu P wymaga

  1. rozszerzenia tej klasy i zadeklarowania odpowiednich metod: add, sub, mult,np.
unit MM: extends rngeWinograd class;
  1. stworzenia i wypełnienia tablic A i B,
  2. wywołania procedury Winograd.

Zobacz też

Uwagi

Przypisy

  1. W loglanie'82 dwuwymiarowej tablicy możemy nadać kształt trójkątny, wstęgowy i oczywiście kształt kwadratowy