Ir al contenido

Mergesort odd-even y ordenamiento bitónico

Problema: mezclar A = (a₁ … an) y B = (b₁ … bn), ya ordenadas, en C ordenada de 2n elementos (supuestos: mismo tamaño y n potencia de 2).

Con n = 2 (lámina 24) hacen falta 3 comparadores. P1 compara los menores: min(a₁, b₁) = c₁. P2 compara los mayores: max(a₂, b₂) = c₄. P3 ordena los dos del medio (c₂, c₃).

Odd-even merge de Batcher (láminas 26–28)

Sección titulada «Odd-even merge de Batcher (láminas 26–28)»

Para mezclar dos secuencias de n:

  1. Mezclar recursivamente los elementos impares de A y B (a₁, a₃, … con b₁, b₃, …) y obtener d₁ … dn.
  2. A la vez, mezclar los pares (a₂, a₄, … con b₂, b₄, …) y obtener e₁ … en.
  3. Juntar: c₁ = d₁, c2n = en, y para i = 1 … n − 1: c2i = min(di+1, ei), c2i+1 = max(di+1, ei).
Ejemplo completo de la lámina 25: A = {3, 5, 7, 9}, B = {2, 4, 6, 8}

Impares: A → (3, 7), B → (2, 6). Mezclados: d = (2, 3, 6, 7).

Pares: A → (5, 9), B → (4, 8). Mezclados: e = (4, 5, 8, 9).

Juntar:

  • c₁ = d₁ = 2 · c₈ = e₄ = 9
  • i = 1: c₂ = min(d₂, e₁) = min(3, 4) = 3, c₃ = max(3, 4) = 4
  • i = 2: c₄ = min(d₃, e₂) = min(6, 5) = 5, c₅ = max(6, 5) = 6
  • i = 3: c₆ = min(d₄, e₃) = min(7, 8) = 7, c₇ = max(7, 8) = 8

C = (2, 3, 4, 5, 6, 7, 8, 9) ✓. El último paso son 3 comparaciones independientes: se hacen en un solo paso paralelo.

Complejidad del mergesort paralelo (lámina 29): mezclar dos secuencias de n tiene profundidad O(log n), y el mergesort hace log n niveles de mezcla. Por tanto el span es O(log² n), con trabajo O(n log n) en el modelo PRAM CREW. Con el merge de Cole se llega a O(log n log log n).

Conocimiento general: como red de comparadores fija, el odd-even mergesort de Batcher usa O(n log² n) comparadores, igual que el bitónico.

Secuencia bitónica: a₀ < a₁ < … < ai > ai+1 > … > an−1, es decir, primero crece y después decrece. También cuenta una rotación cíclica de una secuencia así.

Propiedad clave (half-cleaner): si en una secuencia bitónica de n elementos se compara-intercambia ai con ai+n/2 para todo i < n/2, se obtienen dos secuencias bitónicas de n/2, y todo elemento de la primera es ≤ que todo elemento de la segunda.

entrada bitónica (sube y baja)tras comparar a[i] con a[i+4]35897421mín / máx34217589mínimos: bitónicamáximos: bitónica4 ≤ 5

Cómo leerlo: la entrada (3, 5, 8, 9, 7, 4, 2, 1) sube y luego baja: es bitónica. Se compara cada elemento de la mitad celeste con el que está 4 posiciones después (arcos) y se quedan los mínimos a la izquierda y los máximos a la derecha. Resultan (3, 4, 2, 1) y (7, 5, 8, 9): las dos son bitónicas y el mayor de los mínimos (4) es menor que el menor de los máximos (5), línea roja. Repetir esto en cada mitad ordena todo.

Aplicándolo recursivamente (n/2, n/4, …, 2) una secuencia bitónica queda ordenada en log n pasos paralelos: es la mezcla bitónica.

Para ordenar una entrada cualquiera (lámina 33): todo par de números es bitónico. Se ordenan pares (alternando creciente y decreciente), así cada 4 forman una secuencia bitónica. Se mezclan los bloques de 4 (alternando), y cada 8 son bitónicos, y así sucesivamente.

Red de ordenamiento bitónico (n = 8)

Cada línea horizontal es una posición del arreglo. Cada barra vertical es un comparar-intercambiar: la flecha apunta a donde va el mayor. Todas las barras de una columna son independientes y se ejecutan a la vez.

k=2, j=1k=4, j=2k=4, j=1k=8, j=4k=8, j=2k=8, j=17733668811552244entradaahora
columna 0/6
columnas (pasos en paralelo)log n (log n + 1)/2 = 6
comparadores por columnan/2 = 4
trabajo total24 comparaciones
Celeste = bloque que se ordena creciente, naranja = decreciente. Dale a ▶ y mira cómo se forman secuencias bitónicas cada vez más largas.

La etapa que construye bloques ordenados de tamaño 2i es una mezcla bitónica de i pasos. Las etapas van de i = 1 a log n:

Tp = Σi=1log n i = 1 + 2 + … + log n = log n · (log n + 1) / 2 = O(log² n)

Comprobación con n = 8: 1 + 2 + 3 = 6 columnas (las del playground). Con n = 2²² son 22·23/2 = 253 pasos.

Trabajo: cada paso tiene n/2 comparadores, así que W = (n/2) · log n (log n + 1)/2 = O(n log² n). Es más que el O(n log n) del secuencial, así que no es óptimo en costo. Lo compensa su regularidad: no depende de los datos y se mapea bien a hardware (hipercubos, GPUs).

Bitónico con OpenMP: medido contra std::sort (solución propia)

Sección titulada «Bitónico con OpenMP: medido contra std::sort (solución propia)»
void bitonico(vector<int>& a) {
int n = a.size();
for (int k = 2; k <= n; k <<= 1)
for (int j = k >> 1; j > 0; j >>= 1) {
#pragma omp parallel for schedule(static)
for (int i = 0; i < n; i++) {
int l = i ^ j;
if (l > i) {
bool asc = (i & k) == 0;
if ((asc && a[i] > a[l]) || (!asc && a[i] < a[l])) swap(a[i], a[l]);
}
}
}
}

Cómo leer el código: k es el tamaño del bloque que se está formando y j la distancia del comparador. El socio de i es i ^ j (XOR: cambia un bit). Por eso en un hipercubo cada comparación es entre vecinos directos. El bit i & k decide si ese bloque va creciente o decreciente.

Con n = 2²² enteros al azar (salida verificada igual a la de std::sort):

Hilosbitónicostd::sort (1 hilo)
11.219 s0.260 s
20.643 s0.271 s
40.496 s0.257 s
80.349 s0.283 s

Con 8 hilos el bitónico sigue siendo más lento que std::sort con un solo hilo. Hace 253 · 2²¹ ≈ 5.3·10⁸ comparaciones, contra ≈ n log n ≈ 9.2·10⁷ de un buen secuencial (≈ 6 veces más trabajo), y además recorre el arreglo completo 253 veces. Escala (S(8) = 3.5 respecto a sí mismo), pero contra el mejor secuencial S = 0.75. Es la diferencia entre speedup relativo y speedup absoluto.

O(n log n) con un montículo (heap): se extrae la raíz (el máximo), se lleva el último elemento a la raíz y se “hunde”. Cada extracción depende de la anterior, porque el heap cambia. Es una cadena secuencial de n pasos, y por eso es difícil de paralelizar (lámina 35).

1. ¿Es bitónica cada secuencia? (a) 2 5 8 6 1 (b) 1 3 2 4 (c) 7 4 1 3 6

Ver solución

(a) Sí: sube 2→5→8 y baja 8→6→1.

(b) No: sube, baja y vuelve a subir (1 3 2 4). Tampoco es rotación de una bitónica.

(c) Sí: baja y luego sube. Es una rotación cíclica de 1 3 6 7 4, que sube y baja.

2. Aplica el half-cleaner a la secuencia bitónica (1, 4, 7, 8, 6, 5, 3, 2).

Ver solución

Se compara ai con ai+4: (1,6) → 1, 6 · (4,5) → 4, 5 · (7,3) → 3, 7 · (8,2) → 2, 8.

Queda (1, 4, 3, 2 | 6, 5, 7, 8). Primera mitad: 1 4 3 2, bitónica. Segunda: 6 5 7 8, bitónica (rotación). Y máx(primera) = 4 ≤ 5 = mín(segunda) ✓.

3. ¿Cuántos pasos paralelos y cuántas comparaciones hace la red bitónica con n = 1024?

Ver solución

log n = 10, así que pasos = 10·11/2 = 55. Comparaciones = 55 · 512 = 28 160. Un secuencial necesita ≈ n log n = 10 240, así que la red hace 2.75 veces más trabajo.

  • Aplicar el half-cleaner a una secuencia no bitónica: las mitades ya no quedan separadas (menores | mayores).
  • Decir que el bitónico tarda log n pasos: eso es solo una mezcla. El ordenamiento completo tarda log n (log n + 1)/2 = O(log² n).
  • Llamarlo óptimo en costo: su trabajo es O(n log² n), mayor que el O(n log n) secuencial.

Aplica un paso de half-cleaner a (2, 6, 7, 5, 4, 3, 1, 0). Comprueba las dos propiedades.

Ver solución

Pares (ai, ai+4): (2, 4), (6, 3), (7, 1), (5, 0).

  • Mínimos: (2, 3, 1, 0) → sube y baja: bitónica ✓.
  • Máximos: (4, 6, 7, 5) → sube y baja: bitónica ✓.
  • máx de los mínimos = 3 ≤ 4 = mín de los máximos ✓.

Autoevaluación

0/6 correctas · 0 respondidas
1. Una secuencia bitónica es…
2. El half-cleaner compara a_i con…
3. Pasos paralelos del bitónico con n elementos:
4. ¿Por qué el bitónico no es óptimo en costo?
5. En el odd-even merge, c_{2i} =
6. ¿Por qué heapsort es difícil de paralelizar?

Fuente ·U5.2 Algoritmos-ordenamiento.pdf (láminas 22–35) · bitonico.cpp (solución propia, ejecutada)