Contagem de classificação em Java
1. Visão geral
Algoritmos de classificação de propósito geral comoMerge Sort não fazem suposições sobre a entrada, portanto, eles não podem superar oO(n log n) no pior caso. A Classificação de Contagem, pelo contrário, tem uma suposição sobre a entrada, o que a torna um algoritmo de classificação de tempo linear.
Neste tutorial, vamos nos familiarizar com a mecânica da Classificação de contagem e, em seguida, implementá-la em Java.
2. Classificação de contagem
A classificação por contagem, ao contrário dos algoritmos de classificação mais clássicos, não classifica a entrada fornecida comparando os elementos. Em vez disso,it assumes that the input elements are n integers in the range [0, k]. Quandok = O(n), then, a classificação de contagem será executada emO(n).
Observe, então, que não podemos usar a classificação de contagem como um algoritmo de classificação de propósito geral. No entanto, quando a entrada está alinhada com esta suposição, é muito rápido!
2.1. Matriz de frequência
Vamos supor que vamos classificar uma matriz de entrada com valores no intervalo [0, 5]:https://www.example.com/wp-content/uploads/2019/09/counts.png
Primeiro,we should count the occurrence of each number in the input array. If we represent the countings with array C, then C[i] represents the frequency of number i in the input array:
Por exemplo, como 5 aparece 3 vezes na matriz de entrada, o valor do índice 5 é igual a 3.
Now given the array C, we should determine how many elements are less than or equal to each input element. Por exemplo:
-
Um elemento é menor ou igual a zero, ou seja, existe apenas um valor zero, que é igual aC[0]
-
Dois elementos são menores ou iguais a um, que é igual aC[0] + C[1]
-
Quatro valores são menores ou iguais a dois, o que é igual aC[0] + C[1] + C[2]
So if we keep computing the summation of n consecutive elements in C, we can know how many elements are less than or equal to number n-1 in the input array. De qualquer forma, ao aplicar esta fórmula simples, podemos atualizar oC da seguinte forma:
2.2. O Algoritmo
Agora podemos usar o array auxiliarC para classificar o array de entrada. Veja como funciona a classificação de contagem:
-
Ele itera a matriz de entrada na ordem inversa
-
Para cada elemento,i, C[i] – 1 representa a localização do númeroi na matriz classificada. Isso se deve ao fato de que existemC[i] eleções menores ou iguais ai
-
Em seguida, diminui oC[i] ao final de cada rodada
Para classificar a matriz de entrada de amostra, devemos primeiro começar com o número 5, uma vez que é o último elemento. De acordo comC[5], there, 11 elementos são menores ou iguais ao número 5.
Portanto, 5 deve ser o elemento 11th na matriz classificada, portanto, o índice 10:
Como movemos 5 para a matriz classificada, devemos decrementar o elementoC[5]. Next na ordem reversa é 2. Como existem 4 elementos menores ou iguais a 2, esse número deve ser o elemento 4th na matriz classificada:
Da mesma forma, podemos encontrar o ponto certo para o próximo elemento, que é 0:
Se mantivermos a iteração inversa e movermos cada elemento adequadamente, teremos algo como:
3. Classificação de Contagem - Implementação Java
3.1. Computando a matriz de frequência
Em primeiro lugar, dada uma matriz de entrada de elementos e ok, we deve calcular a matrizC:
int[] countElements(int[] input, int k) {
int[] c = new int[k + 1];
Arrays.fill(c, 0);
for (int i : input) {
c[i] += 1;
}
for (int i = 1; i < c.length; i++) {
c[i] += c[i - 1];
}
return c;
}
Vamos analisar a assinatura do método:
-
input representa uma matriz de números que vamos classificar
-
A matrizinput é uma matriz de inteiros no intervalo de [0,k] - entãok representa o número máximo eminput
-
O tipo de retorno é uma matriz de inteiros que representam oC array
E aqui está como o métodocountElements funciona:
-
Primeiro, inicializamos oC array. Como o intervalo [0, k] contémk+1 numbers, estamos criando uma matriz capaz de conterk+1 numbers
-
Então, para cada número noinput, we está computando a frequência desse número
-
E, finalmente, estamos adicionando elementos consecutivos para saber quantos elementos são menores ou iguais a um determinado número
Além disso, podemos verificar se o métodocountElements funciona conforme o esperado:
@Test
void countElements_GivenAnArray_ShouldCalculateTheFrequencyArrayAsExpected() {
int k = 5;
int[] input = { 4, 3, 2, 5, 4, 3, 5, 1, 0, 2, 5 };
int[] c = CountingSort.countElements(input, k);
int[] expected = { 1, 2, 4, 6, 8, 11 };
assertArrayEquals(expected, c);
}
3.2. Classificando a matriz de entrada
Agora que podemos calcular a matriz de frequências, poderemos classificar qualquer conjunto de números:
int[] sort(int[] input, int k) {
int[] c = countElements(input, k);
int[] sorted = new int[input.length];
for (int i = input.length - 1; i >= 0; i--) {
int current = input[i];
sorted[c[current] - 1] = current;
c[current] -= 1;
}
return sorted;
}
Veja como funciona o métodosort :
-
Primeiro, ele calcula oC array
-
Em seguida, itera oinput array ao contrário e para cada elemento eminput, encontra seu ponto correto na matriz classificada. A seleçãoith eminput deve ser a seleçãoCth na matriz classificada. Como os arrays Java são indexados por zero, a entradaC[i]-1 é a seleçãoCth - por exemplo,sorted[5] é o sexto elemento do array classificado
-
Cada vez que encontramos uma correspondência, ele diminui o valorC[i] correspondente
Da mesma forma, podemos verificar se o métodosort funciona conforme o esperado:
@Test
void sort_GivenAnArray_ShouldSortTheInputAsExpected() {
int k = 5;
int[] input = { 4, 3, 2, 5, 4, 3, 5, 1, 0, 2, 5 };
int[] sorted = CountingSort.sort(input, k);
// Our sorting algorithm and Java's should return the same result
Arrays.sort(input);
assertArrayEquals(input, sorted);
}
4. Revisitando o algoritmo de classificação de contagem
4.1. Análise de complexidade
Most classic sorting algorithms, like merge sort, sort any given input by just comparing the input elements to each other. Estes tipos de algoritmos de classificação são conhecidos comocomparison sorts. No pior caso, as classificações de comparação devem levar pelo menos O(n log n) para classificarn eleções.
A classificação de contagem, por outro lado, não classifica a entrada comparando os elementos de entrada, portanto, claramente não é um algoritmo de classificação por comparação.
Vamos ver quanto tempo leva para classificar a entrada:
-
Ele calcula oC array emO(n+k) time: uma vez que itera uma matriz de entrada com tamanhon inO(n) areia, em seguida, iteraC inO(k) – para que seja O(n+k) in total
-
Depois de calcular oC, , o sit classifica a entrada iterando a matriz de entrada e executando algumas operações primitivas em cada iteração. Portanto, a operação de classificação real levaO(n)
No total, a classificação de contagem levaO(n+k) tempo para ser executada:
O(n + k) + O(n) = O(2n + k) = O(n + k)
If we assume k=O(n), then counting sort algorithm sorts the input in linear time. Ao contrário dos algoritmos de classificação de uso geral, as classificações de contagem fazem uma suposição sobre a entrada e levam menos do que o limite O(n log n) mais lento para executar.
4.2. Estabilidade
Alguns momentos atrás, estabelecemos algumas regras peculiares sobre a mecânica da contagem de notas, mas nunca esclarecemos o motivo por trás delas. Para ser mais específico:
-
Por que devemos iterar a matriz de entrada ao contrário?
-
Por que diminuímos aC[i] de cada vez que o estamos usando?
Vamos iterar desde o início para entender melhor a primeira regra. Suponha que vamos classificar uma matriz simples de inteiros como o seguinte:
Na primeira iteração, devemos encontrar o local classificado para o primeiro 1:
Portanto, a primeira ocorrência do número 1 obtém o último índice na matriz classificada. Ignorando o número 0, vamos ver o que acontece com a segunda ocorrência do número 1:
A ordem de aparecimento dos elementos com o mesmo valor é diferente na entrada e na matriz classificada, então o algoritmo não éstable quando estamos iterando desde o início.
O que acontece se não diminuirmos o valorC[i] após cada uso? Vamos ver:
Ambas as ocorrências do número 1 estão obtendo o último lugar na matriz classificada. Portanto, se não diminuirmos o valorC[i] após cada uso, podemos perder alguns números ao classificá-los!
5. Conclusão
Neste tutorial, primeiro, aprendemos como a Classificação de contagem funciona internamente. Em seguida, implementamos esse algoritmo de classificação em Java e escrevemos alguns testes para verificar seu comportamento. E, finalmente, provamos que o algoritmo é um algoritmo de classificação estável com complexidade de tempo linear.
Como de costume, os códigos de amostra estão disponíveis em nosso projetoGitHub, então certifique-se de conferir!