Programowanie współbieżne i rozproszone/PWR Wykład 7: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Mengel (dyskusja | edycje)
 
(Nie pokazano 1 wersji utworzonej przez jednego użytkownika)
Linia 1: Linia 1:
== Problem bizantyjskich generałów ==
== Algorytmy roproszone ==


== Uogólnienie problemu bizantyjskich generałów ==
Obecnie zajmiemy się algorytmami, które realizują podstawowe zadania w systemach rozproszonych.


=== Założenia wstępne ===
=== Algorytm uzgadniania ===


=== Warunki poprawności ===
; Zadanie
Danych jest <math>n</math> procesorów. Każdy procesor może działać poprawnie lub być wadliwy. Wiadomo, że co najwyżej <math>m</math> procesorów jest wadliwych. Każdy procesor przechowuje pewną wartość. Oznaczmy wartość przechowywaną w procesorze <math>p</math> przez <math>v_p</math>. Procesory porozumiewają się poprzez wymianę komunikatów, która odbywa się zgodnie z następującymi założeniami:
* Każdy procesor może wysłać komunikat do każdego.
* Komunikacja między dobrymi procesorami jest niezawodna.
* Odbiorca zna nadawcę komunikatu.
Zaprojektować algorytm, który umożliwi każdemu procesorowi <math>p</math> ustalenie wektora wartości <math>V_p</math> takiego, że:
# Dla każdych dwóch '''dobrych''' procesorów <math>p</math> i <math>q</math> zachodzi: <math>V_p(q) = v_q</math>.
# Dla każdych dwóch '''dobrych''' procesorów <math>p</math> i<math>q</math> oraz '''dowolnego''' procesora <math>r</math> zachodzi: <math>V_p(r) = V_q(r)</math>


=== Warunki rozwiązywalności ===
Intuicyjnie wartością <math>q</math> współrzędnej wektora <math>V_p(q)</math>
jest to, co procesor <math>p</math> przypuszcza o wartości <math>v_q</math>. Powyższe dwa warunki wyrażają fakt, że w wyniku działania algorytmu, procesor dobry musi uzyskać dokładną wiedzę o wartości przechowywanej w każdym dobrym procesie. Warunek drugi oznacza, że wiedza procesorów dobrych musi być spójna: każdy procesor dobry musi wiedzieć to samo o każdym innym (także wadliwym) procesorze.


=== Przykład ===
=== Wariant pierwszy. <math>n=3, m=0</math> ===
Gdy mamy 3 procesory i wszystkie są dobre to wystarczy, aby każdy przesłał każdemu swoją wartość. Jako <math>V_p(q)</math> procesor <math>p</math> przyjmuje to, co otrzymał od procesora <math>q</math>. Ponieważ komunikacja między dobrymi procesorami jest niezawodna, a wszystkie procesory są dobre, więc oczywiście zachodzi: <math>V_p(q) = v_q</math> i oba warunki poprawności są spełnione.


=== Rekurencyjny algorytm uzgadniania ===
=== Wariant drugi. <math>n=4, m = 1</math> ===
Załóżmy teraz, że wśród 4 procesorów jeden jest wadliwy. Przeanalizujmy tę fazę algorytmu, w której ustala się wiedzę o procesorze wadliwym. Na wszystkich rysunkach w tym wykładzie procesory wadliwe będziemy przedstawiać w postaci kwadratów, a dobre w postaci okręgów. Przypuśćmy, że procesor wadliwy <math>q</math> wysyła do pozostałych trzech wartość <math>v_q</math>. Czy procesory mogą przyjąć jako swoją wiedzę o <math>q</math> to, co od niego otrzymały?
Oczywiście, nie! Ponieważ komunikacja między procesorem wadliwym nie musi być poprawna, więc dobre procesory mogą otrzymać różne wartości. Przyjęcie jako <math>V_p(q)</math> tego, co dotarło do procesu <math>p</math> od procesu <math>q</math> może sposowodować niespełnienie drugiego warunku poprawności:


=== Przykład ===
;Rysunek 1


=== Dowód poprawności ===
Jest zatem potrzebna dodatkowa wiedza o procesie <math>q</math>. Jej źródłem jest dodatkowa wymiana informacji, którą procesory otrzymały od procesora <math>q</math>.
 
Usupełnijmy więc algorytm w następujący sposób. Aby ustalić wiedzę procesorów o procesorze <math>q</math>:
# Procesor <math>q</math> wysyła wartość <math>v_q</math> do pozostałych procesorów. Oznaczmy wartość, którą w wyniku tej komunikacji otrzymał proces <math>p</math> przez <math>v_p'</math>
# Pozostałe procesory wymieniają między sobą wartości otrzymane od procesora <math>q</math>. W ten sposób każdy procesor dysponuje multizbiorem trzech wartości.
# Jako wartość <math>V_p(q)</math> przyjmuje się tę wartość z multizbioru wartości, który dotarły do procesora <math>q</math>, która występuje w niej największą liczbę razy. Tak sposób wyboru będziemy oznaczać wyborem większościowym i oznaczać przez '''maj'''.
Popatrzmy na przykład, w którym procesory ustalają swoją wiedzę na temat procesora dobrego:
 
; Rysunek 2
 
Jednak w dalszym ciągu problematyczna jest sytuacja, w której ustalamy informacje o procesorze wadliwym. Może on wysłać do każdego procesora inną wartość:
 
 
Po wymianie informacji między procesorami dobrymi okazuje się, że dysponują one tym samym multizbiorem wartości. Nie ma w nim jednak wartości pojawiającej się najczęściej. W takiej sytuacji przyjmujemy, że wartością funkcji '''maj''' jest pewna z góry ustalona wartość. Zauważmy, że ponieważ wszystkie procesory mają ten sam multizbiór, więc wartości funkcji '''maj''' będą w nich takie same. Jest to zgodne z wymaganiem drugim. Oczywiście ta wartość nie musi być równa <math>v_q</math>, ale pierwszy warunek poprawności wymaga równości jedynie w przypadku, gdy <math>q</math> jest dobry.
 
=== Wariant 3. <math>n=3, m=1</math> ===
 
Popatrzmy jeszcze na przypadek trzech procesorów wśród których jeden jest wadliwy. Popatrzmy na przypadek, w którym ustala się wartość przechowywaną w wadliwym procesorze.
 
; Rysunek 3
 
Jeśli wadliwy procesor wyśle do pozostałych różne wartości, to będą one dysponowały multizbiorem <math>\{a, b\}</math>. Zatem procesory dobre zakładają, że w procesorze wadliwym jest pewna z góry ustalona wartość <math>c</math>. Zastanówmy się, czym powinno być <math>c</math>. W tym celu rozpatrzmy następującą wymianę komunikatów (tym razem prowadzącą do ustalenia wartości przechowywanej w dobrym procesorze):
 
I tym razem procesor 2 dysponuje multizbiorem <math>\{a, b\}</math>. Aby zapewnić warunek pierwszy poprawności należy zatem przyjąć <math>c=a</math>. Z drugiej jednak strony przy następującym scenariuszu: 
 
; Rysunek 4
 
należy przyjąć <math>c=b</math>. Otrzymana sprzeczność pokazuje, że nie da się rozwiązać problemu dla <math>n=3, m = 1</math>.
 
Z powyższych rozważań wynika, że przedstawiony problem nie zawsze ma rozwiązanie. Warunkiem koniecznym i wystarczającym na to, aby rozwiązanie istniało jest zachowanie nierówności <math>n > 3m</math>. W dalszych rozwiązaniach przyjmujemy, że taka nierówność zachodzi.
 
=== Algorytm OMIC ===
 
Przedstawimy teraz algorytm o nazwie ''Oral Messages Interchange Communication'' (w skrócie: ''OMIC''), który rozwiązuje zadanie postawionie na wstępie przy założenu <math>n>3m</math>.
 
Algorytm OMIC jest rekurencyjny. Składa się on z <math>m+1</math> faz. Jego wykonanie rozpoczyna się od fazy o numerze <math>m</math> a kończy na fazie o numerze 0. Opiszmy najpierw nieformalnie działanie tego algorytmu. W opisie przedstawimy sposób uzyskania informacji o '''jednym''' wyróżnionym procesie. Aby uzyskać informację o wszystkich procesach należy powtórzyć go wyróżniając po kolei każdy proces.
 
Niech OMIC (f) oznacza f-tą fazę algorytmu. Mamy:
#OMIC (0): proces <math>q</math> rozsyła swoją wartość <math>v_q</math> do wszystkich pozostałych procesorów. Oznaczmy przez <math>v_p'</math> to, co proces <math>p</math> otrzymał od procesu <math>q</math>. Przyjmujemy <math>V_p(q) = v_p'</math>. Zauważmy, że w sytuacji, gdy <math>m=0</math> ta jedna faza faktycznie wystarcza do zapewnienia obu warunków poprawności.
 
; Rysunek 5
 
#OMIC(f): proces <math>q</math> rozsyła swoją wartość <math>v_q</math> do wszystkich pozostałych procesorów. Oznaczmy przez <math>v_p'</math> to, co proces <math>p</math> otrzymał od procesu <math>q</math>. Ponieważ proces <math>q</math> mógł oszukiwać, więc tym razem nie można jako <math>V_p(q)</math> przyjąć <math>v_p'</math>, gdyż mogłoby to prowadzić do niespełnienia drugiego warunku poprawności. Tak naprawdę wszystkie procesy z wyjątkiem <math>q</math> powinny teraz uzgodnić wspólną wiedzę o tym, co otrzymały od <math>q</math>, czyli o wartościach primowanych. I to właśnie prowadzi do rekurencji. Wszystkie procesy z wyjątkiem <math>q</math> wykonują teraz bowiem algorytm OMIC(f-1). Każdy proces <math>p</math> biorący udział w tej fazie rozsyła jednak <math>v_p'</math>, a nie <math>v_p</math>. Wykonanie OMIC(f-1) prowadzi oczywiście do obliczenia wartości <math>V_p'(r)</math> w każdym procesie <math>p</math> biorącym udział w tej fazie. Zgodnie z intuicją
<math>V_p'(r)</math> oznacza wiedze, jaką proces p przyjmuje o wartości początkowej z procesu <math>r</math>, czyli  w tym wypadku to, co proces <math>p</math> zakłada o wartości <math>v_r'</math>, czyli jeszcze inaczej: to co proces <math>p</math> zakłada o wartości, którą proces <math>r</math> otrzymał od procesu <math>q</math> wysyłającego <math>v_q</math>.
Popatrzmy, jaką informacją dysponuje teraz proces <math>p</math> o procesie <math>q</math>:
# Po pierwsze ma wartości <math>V_p'(r)</math> dla każdego <math>r\neq p, r \neq q</math>. Jest to informacja o <math>v_q</math> otrzymana za pośrednictwem <math>r</math>.
# Po drugie: wartość <math>v_p'</math>, czyli informacja o <math>v_q</math> uzyskana bezpośrednio od <math>q</math>. Oznaczmy tę wartość jako <math>V_p'(q)</math>.
Jako rozwiązanie, czyli <math>V_p(q)</math> przyjmuje się tę wartość z multizboru <math>\{V_p'(r) | r \neq q \}</math>, która występuje w nim najczęściej.
 
; Rysunek 6
 
Spróbujmy opisać ten algorytm nieco bardziej precyzyjnie. Ponumerujmy procesory od 0 do n-1, a fazy od 0 do m przyjmując następujące definicje typów:
'''type'''
  Procesor = 0 .. n;
  Faza = 0 .. m;
Wartości początkowe <math>v_p</math> możemy traktować jak tablicę wartości pewnego typu indeksowaną procesorami:
  Wektor = '''array''' [Procesor] of Typ;
Umówmy się, że zbiory procesorów reprezentujemy za pomocą typu '''setof[Procesor]'''. Spróbujmy zapisać funkcję OMIC, która wylicza wartości <math>V_p</math>. Aby ustalić, jakie argumenty powinna mieć ta funkcja, zastanówmy się, co zmienia się w kolejnych wywołaniach funkcji OMIC. Oczywiście zmienia się numer fazy, ale także zestaw wartości początkowych oraz zbiór procesorów biorących udział w kolejnej fazie. Wynikiem algorytmu jest tablica <math>V</math> indeksowana procesorami, której elementami są wektory wartości.
Zatem OMIC możemy zapisać jako funkcję:
  '''function''' OMIC (f: Faza; v: Wektor; S: '''setof''' Procesor): '''array''' [Procesor] of Wektor
To, co poprzednio zapisawaliśmy, jako V_p(q) teraz zapiszemy więc, jako
OMIC (m, v, {}) (p) (q). Jest to wartość, jaką procesor p założy o wartości początkowej w q w wyniku wykonania algorytmu OMIC z m fazami, wektorem wartościami początkowymi v, i zbiorem s.
Aby sprecyzować treść funkcji OMIC przyjmijmy, że mamy daną funkcję wyboru większościowego '''maj''' (S: setof, v: Wektor) jest to wartość, która występuje najczęściej pośród wszystkich wartości tablicy v na pozycjach należących do zbioru S.
Załóżmy także, że działanie sieci jest symulowane za pomocą funkcji '''send''':
  send(f, t, q, p) jest to wartość jaką proces p otrzymuje w wyniku przesłania przez proces q wartości t w rundzie f. Oczywiście, jeśli procesory p i q są dobre, to mamy:
  send (f, t, q, p) = t
Ponadto przez distr (f, t, q) oznaczmy tablicę, która na pozycji p ma wartość, jaką procesor p otrzyma na skutek rozgłoszenia przez proces q wartości t. Możemy zapisać:
  distr (f, t, q) (p) = send (f, t, q, p).
 
Zdefiniujmy teraz formalnie OMIC (f, v, S) (p) (q) dla każdego p, q \in S:
 
OMIC (f, v, S) (p) (q) =
  '''if''' f = 0 '''then''' send (r, v(q), q, p)
  '''else'''
    '''if''' p = q '''then'''
      send (r, v(q), q, q)
    '''else'''
      maj (S-{q}, OMIC (f-1, distr (r, v(q), q), S-{q}) (p))
 
=== Poprawność algorytmu OMIC ===
 
Dowód poprawności algorytmu OMIC polega na pokazaniu następujących lematów pomocnuczych:
 
; Lemat
 
Przypuśćmy, że p oraz q są dobrymi procesorami należącymi do zbioru s.
Jeśli liczba dobrych procesorów w  zbiorze S przekracza liczbę procesorów wadliwych w S zwiększoną o numer fazy, to
            OMIC (f, v, s) (p) (q) = v (q)
 
; Wniosek
 
  OMIC (m, v, Full) (p) (q) = v (q)
 
; Lemat
Niech p, q, r będą procesorami należącymi do zbioru S. Ponadto przypuśćmy, że p, q są dobre, liczba procesorów w zbiorze s przekracza 3f, a liczba wadliwych procesorów w zbiorze S nie przekracza f. Wówczas:
      OMIC (f, v, s) (p) (r) =  
      OMIC (f, v, s) (q) (r)
 
; Wniosek
 
  OMIC (m, v, Full) (p) (r) =  
OMIC (f, v, s) (q) (r)

Aktualna wersja na dzień 16:26, 31 sie 2023

Algorytmy roproszone

Obecnie zajmiemy się algorytmami, które realizują podstawowe zadania w systemach rozproszonych.

Algorytm uzgadniania

Zadanie

Danych jest n procesorów. Każdy procesor może działać poprawnie lub być wadliwy. Wiadomo, że co najwyżej m procesorów jest wadliwych. Każdy procesor przechowuje pewną wartość. Oznaczmy wartość przechowywaną w procesorze p przez vp. Procesory porozumiewają się poprzez wymianę komunikatów, która odbywa się zgodnie z następującymi założeniami:

  • Każdy procesor może wysłać komunikat do każdego.
  • Komunikacja między dobrymi procesorami jest niezawodna.
  • Odbiorca zna nadawcę komunikatu.

Zaprojektować algorytm, który umożliwi każdemu procesorowi p ustalenie wektora wartości Vp takiego, że:

  1. Dla każdych dwóch dobrych procesorów p i q zachodzi: Vp(q)=vq.
  2. Dla każdych dwóch dobrych procesorów p iq oraz dowolnego procesora r zachodzi: Vp(r)=Vq(r)

Intuicyjnie wartością q współrzędnej wektora Vp(q) jest to, co procesor p przypuszcza o wartości vq. Powyższe dwa warunki wyrażają fakt, że w wyniku działania algorytmu, procesor dobry musi uzyskać dokładną wiedzę o wartości przechowywanej w każdym dobrym procesie. Warunek drugi oznacza, że wiedza procesorów dobrych musi być spójna: każdy procesor dobry musi wiedzieć to samo o każdym innym (także wadliwym) procesorze.

Wariant pierwszy. n=3,m=0

Gdy mamy 3 procesory i wszystkie są dobre to wystarczy, aby każdy przesłał każdemu swoją wartość. Jako Vp(q) procesor p przyjmuje to, co otrzymał od procesora q. Ponieważ komunikacja między dobrymi procesorami jest niezawodna, a wszystkie procesory są dobre, więc oczywiście zachodzi: Vp(q)=vq i oba warunki poprawności są spełnione.

Wariant drugi. n=4,m=1

Załóżmy teraz, że wśród 4 procesorów jeden jest wadliwy. Przeanalizujmy tę fazę algorytmu, w której ustala się wiedzę o procesorze wadliwym. Na wszystkich rysunkach w tym wykładzie procesory wadliwe będziemy przedstawiać w postaci kwadratów, a dobre w postaci okręgów. Przypuśćmy, że procesor wadliwy q wysyła do pozostałych trzech wartość vq. Czy procesory mogą przyjąć jako swoją wiedzę o q to, co od niego otrzymały? Oczywiście, nie! Ponieważ komunikacja między procesorem wadliwym nie musi być poprawna, więc dobre procesory mogą otrzymać różne wartości. Przyjęcie jako Vp(q) tego, co dotarło do procesu p od procesu q może sposowodować niespełnienie drugiego warunku poprawności:

Rysunek 1

Jest zatem potrzebna dodatkowa wiedza o procesie q. Jej źródłem jest dodatkowa wymiana informacji, którą procesory otrzymały od procesora q.

Usupełnijmy więc algorytm w następujący sposób. Aby ustalić wiedzę procesorów o procesorze q:

  1. Procesor q wysyła wartość vq do pozostałych procesorów. Oznaczmy wartość, którą w wyniku tej komunikacji otrzymał proces p przez vp
  2. Pozostałe procesory wymieniają między sobą wartości otrzymane od procesora q. W ten sposób każdy procesor dysponuje multizbiorem trzech wartości.
  3. Jako wartość Vp(q) przyjmuje się tę wartość z multizbioru wartości, który dotarły do procesora q, która występuje w niej największą liczbę razy. Tak sposób wyboru będziemy oznaczać wyborem większościowym i oznaczać przez maj.

Popatrzmy na przykład, w którym procesory ustalają swoją wiedzę na temat procesora dobrego:

Rysunek 2

Jednak w dalszym ciągu problematyczna jest sytuacja, w której ustalamy informacje o procesorze wadliwym. Może on wysłać do każdego procesora inną wartość:


Po wymianie informacji między procesorami dobrymi okazuje się, że dysponują one tym samym multizbiorem wartości. Nie ma w nim jednak wartości pojawiającej się najczęściej. W takiej sytuacji przyjmujemy, że wartością funkcji maj jest pewna z góry ustalona wartość. Zauważmy, że ponieważ wszystkie procesory mają ten sam multizbiór, więc wartości funkcji maj będą w nich takie same. Jest to zgodne z wymaganiem drugim. Oczywiście ta wartość nie musi być równa vq, ale pierwszy warunek poprawności wymaga równości jedynie w przypadku, gdy q jest dobry.

Wariant 3. n=3,m=1

Popatrzmy jeszcze na przypadek trzech procesorów wśród których jeden jest wadliwy. Popatrzmy na przypadek, w którym ustala się wartość przechowywaną w wadliwym procesorze.

Rysunek 3

Jeśli wadliwy procesor wyśle do pozostałych różne wartości, to będą one dysponowały multizbiorem {a,b}. Zatem procesory dobre zakładają, że w procesorze wadliwym jest pewna z góry ustalona wartość c. Zastanówmy się, czym powinno być c. W tym celu rozpatrzmy następującą wymianę komunikatów (tym razem prowadzącą do ustalenia wartości przechowywanej w dobrym procesorze):

I tym razem procesor 2 dysponuje multizbiorem {a,b}. Aby zapewnić warunek pierwszy poprawności należy zatem przyjąć c=a. Z drugiej jednak strony przy następującym scenariuszu:

Rysunek 4

należy przyjąć c=b. Otrzymana sprzeczność pokazuje, że nie da się rozwiązać problemu dla n=3,m=1.

Z powyższych rozważań wynika, że przedstawiony problem nie zawsze ma rozwiązanie. Warunkiem koniecznym i wystarczającym na to, aby rozwiązanie istniało jest zachowanie nierówności n>3m. W dalszych rozwiązaniach przyjmujemy, że taka nierówność zachodzi.

Algorytm OMIC

Przedstawimy teraz algorytm o nazwie Oral Messages Interchange Communication (w skrócie: OMIC), który rozwiązuje zadanie postawionie na wstępie przy założenu n>3m.

Algorytm OMIC jest rekurencyjny. Składa się on z m+1 faz. Jego wykonanie rozpoczyna się od fazy o numerze m a kończy na fazie o numerze 0. Opiszmy najpierw nieformalnie działanie tego algorytmu. W opisie przedstawimy sposób uzyskania informacji o jednym wyróżnionym procesie. Aby uzyskać informację o wszystkich procesach należy powtórzyć go wyróżniając po kolei każdy proces.

Niech OMIC (f) oznacza f-tą fazę algorytmu. Mamy:

  1. OMIC (0): proces q rozsyła swoją wartość vq do wszystkich pozostałych procesorów. Oznaczmy przez vp to, co proces p otrzymał od procesu q. Przyjmujemy Vp(q)=vp. Zauważmy, że w sytuacji, gdy m=0 ta jedna faza faktycznie wystarcza do zapewnienia obu warunków poprawności.
Rysunek 5
  1. OMIC(f): proces q rozsyła swoją wartość vq do wszystkich pozostałych procesorów. Oznaczmy przez vp to, co proces p otrzymał od procesu q. Ponieważ proces q mógł oszukiwać, więc tym razem nie można jako Vp(q) przyjąć vp, gdyż mogłoby to prowadzić do niespełnienia drugiego warunku poprawności. Tak naprawdę wszystkie procesy z wyjątkiem q powinny teraz uzgodnić wspólną wiedzę o tym, co otrzymały od q, czyli o wartościach primowanych. I to właśnie prowadzi do rekurencji. Wszystkie procesy z wyjątkiem q wykonują teraz bowiem algorytm OMIC(f-1). Każdy proces p biorący udział w tej fazie rozsyła jednak vp, a nie vp. Wykonanie OMIC(f-1) prowadzi oczywiście do obliczenia wartości Vp(r) w każdym procesie p biorącym udział w tej fazie. Zgodnie z intuicją

Vp(r) oznacza wiedze, jaką proces p przyjmuje o wartości początkowej z procesu r, czyli w tym wypadku to, co proces p zakłada o wartości vr, czyli jeszcze inaczej: to co proces p zakłada o wartości, którą proces r otrzymał od procesu q wysyłającego vq. Popatrzmy, jaką informacją dysponuje teraz proces p o procesie q:

  1. Po pierwsze ma wartości Vp(r) dla każdego rp,rq. Jest to informacja o vq otrzymana za pośrednictwem r.
  2. Po drugie: wartość vp, czyli informacja o vq uzyskana bezpośrednio od q. Oznaczmy tę wartość jako Vp(q).

Jako rozwiązanie, czyli Vp(q) przyjmuje się tę wartość z multizboru {Vp(r)|rq}, która występuje w nim najczęściej.

Rysunek 6

Spróbujmy opisać ten algorytm nieco bardziej precyzyjnie. Ponumerujmy procesory od 0 do n-1, a fazy od 0 do m przyjmując następujące definicje typów:

type
  Procesor = 0 .. n;
  Faza = 0 .. m;

Wartości początkowe vp możemy traktować jak tablicę wartości pewnego typu indeksowaną procesorami:

  Wektor = array [Procesor] of Typ;

Umówmy się, że zbiory procesorów reprezentujemy za pomocą typu setof[Procesor]. Spróbujmy zapisać funkcję OMIC, która wylicza wartości Vp. Aby ustalić, jakie argumenty powinna mieć ta funkcja, zastanówmy się, co zmienia się w kolejnych wywołaniach funkcji OMIC. Oczywiście zmienia się numer fazy, ale także zestaw wartości początkowych oraz zbiór procesorów biorących udział w kolejnej fazie. Wynikiem algorytmu jest tablica V indeksowana procesorami, której elementami są wektory wartości. Zatem OMIC możemy zapisać jako funkcję:

 function OMIC (f: Faza; v: Wektor; S: setof Procesor): array [Procesor] of Wektor

To, co poprzednio zapisawaliśmy, jako V_p(q) teraz zapiszemy więc, jako OMIC (m, v, {}) (p) (q). Jest to wartość, jaką procesor p założy o wartości początkowej w q w wyniku wykonania algorytmu OMIC z m fazami, wektorem wartościami początkowymi v, i zbiorem s. Aby sprecyzować treść funkcji OMIC przyjmijmy, że mamy daną funkcję wyboru większościowego maj (S: setof, v: Wektor) jest to wartość, która występuje najczęściej pośród wszystkich wartości tablicy v na pozycjach należących do zbioru S. Załóżmy także, że działanie sieci jest symulowane za pomocą funkcji send:

 send(f, t, q, p) jest to wartość jaką proces p otrzymuje w wyniku przesłania przez proces q wartości t w rundzie f. Oczywiście, jeśli procesory p i q są dobre, to mamy:
 send (f, t, q, p) = t 

Ponadto przez distr (f, t, q) oznaczmy tablicę, która na pozycji p ma wartość, jaką procesor p otrzyma na skutek rozgłoszenia przez proces q wartości t. Możemy zapisać:

 distr (f, t, q) (p) = send (f, t, q, p). 

Zdefiniujmy teraz formalnie OMIC (f, v, S) (p) (q) dla każdego p, q \in S:

OMIC (f, v, S) (p) (q) =  
  if f = 0 then send (r, v(q), q, p)
  else
    if p = q then 
      send (r, v(q), q, q)
    else 
      maj (S-{q}, OMIC (f-1, distr (r, v(q), q), S-{q}) (p))
 

Poprawność algorytmu OMIC

Dowód poprawności algorytmu OMIC polega na pokazaniu następujących lematów pomocnuczych:

Lemat

Przypuśćmy, że p oraz q są dobrymi procesorami należącymi do zbioru s. Jeśli liczba dobrych procesorów w zbiorze S przekracza liczbę procesorów wadliwych w S zwiększoną o numer fazy, to

            OMIC (f, v, s) (p) (q) = v (q) 
Wniosek
 OMIC (m, v, Full) (p) (q) = v (q)
Lemat

Niech p, q, r będą procesorami należącymi do zbioru S. Ponadto przypuśćmy, że p, q są dobre, liczba procesorów w zbiorze s przekracza 3f, a liczba wadliwych procesorów w zbiorze S nie przekracza f. Wówczas:

      OMIC (f, v, s) (p) (r) = 
      OMIC (f, v, s) (q) (r)
Wniosek
 OMIC (m, v, Full) (p) (r) = 
OMIC (f, v, s) (q) (r)