¿Cómo puedo calcular de manera eficiente el número de intercambios requeridos por los métodos de ordenación lenta como la ordenación por inserción y la ordenación por burbujas para ordenar una matriz determinada?

Lo primero a tener en cuenta en este problema es que la cantidad de intercambios necesarios para ordenar una matriz es equivalente a la cantidad de inversiones en una matriz. La inversión en una matriz A[1 .. n] se define como un par (i, j) tal que i < j y A[i] > A[j] . Si la matriz no tiene inversión, se debe ordenar.

Hay dos formas de contar el número de inversiones en una matriz. Enumeraré dos soluciones a continuación, que se ejecutan en O(n log(n))

1) Modificar tipo de fusión:

Modifique el orden de fusión para que, además de ordenar su entrada, cuente el número de inversiones que estaban en esa matriz. Probémoslo por inducción.

Caso base:
Fácil: devuelve 0 inversión si el tamaño de la matriz es menor o igual que 1

Caso general:
Analicemos el problema paso a paso.

Tenemos dos matrices para fusionar. ¿Qué tipo de inversión podría haber allí? Una inversión (i, j) podría estar en la misma mitad (izquierda o mitad derecha), o podría estar en la mitad izquierda y j podría estar en la mitad derecha (no puedo estar en la mitad derecha porque tenemos la restricción i < j ). Sin embargo, según la hipótesis inductiva, el número de inversiones dentro de la mitad izquierda y derecha de la matriz sería devuelto por las respectivas llamadas recursivas a cada mitad de la matriz, por lo que solo necesitamos contar la cantidad de inversiones en las dos mitades ( i en izquierda y j en derecha).

¿Cómo contamos el número de inversiones en la matriz izquierda y derecha? En el paso de fusión del tipo de fusión, se nos da una matriz de dos tipos (izquierda y derecha). Para cada elemento left[i] en la matriz izquierda, queremos saber cuántos elementos right[j] en la matriz derecha son estrictamente más pequeños que la left[i] . Tenga en cuenta que cuando insertamos left[i] en la matriz combinada, sabemos que cada elemento a la izquierda de j es menor que left[i] (ya que se insertan en la matriz combinada antes de left[i] ). Entonces, cada vez que left[i] se inserta en la matriz combinada, incremente el contador en j . Este será el número de inversiones.

Aquí hay un código de muestra escrito en python:

 def merge_sort(A): if len(A) <= 1: return 0, A middle = len(A)/2 left_inversions, left = merge_sort(A[:middle]) right_inversions, right = merge_sort(A[middle:]) merge_inversions, merged = merge(left, right) inversions = left_inversions + right_inversions + merge_inversions return inversions, merged def merge(left, right): result = [] i, j, inversions = 0, 0, 0 while i < len(left) and j < len(right): if left[i] <= right[j]: inversions += j result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 inversions += j*(len(left)-i) result += left[i:] result += right[j:] return inversions, result 

2) Resolver usando BIT (árbol indexado binario)

Aquí hay una forma ordenada de resolver el problema usando BIT (árbol indexado binario). BIT es una estructura de datos muy ligera que a menudo se usa para almacenar frecuencias de valores. Tiene dos operaciones básicas: suma y suma.

Cuando hay una lista [matemáticas] a_1, a_2, a_3,…, a_n [/ matemáticas]

[matemáticas] agregar (i, x): a_i = a_i + x [/ matemáticas]
[matemática] suma (i): a_1 + a_2 + a_3 +… + a_i [/ ​​matemática]

BIT puede realizar sumas y sumas en tiempo O(log(n)) .

Si desea saber cómo funciona, aquí hay un enlace a un tutorial de TopCoder que habla sobre BIT http://community.topcoder.com/tc…

Aquí hay una implementación de muestra de BIT en Python:

 class Bit: def __init__(self, n): sz = 1 while n > sz: sz *= 2 self.size =sz self.data = [0]*sz def sum(self, i): s = 0 while i > 0: s += self.data[i] i -= i & -i return s def add(self, i, x): while i < self.size: self.data[i] += x i += i & -i 

¿Cómo podemos contar el número de inversiones usando BIT? Aquí está el algoritmo:

Deje que A[0 ... n-1] sea ​​una matriz. La inversión es un par (i, j) donde i < j tal que A[i] > A[j] .

Cree un BIT de tamaño mayor que n. Iterar a través de la matriz A (sea j el índice del bucle), y para cada elemento A[j] hacer:

1) Agregue j-sum(A[j]) al número de inversiones
2) add(A[j], 1) (es decir, sumar 1 a la posición A[j] en BIT. Esto cuenta efectivamente el número de tiempo que se ve el valor A[j] hasta ahora)

¿Por qué funciona esto? Tenga en cuenta que para cada j , sum(A[j]) es el número de i tal que i < j y A[i] < A[j] , que es exactamente lo contrario de la definición de la inversión. En otras palabras, j-sum(A[j]) es el número de inversión. Tanto la suma como la suma toman O(log(m)) donde m=max(A) y se realizan n veces, por lo que el tiempo de ejecución de este algoritmo es O(n log(m)) .

Código en python:

 def inversions(A): res = 0 bit = Bit(max(A)+1) for i, v in enumerate(A): res += i - bit.sum(v) bit.add(v, 1) return res 

Muchas gracias Yoshiya Miyata por su explicación detallada.

Por si acaso alguien interesado, el código Java utiliza la técnica de clasificación de fusión modificada.

  
   // Uso largo para que este programa se pueda usar para contar inversiones para el tamaño de entrada de más de 100,000 y para eso, las inversiones están fuera del rango de enteros.
   inversiones largas estáticas privadas = 0l;
   public static void main (String [] args) {
       int [] ar = {4,2,6,8,1,78,23,7,2};
       getInversions (ar, 0, ar.length - 1);
       System.out.println (inversiones);
   }
   // Ordeno la matriz usando la técnica de fusión.
   getInversions estático público vacío (int [] nums, int left, int right) {
     si (izquierda  = leftLength) {
       // copia los elementos restantes de la derecha
       para (; j 

Nota: Acabo de usar la variable global para una modificación rápida, pero la mejor manera es cambiar los métodos de fusión y getInversions para devolver las inversiones.

Lenguaje: C ++ (g ++ 4.3.2)

Algoritmo: Combinar clasificación (bidireccional)

En este Código, t es el número de casos de prueba y la restricción es un número entero, pero se puede cambiar muy fácilmente si es necesario.

Complejidad de tiempo: O (nlogn)
Categoría: Devide and Conquer

Aquí, he usado la técnica de dividir y conquistar, por lo que cada matriz de tiempo se divide en la mitad, luego en cada mitad, se calcula el número de inversiones y luego se agregan las inversiones en la fusión, este proceso se realiza de forma recursiva hasta que no se complete matriz como matriz combinada.

  #include 

 largo largo int MS (int a [], int temp [], int left, int right);
 long long int Merge (int a [], int temp [], int left, int mid, int right);

 largo largo int MS (int a [], int temp [], int left, int right)
 {
	 largo largo int cnt = 0;
	 int mid;
	 si (derecha> izquierda)
	 {
		 mid = (derecha + izquierda) / 2;
		 cnt = MS (a, temp, izquierda, media);
		 cnt + = MS (a, temp, mid + 1, right);
		 cnt + = Fusionar (a, temp, izquierda, media + 1, derecha);
	 }
	 return cnt;
 }

 long long int Merge (int a [], int temp [], int left, int mid, int right)
 {
	 int i, j, k;
	 largo largo int cnt = 0;
	 i = izquierda;
	 j = medio;
	 k = izquierda;
	 while (i <= mid-1 && j <= right)
	 {
		 si (a [i] <= a [j])
			 temp [k ++] = a [i ++];
		 más
		 {
			 temp [k ++] = a [j ++];
			 cnt + = (mediados de i);
		 }
	 }
	 mientras que (i <= mediados de 1)
		 temp [k ++] = a [i ++];
	 mientras que (j <= derecha)
		 temp [k ++] = a [j ++];
	 para (i = izquierda; i <= derecha; i ++)
		 a [i] = temp [i];
	 return cnt;
 }

 int main ()
 {
	 int * a, * tmp, t, n;
	 scanf ("% d", & t);
	 para (int i = 0; i 
    

En el volumen 3 de Knuth, analiza la clasificación de burbujas en términos de A (número de pasadas), B (número de intercambios) y C (número de comparaciones). El componente C parece ser la parte más complicada del análisis para Bubble sort.

El componente B parece ser lo que buscas. Knuth cubre eso en la sección 5.1.1 (p. 11) del Volumen 3.

El mejor enlace para encontrar el número de inversiones en una matriz usando BIT
Contando inversiones en una matriz usando el árbol indexado binario
Sin embargo, la comprensión de BIT es imprescindible. Si desea conocer la Intitution Behind BIT. Aquí está el enlace: -BIT: ¿Cuál es la intuición detrás de un árbol indexado binario y cómo se pensó?

Dado que el tipo de fusión es la mejor técnica conocida con complejidad de tiempo nlgn. Incluso para contar los intercambios en el orden de inserción, esto se aplica como la forma más eficiente.

¡¡Gracias!!