¿Qué es un algoritmo para encontrar la mediana de la complejidad en o (n) tiempo?

Hay un algoritmo conocido para encontrar el elemento más pequeño [math] k_th [/ math] en una matriz desordenada de n elementos. También podemos usar el algoritmo para encontrar la mediana, ya que la mediana será [matemática] \ frac {n + 1} {2} [/ matemática] elemento más pequeño si [matemática] n [/ matemática] es impar, y el promedio de [ matemática] \ lceil {\ frac {n + 1} {2}} \ rceil [/ math] y [math] \ lfloor {\ frac {n + 1} {2}} \ rfloor [/ math] elemento más pequeño cuando [ matemáticas] n [/ matemáticas] es par.

El algoritmo se basa en una estrategia de divide y vencerás. Así que redefinamos nuestro problema:

Problema: Dada una matriz [matemática] A = A [1,…, n] [/ matemática] de [matemática] n [/ matemática] números y un índice [matemática] k (1≤k≤n) [/ matemática] , encuentre el elemento [math] k_ {th} [/ math] más pequeño de A.

Algoritmo: SELECCIONAR (A, i)

Divida los n elementos en grupos de 5 (más el resto).

Encuentre la mediana de cada grupo de 5 (de memoria). (Si el grupo restante tiene un número par de elementos, entonces rompa los lazos arbitrariamente, por ejemplo, eligiendo la mediana inferior).

Use SELECT recursivamente para encontrar la mediana (llámela x) de estos
Techo (n / 5) medianas.

Partición alrededor de x. Deje k = rango (x).

Si i = k, entonces devuelve x.
De lo contrario, si i <k, use SELECT recursivamente llamando a SELECT (A [1, …, k − 1], i).
De lo contrario, si i> k, use SELECT recursivamente llamando a SELECT (A [k + 1, …, i], i − k).

Tiempo de ejecución:

Para demostrar que nuestro algoritmo es eficiente, sería útil saber que el subarreglo activo en cada llamada recursiva sucesiva es mucho más pequeño que el subarreglo anterior. De esa manera, tenemos la garantía de que solo son necesarias relativamente pocas llamadas recursivas. Por lo tanto, determinemos un límite superior en el tamaño de [math] A ‘[/ math], el nuevo subarray activo, dado que el viejo subarray, [math] A [/ math], tenía un tamaño [math] n [/ math ]

Como en la línea [matemáticas] 7 [/ matemáticas], dejemos que [matemáticas] x [/ matemáticas] sea la mediana de las medianas. Entonces, [math] A ‘[/ math] no contiene ningún elemento mayor que [math] x [/ math], o [math] A’ [/ math] no contiene ningún elemento menor que [math] x [/matemáticas]. Sin embargo, como veremos, hay muchos elementos mayores que xy muchos elementos menores que [math] x [/ math]. En particular, cada grupo (no resto) cuya mediana es menor que [matemáticas] x [/ matemáticas] contiene al menos tres elementos menos que [matemáticas] x [/ matemáticas], y cada grupo (no resto) cuya mediana es mayor que [matemáticas] x [/ matemáticas] contiene al menos tres elementos mayores que [matemáticas] x [/ matemáticas]. Además, hay al menos [math] \ lceil {\ frac {n} {5}} \ rceil – 1 [/ math] grupos no restantes, de los cuales al menos [math] \ lfloor {\ frac {1} { 2} \ lceil {\ frac {n} {5}} \ rceil – 2} \ rfloor [/ math] tienen una mediana menor que [math] x [/ math] y al menos [math] \ lfloor {\ frac {1 } {2} \ lceil {\ frac {n} {5}} \ rceil – 2} \ rfloor [/ math] tienen una media mayor que [math] x [/ math]. Finalmente, el grupo con [matemática] x [/ matemática] como su mediana contiene dos elementos mayores que [matemática] x [/ matemática] y dos elementos menores que [matemática] x [/ matemática] (a menos que [matemática] n <5 [/ matemáticas]) Por lo tanto,

[matemática] \ text {número de elementos de A menor que x} \ geq \ frac {3n} {10} + 6 [/ matemática]

y

[matemáticas] \ text {número de elementos de A mayor que x} \ geq \ frac {3n} {10} + 6 [/ matemáticas]

Entonces obtenemos una recurrencia generalizada del problema como:

[matemáticas] \ boxed {T (n) \ leq T (\ lceil {\ frac {n} {5}} \ rceil) + T (\ frac {7n} {10} + 6) + O (n)} [ /matemáticas]

Al resolver esta recurrencia utilizando el teorema del Maestro [1], de hecho obtenemos [matemáticas] T (n) [/ matemáticas] como [matemáticas] O (n). [/ Matemáticas]

A continuación se muestra una función C ++ [2] para implementar lo mismo:

// Una función simple para encontrar la mediana de arr []. Se llama
int findMedian (int arr [], int n)
{
ordenar (arr, arr + n); // Ordenar la matriz
retorno arr [n / 2]; // Devuelve el elemento medio
}

intercambio nulo (int * a, int * b)
{
int temp = * a;
* a = * b;
* b = temp;
}
// Busca x en arr [l..r] y divide la matriz
// alrededor de x.
partición int (int arr [], int l, int r, int x)
{
// Busca x en arr [l..r] y muévelo al final
int i;
para (i = l; i <r; i ++)
if (arr [i] == x)
rotura;
swap (& arr [i], & arr [r]);
// Algoritmo de partición estándar
i = l;
para (int j = l; j <= r – 1; j ++)
{
if (arr [j] <= x)
{
swap (& arr [i], & arr [j]);
i ++;
}
}
swap (& arr [i], & arr [r]);
volver i;
}
// Devuelve el elemento k’th más pequeño en arr [l..r] en el peor de los casos
// tiempo lineal. ASUNCIÓN: TODOS LOS ELEMENTOS EN ARR [] SON DISTINTOS
int kthSmallest (int arr [], int l, int r, int k)
{
// Si k es menor que el número de elementos en la matriz
si (k> 0 && k <= r – l + 1)
{
int n = r-l + 1; // Número de elementos en arr [l..r]

// Divide arr [] en grupos de tamaño 5, calcula la mediana
// de cada grupo y guárdelo en la matriz mediana [].
int i, mediana [(n + 4) / 5]; // Habrá grupos de piso ((n + 4) / 5);
para (i = 0; i <n / 5; i ++)
mediana [i] = findMedian (arr + l + i * 5, 5);
if (i * 5 <n) // Para el último grupo con menos de 5 elementos
{
mediana [i] = findMedian (arr + l + i * 5, n% 5);
i ++;
}

// Encuentra la mediana de todas las medianas usando llamadas recursivas.
// Si mediana [] tiene solo un elemento, entonces no es necesario
// de llamada recursiva
int medOfMed = (i == 1)? mediana [i-1]:
kthSmallest (mediana, 0, i-1, i / 2);

// Particionar la matriz alrededor de un elemento aleatorio y
// obtiene la posición del elemento pivote en una matriz ordenada
int pos = partición (arr, l, r, medOfMed);

// Si la posición es la misma que k
si (pos-l == k-1)
volver arr [pos];
if (pos-l> k-1) // Si la posición es más, recurrir a la izquierda
return kthSmallest (arr, l, pos-1, k);

// De lo contrario, se repite para la submatriz derecha
return kthSmallest (arr, pos + 1, r, k-pos + l-1);
}

// Si k es más que el número de elementos en la matriz
devolver INT_MAX;
}

También escribí un programa para generar un conjunto de datos aleatorio y luego tracé un gráfico en R para demostrar la diferencia.

vector generateRandomArray (int * arr, int n)
{
vector v (n);
srand (tiempo (NULL)); // Solo por ejemplo
generar (v.begin (), v.end (), rand);
copy (v.begin (), v.end (), arr);
volver v;
}
int main ()
{
salida corriente;
fout.open (“analysis.txt”);
fout << "MEIDAN_OF_MEDIAN REGULAR_SORTING \ n";
para (int i = 1000; i <= 1000000; i + = 1000)
{
int arr [i];
vector v = generateRandomArray (arr, i);
int mediana;
clock_t start, endd;
doble msecs1, msecs2;
inicio = reloj ();
// para (int j = 0; j <50; ++ j)
mediana = (kthSmallest (arr, 0, i – 1, i / 2) + kthSmallest (arr, 0, i-1, i / 2 + 1)) / 2.0;
endd = reloj ();
msecs1 = (((double) (endd – start)) * 1000) / CLOCKS_PER_SEC;
inicio = reloj ();
// para (int j = 0; j <50; ++ j) {
sort (v.begin (), v.end ());
mediana = (v [i / 2] + v [i / 2 – 1]) / 2.0; //}
endd = reloj ();
msecs2 = (((double) (endd – start)) * 1000) / CLOCKS_PER_SEC;
cout << i << "" << msecs1 << "" << msecs2 << "\ n";
fout << msecs1 << "" << msecs2 << "\ n";
}
devuelve 0;
}

El código escrito en R para generar una trama.

require (ggplot2)
analysis <- read.table ('analysis2.txt', header = TRUE)
INPUT_VALUE <- as.vector (seq (1,313))
INPUT_VALUE <- as.data.frame (INPUT_VALUE)
analysis_df <- data.frame (INPUT_VALUE, análisis)
ggplot (analysis_df, aes (INPUT_VALUE, y = value, color = variable)) +
geom_smooth (aes (y = MEIDAN_OF_MEDIAN, col = “MEIDAN_OF_MEDIAN”)) +
geom_smooth (aes (y = REGULAR_SORTING, col = “REGULAR_SORTING”))

La trama se da a continuación:

Input_Size está en una escala de x1000

Con valores de entrada suficientemente menores, tenemos casi el mismo rendimiento, pero tan pronto como las entradas aumentan, la diferencia se ve claramente.

Espero que haya ayudado.

Saludos 🙂

Notas al pie

[1] Teorema maestro (análisis de algoritmos) – Wikipedia

[2] K’th Elemento más pequeño / más grande en matriz sin clasificar | Set 3 (peor tiempo lineal del caso) – GeeksforGeeks

No conozco ningún algoritmo para encontrar la mediana en el tiempo O (N) (peor de los casos). Pero conozco un algoritmo que tiene un tiempo lineal esperado.

Es lo mismo que el paso de partición del algoritmo de clasificación rápida. Elija un elemento como pivote, particione la matriz. Después de la partición, si la posición del pivote es igual a n / 2, entonces ha encontrado la mediana.

Si la posición es, digamos, ‘x’ yx> n / 2, entonces debe buscar la mediana en la parte izquierda, si x

Todo el proceso es casi el mismo que el de clasificación rápida, excepto que, después de la partición, solo verá una parte en lugar de ambas. Esto reduce la complejidad de NlogN a N (esperado, para ambos).

Estoy bastante seguro de que no hay un algoritmo para hacerlo en O (N) para el peor de los casos, porque, si existiera dicho algoritmo, quicksort lo usaría para obtener O (NlogN) en su peor de los casos.

Esto debería ayudar:
La respuesta de Ajay Gaur a ¿Cómo escribo un algoritmo para encontrar el enésimo número más alto en una matriz?

La mediana es solo un caso especial.

Consulte el Algoritmo de selección – Algoritmo de selección

More Interesting

¿Qué algoritmo debo usar para crear un solucionador de Sudoku?

¿Cómo se usa el teorema de Bayes en robótica?

¿Qué es una mónada?

Cómo WAP para encontrar el máximo de todos los elementos del tamaño de matriz 'n'

Big data, seguridad informática y matemática financiera; ¿Cuál de estos campos es el mejor para emprender como carrera si eres de antecedentes matemáticos?

¿Puede C (lenguaje de programación) tratar con grandes números?

¿Por qué los maestros programadores insisten en usar las matemáticas para enseñar a sus estudiantes los conceptos básicos de la programación dado que no se usa tanto a diario?

¿Cuál es la diferencia entre una cubierta abierta y una subcubierta finita en relación con la compacidad?

Mi cerebro no procesa muy bien la resolución de problemas matemáticos. ¿La programación es para mí?

Las calificaciones en una prueba intermedia se distribuyen normalmente con una media de 69 y una desviación estándar de 10. ¿Cuál es la probabilidad de que una clase de 27 tenga un promedio menor de 67 (3 lugares decimales)? ¿Cómo hago esto?

¿Cuáles son algunos problemas realmente fáciles de explicar que en realidad son increíblemente difíciles de resolver?

Cómo ser más competente en matemáticas por mi cuenta

¿Qué es lo contrario de una máquina de Turing? ¿Existe una máquina teórica que ya esté configurada para calcular algún algoritmo de la manera más directa?

¿Qué son las matemáticas discretas?

¿Cuánto conocimiento matemático profundo deberías tener como diseñador de juegos?