Zaawansowane algorytmy i struktury danych/Wykład 13: Różnice pomiędzy wersjami
Nie podano opisu zmian |
Nie podano opisu zmian |
||
Linia 4: | Linia 4: | ||
W module tym | W module tym zajmiemy się przyspieszaniem obliczeń za pomocą korzystania z wielu procesorów (maszyn) działających równolegle. Niestety nie ma ogólnie przyjętego modelu obliczeń równoległych. Rozważymy w tym module dwa modele: maszynę PRAM i układy arytmetyczne (logiczne). O ile maszyna PRAM jest modelem wysoko-poziomowym, to układy arytmetyczne są modelem niskopoziomowym, ale niewątpliwie bardzo istotnym niskopoziomowym, ale | ||
niewątpliwie bardzo istotnym. | niewątpliwie bardzo istotnym. | ||
Linia 13: | Linia 13: | ||
<center>[[Grafika:Parallel1-1.png]]<br>Rysunek 1: Struktura koncepcyjna PRAMu </center> | <center>[[Grafika:Parallel1-1.png]]<br>Rysunek 1: Struktura koncepcyjna PRAMu </center> | ||
Maszyna | Maszyna PRAM składa się z wielu procesorów pracujących synchronicznie, korzystących ze wspólnej pamięci (która oprócz przechowywania danych służy do komunikacji między procesorami). Każdy procesor jest standardowym komputerem typu RAM (ang. ''Random Access Machine''). Zakładamy, że procesory są ponumerowane liczbami naturalnymi. Procesory wykonują jeden wspólny program, ale wykonanie poszczególnych instrukcji zależy od indeksu procesora. W jednym kroku procesor pobiera dane z pamięci, potem wykonuje operację, którą może być wpisanie pewnych danych. Wszystkie procesory wykonują jeden krok jednocześnie. Równoległość jest wyrażona poprzez następującą instrukcję: | ||
<center> <math> \textbf{for all } i \in X \textbf{do in parallel}\ akcja (i)</math>.</center> | <center> <math> \textbf{for all } i \in X \textbf{do in parallel}\ akcja (i)</math>.</center> | ||
Linia 28: | Linia 28: | ||
E (od ang. exclusive) wyklucza taką możliwość. Operacjami są R (czytanie, od ang. read) oraz W (zapis, od ang. write) | E (od ang. exclusive) wyklucza taką możliwość. Operacjami są R (czytanie, od ang. read) oraz W (zapis, od ang. write) | ||
w tej samej komórce przez wiele procesorów w tym samym momencie. | w tej samej komórce przez wiele procesorów w tym samym momencie. | ||
Mamy zatem EREW PRAM, CREW PRAM, CRCW PRAM (modelu ERCW nie rozważamy jako zupełnie | Mamy zatem EREW PRAM, CREW PRAM, CRCW PRAM (modelu ERCW nie rozważamy jako zupełnie sztucznego). | ||
Podstawowym naszym modelem | Podstawowym naszym modelem PRAM-u będzie CREW PRAM: wiele procesrów może jednocześnie czytać z tej samej komórki, ale tylko jeden może zapisywać. | ||
Linia 100: | Linia 100: | ||
== Klasa NC i równoległe algorytmy optymalne == | == Klasa NC i równoległe algorytmy optymalne == | ||
Najważniejszą klasą problemów algorytmicznych stanowią problemy które można obliczyć w czasie wielomianowo-logarytmicznym używając wielomianowej liczby procesorów. Klasę tę oznaczamy przez NC, odpowiadające algorytmy nazywamy algorytmami typu NC. | Najważniejszą klasą problemów algorytmicznych stanowią problemy, które można obliczyć w czasie wielomianowo-logarytmicznym, używając wielomianowej liczby procesorów. Klasę tę oznaczamy przez NC, odpowiadające algorytmy nazywamy algorytmami typu NC. | ||
Niech <math>\cal{P}</math> oznacza klasę problemów wykonywanych w deterministycznym czasie wielomianowym na maszynie | Niech <math>\cal{P}</math> oznacza klasę problemów wykonywanych w deterministycznym czasie wielomianowym na maszynie | ||
Linia 109: | Linia 109: | ||
Podobnie jak problemy NP-zupełne można zdefiniować | Podobnie jak problemy NP-zupełne można zdefiniować problemy P-zupełne. Są to te problemy <math>X \in \cal{P}</math>, takie | ||
że dla każdego innego problemu <math>Y \in \cal{P}</math> istnieje NC-redukcja Y do X. | że dla każdego innego problemu <math>Y \in \cal{P}</math> istnieje NC-redukcja Y do X. | ||
Inaczej mówiąc | Inaczej mówiąc | ||
Linia 121: | Linia 121: | ||
sprawdzanie czy gramatyka bezkontekstowa generuje język pusty. | sprawdzanie czy gramatyka bezkontekstowa generuje język pusty. | ||
Przez pracę algorytmu równoległego rozumiemy liczbę procesorów pomnożoną przez czas. Algorytm jest optymalny gdy jego praca jest tego samego rzędu co czas najlepszego znanego algorytmu sekwencyjnego dla danego problemu. | Przez pracę algorytmu równoległego rozumiemy liczbę procesorów pomnożoną przez czas. Algorytm jest optymalny, gdy jego praca jest tego samego rzędu co czas najlepszego znanego algorytmu sekwencyjnego dla danego problemu. | ||
W szczególności interesują nas algorytmy, które są jednocześnie optymalne i są typu NC. Z praktycznego punku widzenia czynnik <math>log n</math> przy liczbie procesorów i przy pracy algorytmu nie jest zbyt istotny. Natomiast czynnik logarytmiczny jest istotny jeśli chodzi o równoległy czas. W tym przypadku potęga logarytmu odgrywa podobną rolę co potęga wielomianu opisującego czas obliczenia sekwencyjnego. Różnica między CRCW i CREW PRAMem w aspekcie klasy NC polega przeważnie na dodaniu jednego czynnika logarytmicznego w funkcji równoległego czasu obliczenia. | W szczególności interesują nas algorytmy, które są jednocześnie optymalne i są typu NC. Z praktycznego punku widzenia czynnik <math>log n</math> przy liczbie procesorów i przy pracy algorytmu nie jest zbyt istotny. Natomiast czynnik logarytmiczny jest istotny, jeśli chodzi o równoległy czas. W tym przypadku potęga logarytmu odgrywa podobną rolę co potęga wielomianu opisującego czas obliczenia sekwencyjnego. Różnica między CRCW i CREW PRAMem w aspekcie klasy NC polega przeważnie na dodaniu jednego czynnika logarytmicznego w funkcji równoległego czasu obliczenia. | ||
== Przykłady obliczeń na modelu CRCW PRAM == | == Przykłady obliczeń na modelu CRCW PRAM == | ||
W przypadku CRCW PRAM założymy, że procesory, jeśli wpisują jednocześnie do tej samej komórki pamięci, to | W przypadku CRCW PRAM założymy, że procesory, jeśli wpisują jednocześnie do tej samej komórki pamięci, to | ||
wpisują to samo. Na przykład jeśli początkowo <math>output=0</math> wówczas następujący algorytm obliczy logiczną | wpisują to samo. Na przykład jeśli początkowo <math>output=0</math>, wówczas następujący algorytm obliczy logiczną | ||
alternatywę w czasie stałym na CRCW PRAM. | alternatywę w czasie stałym na CRCW PRAM. | ||
Linia 137: | Linia 137: | ||
Na CREW potrzebujemy logarytmicznego czasu równoległego aby to zrobić. Pokażemy jeszcze dwa proste problemy, które można na CRCW PRAM wykonać w czasie stałym. Następujący algorytm liczy pierwszą pozycję minimalnego elementu w tablicy C[1 . . n] w czasie O(1). | Na CREW potrzebujemy logarytmicznego czasu równoległego, aby to zrobić. Pokażemy jeszcze dwa proste problemy, które można na CRCW PRAM wykonać w czasie stałym. Następujący algorytm liczy pierwszą pozycję minimalnego elementu w tablicy C[1 . . n] w czasie O(1). | ||
{{algorytm||| | {{algorytm||| | ||
Linia 150: | Linia 150: | ||
}} | }} | ||
Oznaczmy ten algorytm przez <math>A_1</math>. Algorytm korzysta z <math>n^2</math> procesorów. Spróbujemy zmniejszyć tę liczbę do <math>O(n^{1+\epsilon})</math> zachowując czas <math>O(1)</math> | Oznaczmy ten algorytm przez <math>A_1</math>. Algorytm korzysta z <math>n^2</math> procesorów. Spróbujemy zmniejszyć tę liczbę do <math>O(n^{1+\epsilon})</math>, zachowując czas <math>O(1)</math> dla dowolnie małego <math>\epsilon >0</math>. | ||
Niech | Niech | ||
Linia 157: | Linia 157: | ||
Przypuśćmy, że mamy algorytm <math>A_k</math> liczenia minimum w czasie <math>O(1)</math> z <math>O(P_k(n))</math> procesorami. Skonstruujemy algorytm <math>A_{k+1}</math> który działa w czasie stałym i używa tylko | Przypuśćmy, że mamy algorytm <math>A_k</math> liczenia minimum w czasie <math>O(1)</math> z <math>O(P_k(n))</math> procesorami. Skonstruujemy algorytm <math>A_{k+1}</math>, który działa w czasie stałym i używa tylko <math>O(P_{k+1}(n)) </math> procesorów. | ||
Linia 172: | Linia 172: | ||
Algorytm <math>A_{k+1}</math> działa w czasie <math>O(1)</math>, korzystając z <math>P_{k+1}(n))</math> procesorów. Uzasadnienie pozostawiamy jako ćwiczenie. | |||
Algorytmy <math>A_2,\ A_3,\ A_4, \ldots</math> używają odpowiednio następującą (asymptotycznie) liczbę procesorów | Algorytmy <math>A_2,\ A_3,\ A_4, \ldots</math> używają odpowiednio następującą (asymptotycznie) liczbę procesorów | ||
Linia 180: | Linia 180: | ||
Rozważmy jeszcze na CRCW PRAM następujący '''problem pierwszej jedynki''': dana jest tablica zerojedynkowa, znaleźć pozycję pierwszej jedynki (od lewej). | Rozważmy jeszcze na CRCW PRAM następujący '''problem pierwszej jedynki''': dana jest tablica zerojedynkowa, znaleźć pozycję pierwszej jedynki (od lewej). | ||
Następujący algorytm rozwiązuje problem w czasie stałym z kwadratową liczbą procesorów. Zakładamy na razie, że w ciągu jest | Następujący algorytm rozwiązuje problem w czasie stałym z kwadratową liczbą procesorów. Zakładamy na razie, że w ciągu jest jakaś jedynka. | ||
{{algorytm|Pierwsza-Jedynka-1|| | {{algorytm|Pierwsza-Jedynka-1|| | ||
Linia 194: | Linia 194: | ||
Możemy podobnie łatwo sprawdzić czy w ogóle jest jedynka. | Możemy podobnie łatwo sprawdzić, czy w ogóle jest jedynka. | ||
{{algorytm|CzyJestJedynka|| | {{algorytm|CzyJestJedynka|| | ||
Linia 221: | Linia 221: | ||
}} | }} | ||
W ten sposób stosujemy trzy razy algorytm o pracy kwadratowej do segmentu długości <math>\sqrt{n}</math> | W ten sposób stosujemy trzy razy algorytm o pracy kwadratowej do segmentu długości <math>\sqrt{n}</math>. Otrzymujemy złożoność <math>O(\sqrt{n}^2) = O(n)</math>. Czas jest <math>O(1)</math>. | ||
== Drzewa i | == Drzewa i równoległa wersja metody dziel i zwyciężaj == | ||
Do szybkich obliczeń równoległych najbardziej nadają się problemy związane z drzewami, chociaż czasami w tych problemach nie widać od razu struktury drzewiastej. Struktura taka odpowiada również drzewu rekursji. Jako przykład rozważmy problem obliczenia sumy <math>A[1]+A[2]+\ldots A[n]</math>. Dla uproszczenia załóżmy, że n jest potęgą dwójki. | Do szybkich obliczeń równoległych najbardziej nadają się problemy związane z drzewami, chociaż czasami w tych problemach nie widać od razu struktury drzewiastej. Struktura taka odpowiada również drzewu rekursji. Jako przykład rozważmy problem obliczenia sumy <math>A[1]+A[2]+\ldots A[n]</math>. Dla uproszczenia załóżmy, że n jest potęgą dwójki. | ||
Linia 258: | Linia 258: | ||
Drzewa odpowiadają w pewnym sensie rekursji. Wysokość drzew odpowiada czasowi równoległemu. Podobnie głębokość rekursji odpowiada | Drzewa odpowiadają w pewnym sensie rekursji. Wysokość drzew odpowiada czasowi równoległemu. Podobnie głębokość rekursji odpowiada czasowi równoległemu. Równoległa wersja tzw. metody dziel i zwyciężaj polega na tym, że wywołania rekurencyjne wykonujemy jednocześnie (patrz rysunek). Oczywiście może się zdarzyć, że jedno z nich zakończy się wcześniej. Niemniej algorytm czeka na zakończenie obu wywołań. | ||
Linia 277: | Linia 277: | ||
Podobny jest schemat sortowania na PRAMie. Niech <math>ParallelMerge(x)</math> będzie algorytmem który, otrzymawszy tablicę <math>x</math> z posortowanymi lewą i prawą połową da wyniku tablicę <math>x</math> posortowaną. | Podobny jest schemat sortowania na PRAMie. Niech <math>ParallelMerge(x)</math> będzie algorytmem, który, otrzymawszy tablicę <math>x</math> z posortowanymi lewą i prawą połową, da w wyniku tablicę <math>x</math> posortowaną. Łatwo to zrobić w czasie <math>O(\log n)</math> z <math>n</math> | ||
procesorami. Dla <math>i>n/2</math>-ty procesor znajduje w pierwszej połówce sekwencyjnie metodą ''binary search'' | procesorami. Dla <math>i>n/2</math>-ty procesor znajduje w pierwszej połówce sekwencyjnie, metodą ''binary search'', | ||
najmniejszy element większy od <math>x[i]</math>. Wymaga to czasu <math>O(\log n)</math>. Potem każdy procesor{\em wie} gdzie wstawić {\em swój} element. W sumie otrzymujemy algorytm sortowania w czasie <math>O(\log^2)</math> z <math>n</math> procesorami. | najmniejszy element większy od <math>x[i]</math>. Wymaga to czasu <math>O(\log n)</math>. Potem każdy procesor{\em wie} gdzie wstawić {\em swój} element. W sumie otrzymujemy algorytm sortowania w czasie <math>O(\log^2)</math> z <math>n</math> procesorami. | ||
Linia 298: | Linia 298: | ||
Liczbę procesorów można | Liczbę procesorów można zmniejszyć do <math>n/(\log n))</math>. Natomiast nietrywialnym jest zmniejszenie czasu na CREW PRAM. Zostało to zrobione przez Richarda Cole'a, który skonstruował algorytm działający w czasie <math>O(\log n)</math> z <math>O(n)</math> procesorami maszyny EREW PRAM. Algorytm ten jest bardzo interesujący, ale skomplikowany. | ||
== Układy arytmetyczne: sumy prefiksowe == | == Układy arytmetyczne: sumy prefiksowe == | ||
Być może najbardziej podstawowym modelem obliczeń równoległych są układy arytmetyczne (lub logiczne): | Być może najbardziej podstawowym modelem obliczeń równoległych są układy arytmetyczne (lub logiczne): acykliczne grafy z przypisaniem pewnych operacji węzłom wewnętrznym. Każdy węzeł liczy pewną wartość w momencie, gdy wartości jego poprzedników są policzone. Podobnie jak w drzewie, możemy zdefiniować pojęcie liścia: węzeł bez poprzedników. Natomiast graf nie musi mieć jednego korzenia. Zamiast korzenia w grafie wyróżniamy węzły wynikowe (na rysunku te, z których wychodzi strzałka donikąd). | ||
Równoległy czas obliczenia odpowiada maksymalnej wysokości węzła. Poziomy definiujemy podobnie jak dla drzewa. Algorytm równoległy w jednym równoległym kroku oblicza kolejny poziom. Liczba procesorów odpowiada z reguły maksymalnemu rozmiarowi poziomu, chociaż możemy inaczej rozplanować obliczenie gdy jedne poziomy są duże, a drugie małe (ale możemy wtedy zmienić strukturę grafu tak aby temu odpowiadała). | Równoległy czas obliczenia odpowiada maksymalnej wysokości węzła. Poziomy definiujemy podobnie jak dla drzewa. Algorytm równoległy w jednym równoległym kroku oblicza kolejny poziom. Liczba procesorów odpowiada z reguły maksymalnemu rozmiarowi poziomu, chociaż możemy inaczej rozplanować obliczenie, gdy jedne poziomy są duże, a drugie małe (ale możemy wtedy zmienić strukturę grafu tak, aby temu odpowiadała). | ||
Przykładem układu arytmetycznego jest drzewo z rysunku powyżej, które opisuje sumowanie n elementów. Zajmiemy się teraz pewnym rozszerzeniem problemu sumowania. Niech <math>\oplus</math> będzie pewną łączną operacją arytmetyczną (np. suma, mnożenie, maksimum, minimum, pozycja pierwszej jedynki z lewej strony, podobnie z prawej strony). | Przykładem układu arytmetycznego jest drzewo z rysunku powyżej, które opisuje sumowanie n elementów. Zajmiemy się teraz pewnym rozszerzeniem problemu sumowania. Niech <math>\oplus</math> będzie pewną łączną operacją arytmetyczną (np. suma, mnożenie, maksimum, minimum, pozycja pierwszej jedynki z lewej strony, podobnie z prawej strony). | ||
Linia 368: | Linia 368: | ||
<center> [[Grafika:Parallel1-5.png]] <br> Rysunek 5. Układ arytmetyczny odpowiadający PrefSums2. Kolejny graf składa się z pojedynczej kopii | <center> [[Grafika:Parallel1-5.png]] <br> Rysunek 5. Układ arytmetyczny odpowiadający PrefSums2. Kolejny graf składa się z pojedynczej kopii | ||
poprzedniego grafu (dla <math>n/2</math>) | poprzedniego grafu (dla <math>n/2</math>) oraz <math>n/2-1</math> dodatkowych operacji. </center> |
Wersja z 14:47, 25 wrz 2006
Algorytmy równoległe I
W module tym zajmiemy się przyspieszaniem obliczeń za pomocą korzystania z wielu procesorów (maszyn) działających równolegle. Niestety nie ma ogólnie przyjętego modelu obliczeń równoległych. Rozważymy w tym module dwa modele: maszynę PRAM i układy arytmetyczne (logiczne). O ile maszyna PRAM jest modelem wysoko-poziomowym, to układy arytmetyczne są modelem niskopoziomowym, ale niewątpliwie bardzo istotnym niskopoziomowym, ale
niewątpliwie bardzo istotnym.
Model równoległej abstrakcyjnej Maszyny PRAM
Na początku rozważymy wyidealizowany model obleczeń równoległych zwany Równoległą Maszyną ze Swobodnym Dostępem do Pamięci, w skrócie PRAM (od ang. Parallel Random Access Machine, wymawiany piram).

Rysunek 1: Struktura koncepcyjna PRAMu
Maszyna PRAM składa się z wielu procesorów pracujących synchronicznie, korzystących ze wspólnej pamięci (która oprócz przechowywania danych służy do komunikacji między procesorami). Każdy procesor jest standardowym komputerem typu RAM (ang. Random Access Machine). Zakładamy, że procesory są ponumerowane liczbami naturalnymi. Procesory wykonują jeden wspólny program, ale wykonanie poszczególnych instrukcji zależy od indeksu procesora. W jednym kroku procesor pobiera dane z pamięci, potem wykonuje operację, którą może być wpisanie pewnych danych. Wszystkie procesory wykonują jeden krok jednocześnie. Równoległość jest wyrażona poprzez następującą instrukcję:
Wykonanie tej instrukcji polega na wykonaniu dwóch równoległych operacji:
- przydzielenie procesora do każdego elementu ze zbioru ,
- jednoczesne wykonanie przez każdy procesor operacji akcja.
Przeważnie zapis jest w rodzaju `` jeśli jest zbiorem liczb naturalnych.
Podstawowe typy maszyny PRAM
Mamy kilka rodzajów maszyny PRAM w zależności od konfliktów czytania/zapisu we wspólnej pamięci. Litera C (od ang. concurrent) oznacza możliwość jednoczesnego wykonania operacji przez wiele procesorów, E (od ang. exclusive) wyklucza taką możliwość. Operacjami są R (czytanie, od ang. read) oraz W (zapis, od ang. write) w tej samej komórce przez wiele procesorów w tym samym momencie. Mamy zatem EREW PRAM, CREW PRAM, CRCW PRAM (modelu ERCW nie rozważamy jako zupełnie sztucznego).
Podstawowym naszym modelem PRAM-u będzie CREW PRAM: wiele procesrów może jednocześnie czytać z tej samej komórki, ale tylko jeden może zapisywać.
Prostym przykładem obliczenia na CREW jest liczenie kolejnych wierszy trójkąta Pasacala. Początkowo zakładamy, że . Wykonujemy:
repeat 6 times
for each do in parallel
Kolejnymi wartościami tablicy A są wektory:
0 | 0 | 0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 1 | 1 |
0 | 0 | 0 | 1 | 2 | 1 |
0 | 0 | 1 | 3 | 3 | 1 |
0 | 1 | 4 | 6 | 4 | 1 |
1 | 5 | 10 | 10 | 5 | 1 |
Klasa NC i równoległe algorytmy optymalne
Najważniejszą klasą problemów algorytmicznych stanowią problemy, które można obliczyć w czasie wielomianowo-logarytmicznym, używając wielomianowej liczby procesorów. Klasę tę oznaczamy przez NC, odpowiadające algorytmy nazywamy algorytmami typu NC.
Niech oznacza klasę problemów wykonywanych w deterministycznym czasie wielomianowym na maszynie sekwencyjnej. Podstawowym problemem teoretycznym algorytmiki równoległej jest pytanie:
Podobnie jak problemy NP-zupełne można zdefiniować problemy P-zupełne. Są to te problemy , takie że dla każdego innego problemu istnieje NC-redukcja Y do X. Inaczej mówiąc
Przykłady problemów P-zupełnych: programowanie liniowe, maksymalny przepływ w grafie, konstrukcja drzewa DFS, obliczanie wartości układów logicznych, sprawdzanie czy gramatyka bezkontekstowa generuje język pusty.
Przez pracę algorytmu równoległego rozumiemy liczbę procesorów pomnożoną przez czas. Algorytm jest optymalny, gdy jego praca jest tego samego rzędu co czas najlepszego znanego algorytmu sekwencyjnego dla danego problemu.
W szczególności interesują nas algorytmy, które są jednocześnie optymalne i są typu NC. Z praktycznego punku widzenia czynnik przy liczbie procesorów i przy pracy algorytmu nie jest zbyt istotny. Natomiast czynnik logarytmiczny jest istotny, jeśli chodzi o równoległy czas. W tym przypadku potęga logarytmu odgrywa podobną rolę co potęga wielomianu opisującego czas obliczenia sekwencyjnego. Różnica między CRCW i CREW PRAMem w aspekcie klasy NC polega przeważnie na dodaniu jednego czynnika logarytmicznego w funkcji równoległego czasu obliczenia.
Przykłady obliczeń na modelu CRCW PRAM
W przypadku CRCW PRAM założymy, że procesory, jeśli wpisują jednocześnie do tej samej komórki pamięci, to wpisują to samo. Na przykład jeśli początkowo , wówczas następujący algorytm obliczy logiczną alternatywę w czasie stałym na CRCW PRAM.
for each do in parallel
if then output=1;
Na CREW potrzebujemy logarytmicznego czasu równoległego, aby to zrobić. Pokażemy jeszcze dwa proste problemy, które można na CRCW PRAM wykonać w czasie stałym. Następujący algorytm liczy pierwszą pozycję minimalnego elementu w tablicy C[1 . . n] w czasie O(1).
Algorytm
for each do in parallel
;
for each do in parallel
if and then ;
for each do in parallel
if then ;
Oznaczmy ten algorytm przez . Algorytm korzysta z procesorów. Spróbujemy zmniejszyć tę liczbę do , zachowując czas dla dowolnie małego .
Niech
Przypuśćmy, że mamy algorytm liczenia minimum w czasie z procesorami. Skonstruujemy algorytm , który działa w czasie stałym i używa tylko procesorów.
Algorytm
niech ;
podziel tablicę C na rozłączne bloki rozmiaru każdy;
równolegle zastosuj algorytm do każdego z tych bloków;
zastosuj algorytm do tablicy C' składającej się z minimów w blokach.
Algorytm działa w czasie , korzystając z procesorów. Uzasadnienie pozostawiamy jako ćwiczenie.
Algorytmy używają odpowiednio następującą (asymptotycznie) liczbę procesorów
Rozważmy jeszcze na CRCW PRAM następujący problem pierwszej jedynki: dana jest tablica zerojedynkowa, znaleźć pozycję pierwszej jedynki (od lewej).
Następujący algorytm rozwiązuje problem w czasie stałym z kwadratową liczbą procesorów. Zakładamy na razie, że w ciągu jest jakaś jedynka.
Algorytm Pierwsza-Jedynka-1
for each do in parallel
if A[i]1 and A[j]1 then A[j]0;
for each do in parallel
if A[i]1 then FirstOne i.
Możemy podobnie łatwo sprawdzić, czy w ogóle jest jedynka.
Algorytm CzyJestJedynka
jest-jedynka 0;
for each do in parallel
if A[i]1 then jest-jedynka 1;
Oba powyższe algorytmy korzystają z procesorów. Możemy łatwo tę liczbę zmniejszyć do liniowej.
Algorytm Pierwsza-Jedynka
(1) Podziel tablicę A na segmenty długośći ;
(2) W każdym segmencie zastosuj algorytm CzyJestJedynka;
(3) Otrzymujemy ciąg zerojedynkowy długości jako wynik kroku (2);
(4) znajdź pierwszą jedynkę w ciągu C za pomocą algorytmu Pierwsza-Jedynka-1;
(5) Zastosuj algorytm Pierwsza-Jedynka-1 do segmentu odpowiadającego pierwszej jedynce w C;
W ten sposób stosujemy trzy razy algorytm o pracy kwadratowej do segmentu długości . Otrzymujemy złożoność . Czas jest .
Drzewa i równoległa wersja metody dziel i zwyciężaj
Do szybkich obliczeń równoległych najbardziej nadają się problemy związane z drzewami, chociaż czasami w tych problemach nie widać od razu struktury drzewiastej. Struktura taka odpowiada również drzewu rekursji. Jako przykład rozważmy problem obliczenia sumy . Dla uproszczenia załóżmy, że n jest potęgą dwójki.

Rysunek 2: Metoda pełnego zrównoważonego drzewa binarnego: układ arytmetyczny obliczania sumy. Maksymalny poziom .
Wysokością węzła jest jego maksymalna odległość od liścia, wysokość liścia wynosi 0. Przez -ty poziom rozumiemy zbiór węzłów o wysokości . Załóżmy, że elementy są umieszczone w liściach pełnego zrównoważonego drzewa binarnego, następnie wykonujemy (patrz rysunek):
for to do
oblicz jednocześnie wartości węzłów na poziomie -tym;
Drzewo jest strukturą koncepcyjną, każdemu węzłowi możemy przypisać miejsce w pamięci. W naszym przypadku węzły na poziomie -tym mogą odpowiadać elementom
Poprzedni algorytm można zapisać w formie:
for to do
;
for each do in parallel
;
wynik := ;

Rysunek 3. Koncepcyjna struktura równoległej wersji metody dziel i zwyciężaj.
Drzewa odpowiadają w pewnym sensie rekursji. Wysokość drzew odpowiada czasowi równoległemu. Podobnie głębokość rekursji odpowiada czasowi równoległemu. Równoległa wersja tzw. metody dziel i zwyciężaj polega na tym, że wywołania rekurencyjne wykonujemy jednocześnie (patrz rysunek). Oczywiście może się zdarzyć, że jedno z nich zakończy się wcześniej. Niemniej algorytm czeka na zakończenie obu wywołań.
Pokażemy dwa przykłady zastosowania metody dziel i zwyciężaj w wersji równoległej. Zaczniemy od sumy elementów tablicy .
Wynik obliczamy jako . Zakładamy znowu, że jest potęgą dwójki.
funkcja
if then return else
do in parallel
wynik1 ;
wynik2 ;
return wynik1 + wynik2;
Podobny jest schemat sortowania na PRAMie. Niech będzie algorytmem, który, otrzymawszy tablicę z posortowanymi lewą i prawą połową, da w wyniku tablicę posortowaną. Łatwo to zrobić w czasie z
procesorami. Dla -ty procesor znajduje w pierwszej połówce sekwencyjnie, metodą binary search,
najmniejszy element większy od . Wymaga to czasu . Potem każdy procesor{\em wie} gdzie wstawić {\em swój} element. W sumie otrzymujemy algorytm sortowania w czasie z procesorami.
funkcja ;
;
if then
do in parallel
;
;
Liczbę procesorów można zmniejszyć do . Natomiast nietrywialnym jest zmniejszenie czasu na CREW PRAM. Zostało to zrobione przez Richarda Cole'a, który skonstruował algorytm działający w czasie z procesorami maszyny EREW PRAM. Algorytm ten jest bardzo interesujący, ale skomplikowany.
Układy arytmetyczne: sumy prefiksowe
Być może najbardziej podstawowym modelem obliczeń równoległych są układy arytmetyczne (lub logiczne): acykliczne grafy z przypisaniem pewnych operacji węzłom wewnętrznym. Każdy węzeł liczy pewną wartość w momencie, gdy wartości jego poprzedników są policzone. Podobnie jak w drzewie, możemy zdefiniować pojęcie liścia: węzeł bez poprzedników. Natomiast graf nie musi mieć jednego korzenia. Zamiast korzenia w grafie wyróżniamy węzły wynikowe (na rysunku te, z których wychodzi strzałka donikąd).
Równoległy czas obliczenia odpowiada maksymalnej wysokości węzła. Poziomy definiujemy podobnie jak dla drzewa. Algorytm równoległy w jednym równoległym kroku oblicza kolejny poziom. Liczba procesorów odpowiada z reguły maksymalnemu rozmiarowi poziomu, chociaż możemy inaczej rozplanować obliczenie, gdy jedne poziomy są duże, a drugie małe (ale możemy wtedy zmienić strukturę grafu tak, aby temu odpowiadała).
Przykładem układu arytmetycznego jest drzewo z rysunku powyżej, które opisuje sumowanie n elementów. Zajmiemy się teraz pewnym rozszerzeniem problemu sumowania. Niech będzie pewną łączną operacją arytmetyczną (np. suma, mnożenie, maksimum, minimum, pozycja pierwszej jedynki z lewej strony, podobnie z prawej strony).
Problem sum prefiksowych. dany wektor rozmiaru , obliczyć ciąg taki, gdzie
gdzie
Opiszemy dwa rekurencyjne algorytmy dla tego problemu. Niech , oznaczają lewą
i prawą (odpowiednio) połówkę ciągu. Zakładamy, że n jest potęgą dwójki.
Algorytm ;
;
if then
do in parallel
;
;
for each , do in parallel
;
Układ arytmetyczny odpowiadający algorytmowi PrefSums1 jest przedstawiony na Rysunku 4 dla i . Zauważmy, że zasadniczą częścią układu dla są dwie kopie układu dla . Dodatkowo dodajemy węzły odpowiadającej ostatniej instrukcji w algorytmie PrefSums1.

Rysunek 4. Układ arytmetyczny odpowiadający algorytmowi PrefSums1. Kolejne grafy powstają jako podwójne kopie porzednich grafów (dla elementów) z dodanymi elementami odpowiadającymi operacji .
Opiszemy teraz inny algorytm rekurencyjny, w którym mamy tylko jedno wywołanie rekurecyjne (w jednej instancji rekursji).
Algorytm
;
if then
utwórz nową tablicę y;
for each do in parallel
;
;
for each do in parallel
; if then ;
Układ arytmetyczny odpowiadający algorytmowi jest pokazany na rysunku 5.

Rysunek 5. Układ arytmetyczny odpowiadający PrefSums2. Kolejny graf składa się z pojedynczej kopii poprzedniego grafu (dla ) oraz dodatkowych operacji.