# Preparata Algorithm

**Preparata’s algorithm** is a recursive Divide and Conquer Algorithm where the rank of each input key is computed and the keys are outputted according to their ranks.

## C++

`m[i, j] := M[i, j]` `for` `1 <= i, j <= n in parallel;` ` ` `for` ` ` `r : = 1 to logn ` `do` ` ` `{` ` ` `Step 1. In parallel set q[i, j, k] := m[i, j] + m[j, k] ` `for` ` ` `1 <= i, j, k <= n.` ` ` ` ` `Step 2. In parallel set m[i, j] := min` ` ` `{` ` ` `q[i, l, j], q[i, 2, j], ..., q[i, n, j]` ` ` `}` ` ` `for` ` ` `1 <= i, j <= n.` ` ` `}` `Put M(i)(i):=0 ` `for` `all i and M(i)(j):=m[i, j] ` `for` `i≠j` |

In the above procedure **O(N ^{3}) **global memory is used by using the variables

**m[i, j]**for

**1 ≤ i, j ≤ N**and

**q[i, j, k]**for

**1 ≤ i, j, k ≤ N**.

- Initializing m[ ] that takes O(N
^{2}) time. - Step 1 of the above algorithm takes O(1) time while using 3 processors.
- In Step 2, N
^{2}number of different**m[i, j]’s**are computed.

The computation of a single **m[z, j]** involves computing a minimum of **N** numbers and hence can be completed in * O(1) time* using

**N**. In fact, this minimum can also be computed in

^{2}CRCW PRAM processors*using*

**O(1) time****n**processors for any fixed

^{(1 + e)}**e > 0**.

**Step 2** can be completed in **O(1) time** using **n ^{(3 + e)}** common

**CRCW PRAM processors**. Thus, the for loop runs in

*time. The final computation of*

**O(log N)****M**also can be done in O(1) time using

**N**processors.

^{2}The correctness of the above algorithm can be proven by induction on **R**. It can be shown that the value of **m[i, j]** at the end of the **r ^{th} iteration** of the for loop is min, and the minimum is taken over all the sequences of elements of

**{1, 2, …, N}**such that

**k < 2R**. The above algorithm can be specialized to solve several problems including the transitive closure, connected components, minimum spanning tree, and so on.

Let k1, k2, …, kn be the input sequence. Preparata’s algorithm partitions the input into **log N parts** K1, K2, …, if log N; where there are N/log N keys in each part.

If k is any key in the input, its rank in the input is computed as follows. First, the rank **Ri** of **k** is computed for each **i**, **1< i < log N**. Then, the total rank of k is computed as . One of the results that make the use of the above algorithm.

The details of Preparata’s algorithm are given below.

Consider **T(N)** to be the run time of Preparata’s algorithm using **N*log N** processors. Clearly, step 1 takes * T(N/log N) time* and steps 2 and 3 together take

*. Thus, there is*

**O(log(log N)) time**T(n) = T(N/log N) + O(log(log N))

which can be solved by repeated substitution to get T(N) = O(log N). Also, the number of processors used in each step is N*log N.

__Preparata’s Sorting Algorithm__

Below are the steps for the Preparata’s Sorting Algorithm:

- If
**N**is a small constant, sort the keys using any algorithm and quit. - Partition the given
**N**keys into**log N**parts, with**N/(log N)**keys in each part. - Sort each part recursively and separately in parallel, assigning
**N**processors to each part. Let**S**,_{1}**S**, …,_{2}**S**be the sorted sequences._{log N} - Merge
**S**with_{i}**S**for_{j}**1< i, j < log N**in parallel. This can be done by allocating**N**/**(log N)**processors to each pair(i, j). That is, using**N*log N**processors, this step can be accomplished inwith the algorithm. As a by-product of this merging step, the rank is computed of each key in each one of the S**O(log(log N)) time**_{i}‘s(1 < i <**log N**). - Allocate
**log N**processors to compute the rank of each key in the original input. This is done in parallel for all the keys by adding the**log N**ranks computed (for each key) in step 2. This can be done inusing the prefix computation algorithm.**O(log(log N)) time** - Finally, the keys are written in the order of their ranks.

Below is the implementation of the above approach:

## Python3

`# Python program to implement Preparata's` `# a time-optimal parallel algorithm` ` ` ` ` `class` `func:` ` ` `def` `__init__(` `self` `, x, y):` ` ` `self` `.x ` `=` `x` ` ` `self` `.y ` `=` `y` ` ` `# Function to find the left index of` `# the given set of points` `def` `Left_index(points):` ` ` ` ` `# Finding the point on plane` ` ` `minn ` `=` `0` ` ` ` ` `# Traverse the given points` ` ` `for` `i ` `in` `range` `(` `1` `, ` `len` `(points)):` ` ` ` ` `# Update the value of minn` ` ` `if` `points[i].x < points[minn].x:` ` ` `minn ` `=` `i` ` ` `elif` `points[i].x ` `=` `=` `points[minn].x:` ` ` `if` `points[i].y > points[minn].y:` ` ` `minn ` `=` `i` ` ` ` ` `# Return the value of min` ` ` `return` `minn` ` ` `# Function to perform the parallel` `# process in the preparata's algorithm` `# according to the value of p, q, r` `def` `parallel(p, q, r):` ` ` ` ` `# For the three-dimensional` ` ` `val ` `=` `(q.y ` `-` `p.y) ` `*` `(r.x ` `-` `q.x) ` `-` `\` ` ` `(q.x ` `-` `p.x) ` `*` `(r.y ` `-` `q.y)` ` ` ` ` `if` `val ` `=` `=` `0` `:` ` ` `return` `0` ` ` `elif` `val > ` `0` `:` ` ` `return` `1` ` ` `else` `:` ` ` `return` `2` ` ` `# Function to perform the parallel` `# process in the preparata's algorithm` `def` `preparata(points, n):` ` ` ` ` `# There must be at least 3 points` ` ` `if` `n < ` `3` `:` ` ` `return` ` ` ` ` `# Find the leftmost point` ` ` `l ` `=` `Left_index(points)` ` ` ` ` `pre ` `=` `[]` ` ` ` ` `p ` `=` `l` ` ` `q ` `=` `0` ` ` `while` `(` `True` `):` ` ` ` ` `# Add current point to result` ` ` `pre.append(p)` ` ` ` ` `q ` `=` `(p ` `+` `1` `) ` `%` `n` ` ` ` ` `for` `i ` `in` `range` `(n):` ` ` ` ` `# If i is more counterclockwise` ` ` `# than current q, then update q` ` ` `if` `(parallel(points[p],` ` ` `points[i], points[q]) ` `=` `=` `2` `):` ` ` `q ` `=` `i` ` ` ` ` `p ` `=` `q` ` ` ` ` `# While it doesn't come to first point` ` ` `if` `(p ` `=` `=` `l):` ` ` `break` ` ` ` ` `# Print Result` ` ` `for` `each ` `in` `pre:` ` ` `print` `(points[each].x, points[each].y)` ` ` `# Driver Code` ` ` ` ` `algo ` `=` `[]` `algo.append(func(` `0` `, ` `3` `))` `algo.append(func(` `2` `, ` `2` `))` `algo.append(func(` `1` `, ` `1` `))` `algo.append(func(` `2` `, ` `1` `))` `algo.append(func(` `3` `, ` `0` `))` `algo.append(func(` `0` `, ` `0` `))` `algo.append(func(` `3` `, ` `3` `))` ` ` `# Function Call` `preparata(algo, ` `len` `(algo))` |

**Output**

0 3 0 0 3 0 3 3