Contagem estimada de distribuição - na maioria das vezes reinventou a classificação


O número de tipos mais ou menos diferentes um do outro é garantido em mais de cem. Entre eles, existem subgrupos de algoritmos que são minimamente diferentes um do outro, coincidindo em alguma idéia principal geral. De fato, em anos diferentes, pessoas diferentes criam as mesmas classificações novamente, diferindo em detalhes não muito fundamentais.

Essa ideia algorítmica é encontrada com mais frequência do que outras.

Cada elemento é inserido aproximadamente no local da matriz em que deve estar localizado. Acontece uma matriz quase ordenada . À qual a classificação por inserções é aplicada (é mais eficaz para o processamento de matrizes quase ordenadas) ou áreas não ordenadas locais são processadas recursivamente pelo mesmo algoritmo.

EDISON Software - desenvolvimento web
Este artigo foi escrito com o apoio da EDISON, que desenvolve uma ampla gama de soluções para uma variedade de tarefas: desde programas para experimentação online de roupas de lojas multimarcas até um sistema de transmissão de LED entre embarcações fluviais e marítimas .

Adoramos a teoria dos algoritmos! ;-)
Para avaliar aproximadamente onde você deseja colocar o elemento, é necessário descobrir o quanto ele difere do elemento médio da matriz. Para fazer isso, você precisa conhecer os valores dos elementos mínimo e máximo e o tamanho da matriz.

A matriz classificada deve ter dados realmente aleatórios. Todos os inventores deste método vêm com a mesma fórmula:


k é o local aproximado da matriz em que o elemento A ( i ) deve ser
min , max - valores dos elementos mínimo e máximo na matriz A
Tamanho - o número de elementos na matriz A

Aqui está uma ideia tão geral. Vamos ver em quais variações esse algoritmo nasceu várias vezes.

Classificação Rei Salomão :: Ordem Salomão



Este método (e seu belo nome) foi proposto pelo usuário do V2008n há cerca de 5 anos. Tudo tem seu tempo, “o tempo de espalhar pedras e o tempo de coletar pedras” (as palavras do rei Salomão do livro de Eclesiastes) - e no algoritmo, é exatamente isso que acontece. Primeiro, com a ajuda da fórmula, dispersamos os elementos nos locais desejados na matriz. Como a fórmula fornece um lugar não exato, mas aproximado, vários elementos que se aproximam em valor reivindicam algumas posições ao mesmo tempo. Esses grupos de elementos locais são classificados por inserções e depois montados na ordem final.

Classificação de interpolação


"Não há nada novo sob o sol", para citar o mesmo autor novamente. A Wikipedia descreve a classificação por interpolação, reminiscentemente suspeita da classificação de Salomão. Cada "pilha de pedras" é uma pequena matriz dinâmica adicional, onde estão localizados elementos de importância semelhante. A principal diferença é que, após a “dispersão de pedras”, esses grupos locais de elementos não classificados são classificados não por inserções, mas pela classificação por interpolação (recursivamente ou em loop).

Uma matriz ordenada é um conjunto de dados discreto que pode ser considerado como um conjunto finito de valores conhecidos de uma determinada função desconhecida. Na verdade, uma distribuição aproximada do ponto de vista da matemática computacional - isso é interpolação.

Classificação de interpolação JavaScript - loopback
Array.prototype.interpolationSort = function() { var divideSize = new Array(); var end = this.length; divideSize[0] = end; while(divideSize.length > 0) {divide(this);} function divide(A) { var size = divideSize.pop(); var start = end - size; var min = A[start]; var max = A[start]; var temp = 0; for(var i = start + 1; i < end; i++) { if(A[i] < min) { min = A[i]; } else { if(A[i] > max) {max = A[i];} } } if(min == max) { end = end - size; } else { var p = 0; var bucket = new Array(size); for(var i = 0; i < size; i++) {bucket[i] = new Array();} for(var i = start; i < end; i++) { p = Math.floor(((A[i] - min) / (max - min)) * (size - 1)); bucket[p].push(A[i]); } for(var i = 0; i < size; i++) { if(bucket[i].length > 0) { for(var j = 0; j < bucket[i].length; j++) {A[start++] = bucket[i][j];} divideSize.push(bucket[i].length); } } } } }; 

Classificação de interpolação JavaScript - versão recursiva
 Array.prototype.bucketSort = function() { var start = 0; var size = this.length; var min = this[0]; var max = this[0]; for(var i = 1; i < size; i++) { if (this[i] < min) { min = this[i]; } else { if(this[i] > max) {max = this[i];} } } if(min != max) { var bucket = new Array(size); for(var i = 0; i < size; i++) {bucket[i] = new Array();} var interpolation = 0; for(var i = 0; i < size; i++){ interpolation = Math.floor(((this[i] - min) / (max - min)) * (size - 1)); bucket[interpolation].push(this[i]); } for(var i = 0; i < size; i++) { if(bucket[i].length > 1) {bucket[i].bucketSort();} //Recursion for(var j = 0; j < bucket[i].length; j++) {this[start++] = bucket[i][j];} } } }; 

Classificação de histograma :: Classificação de histograma


Essa é uma otimização da classificação por interpolação, que conta o número de elementos pertencentes a grupos não classificados locais. Essa contagem permite inserir itens não classificados diretamente na matriz resultante (em vez de agrupá-los em pequenas matrizes separadas).

Classificação da barra JavaScript
 Array.prototype.histogramSort = function() { var end = this.length; var sortedArray = new Array(end); var interpolation = new Array(end); var hitCount = new Array(end); var divideSize = new Array(); divideSize[0] = end; while(divideSize.length > 0) {distribute(this);} function distribute(A) { var size = divideSize.pop(); var start = end - size; var min = A[start]; var max = A[start]; for(var i = start + 1; i < end; i++) { if (A[i] < min) { min = A[i]; } else { if (A[i] > max) {max = A[i];} } } if (min == max) { end = end - size; } else { for(var i = start; i < end; i++){hitCount[i] = 0;} for(var i = start; i < end; i++) { interpolation[i] = start + Math.floor(((A[i] - min) / (max - min)) * (size - 1)); hitCount[interpolation[i]]++; } for(var i = start; i < end; i++) { if(hitCount[i] > 0){divideSize.push(hitCount[i]);} } hitCount[end - 1] = end - hitCount[end - 1]; for(var i = end - 1; i > start; i--) { hitCount[i - 1] = hitCount[i] - hitCount[i - 1]; } for(var i = start; i < end; i++) { sortedArray[hitCount[interpolation[i]]] = A[i]; hitCount[interpolation[i]]++; } for(var i = start; i < end; i++) {A[i] = sortedArray[i];} } } }; 

Classificação de tag de interpolação


Para otimizar ainda mais a sobrecarga, é proposto aqui não lembrar o número de elementos de importância semelhante em grupos não classificados, mas simplesmente marcar o início desses grupos com sinalizadores Verdadeiro / Falso. True significa que o subgrupo já está classificado e False significa que ainda não está.

Classificação de interpolação marcada com JavaScript
 Array.prototype.InterpolaionTagSort = function() { var end = this.length; if(end > 1) { var start = 0 ; var Tag = new Array(end); //Algorithm step-1 for(var i = 0; i < end; i++) {Tag[i] = false;} Divide(this); } //Algorithm step-2 while(end > 1) { while(Tag[--start] == false){} //Find the next bucket's start Divide(this); } function Divide(A) { var min = A[start]; var max = A[start]; for(var i = start + 1; i < end; i++) { if(A[i] < min) { min = A[i]; } else { if(A[i] > max ) {max = A[i];} } } if(min == max) { end = start; } else { //Algorithm step-3 Start to be the next bucket's end var interpolation = 0; var size = end - start; var Bucket = new Array(size);//Algorithm step-4 for(var i = 0; i < size; i++) {Bucket[i] = new Array();} for(var i = start; i < end; i++) { interpolation = Math.floor(((A[i] - min) / (max - min)) * (size - 1)); Bucket[interpolation].push(A[i]); } for(var i = 0; i < size; i++) { if(Bucket[i].length > 0) {//Algorithm step-5 Tag[start] = true; for(var j = 0; j < Bucket[i].length; j++) {A[start++] = Bucket[i][j];} } } } }//Algorithm step-6 }; 

Classificação de tag de interpolação (no local)


Se os valores dos elementos na matriz não forem repetidos e distribuídos uniformemente (grosso modo - se os dados na forma classificada forem algo como uma progressão aritmética), você poderá classificar em uma passagem, classificando no lugar certo, sem mover os elementos para matrizes intermediárias.

Classificar por interpolação com rótulos (no local) em JavaScript
 Array.prototype.InPlaceTagSort = function() { var n = this.length; var Tag = new Array(n); for(i = 0; i < n; i++) {Tag[i] = false;} var min = this[0]; var max = this[0]; for(i = 1; i < n; i++) { if(this[i] < min) { min = this[i]; } else { if(this[i] > max) {max = this[i];} } } var p = 0; var temp = 0; for(i = 0; i < n; i++) { while(Tag[i] == false) { p = Math.floor(((this[i] - min) / (max - min)) * (n - 1)); temp = this[i]; this[i] = this[p]; this[p] = temp; Tag[p] = true; } } }; 

Classificação do Flash :: Flashsort


Uma vez, escrevi sobre seleção, que foi inventada pelo professor de biofísica Neubert em 1998.

O professor sugeriu distribuir os elementos em várias classes separadas (a participação na classe é determinada pelo tamanho do elemento). Com isso em mente, a fórmula fica assim:


Em vez de Tamanho (tamanho da matriz), a fórmula indica m - o número de classes pelas quais distribuímos os elementos da matriz. A fórmula não calcula a chave na matriz em que o elemento deve ser lançado, mas o número da classe à qual o elemento pertence.

Essa classificação não é ruim, pois é mais econômica em termos de memória adicional. A redistribuição de elementos ocorre no local. Somente a localização de classes é armazenada separadamente (bem, se você olhar de um ângulo diferente, o número de elementos pertencentes a uma classe específica é armazenado separadamente).

Bem, o resto é a mesma música.


Classificação Flash em Java
 /** * FlashSort.java - integer version * Translation of Karl-Dietrich Neubert's algorithm into Java by * Rosanne Zhang */ class FlashSort { static int n; static int m; static int[] a; static int[] l; /* constructor @param size of the array to be sorted */ public static void flashSort(int size) { n = size; generateRandomArray(); long start = System.currentTimeMillis(); partialFlashSort(); long mid = System.currentTimeMillis(); insertionSort(); long end = System.currentTimeMillis(); // print the time result System.out.println("Partial flash sort time : " + (mid - start)); System.out.println("Straight insertion sort time: " + (end - mid)); } /* Entry point */ public static void main(String[] args) { int size = 0; if (args.length == 0) { usage(); System.exit(1); } try { size = Integer.parseInt(args[0]); } catch (NumberFormatException nfe) { usage(); System.exit(1); } FlashSort.flashSort(size); } /* Print usage */ private static void usage() { System.out.println(); System.out.println("Usage: java FlashSort n "); System.out.println(" n is size of array to sort"); } /* Generate the random array */ private static void generateRandomArray() { a = new int[n]; for(int i=0; i < n; i++) { a[i] = (int)(Math.random() * 5 * n); } m = n / 20; l = new int[m]; } /* Partial flash sort */ private static void partialFlashSort() { int i = 0, j = 0, k = 0; int anmin = a[0]; int nmax = 0; for(i=1; i < n; i++) { if (a[i] < anmin) anmin=a[i]; if (a[i] > a[nmax]) nmax=i; } if(anmin == a[nmax]) return; double c1 = ((double)m - 1) / (a[nmax] - anmin); for(i=0; i < n; i++) { k= (int) (c1 * (a[i] - anmin)); l[k]++; } for(k=1; k < m; k++) { l[k] += l[k - 1]; } int hold = a[nmax]; a[nmax] = a[0]; a[0] = hold; int nmove = 0; int flash; j = 0; k = m - 1; while(nmove < n - 1) { while(j > (l[k] - 1)) { j++; k = (int) (c1 * (a[j] - anmin)); } flash = a[j]; while(!(j == l[k])) { k = (int) (c1 * (flash - anmin)); hold = a[l[k] - 1]; a[l[k] - 1] = flash; flash = hold; l[k]--; nmove++; } } } /* Straight insertion sort */ private static void insertionSort() { int i, j, hold; for(i = a.length - 3; i >= 0; i--) { if(a[i + 1] < a[i]) { hold = a[i]; j = i; while (a[j + 1] < hold) { a[j] = a[j + 1]; j++; } a[j] = hold; } } } /* For checking sorting result and the distribution */ private static void printArray(int[] ary) { for(int i=0; i < ary.length; i++) { if((i + 1) % 10 ==0) { System.out.println(ary[i]); } else { System.out.print(ary[i] + " "); } System.out.println(); } } } 

Classificação aproximada :: classificação do Proxmap


Essa classificação é a mais antiga dentre as mencionadas aqui; foi introduzida em 1980 pelo professor Thomas Standish, da Universidade da Califórnia. Na aparência, parece ser significativamente diferente, mas se você olhar de perto, tudo será o mesmo.

O algoritmo opera com um conceito como um acerto - um determinado número que é próximo em valor a algum elemento da matriz.
Para determinar se um elemento da matriz é um acerto, uma função aproximada é aplicada ao elemento.

O professor Standish classificou matrizes de números reais. A função de aproximação era arredondar para baixo os números reais para um número inteiro.
Ou seja, por exemplo, se a matriz contiver elementos 2.8, 2, 2.1, 2.6 etc. então uma batida para esses números será deuce.



Procedimento geral:

  1. Aplicamos uma função aproximada a cada elemento, determinando qual ocorrência corresponde ao próximo elemento.
  2. Assim, para cada ocorrência, podemos calcular o número de elementos correspondentes a essa ocorrência.
  3. Sabendo o número de elementos para todos os hits, determinamos a localização dos hits (bordas à esquerda) na matriz.
  4. Conhecendo a localização dos hits, determinamos a localização de cada elemento.
  5. Depois de determinar a localização do elemento, tentamos inseri-lo em seu lugar na matriz. Se o local já estiver ocupado, movemos os vizinhos para a direita (se o elemento for menor que eles) para abrir espaço para o elemento. Ou, à direita, inserimos o próprio elemento (se for mais que vizinho).

Como uma função de aproximação, você pode atribuir qualquer um com base na natureza geral dos dados na matriz. Nas implementações modernas dessa classificação, as ocorrências geralmente são determinadas não mordiscando a parte fracionária, mas usando nossa fórmula favorita.

Classificação por aproximação do JavaScript
 Array.prototype.ProxmapSort = function() { var start = 0; var end = this.length; var A2 = new Array(end); var MapKey = new Array(end); var hitCount = new Array(end); for(var i = start; i < end; i++) {hitCount[i] = 0;} var min = this[start]; var max = this[start]; for (var i = start+1; i < end; i++){ if (this[i] < min) { min = this[i]; } else { if(this[i] > max) {max = this[i];} } } //Optimization 1.Save the MapKey[i]. for (var i = start; i < end; i++) { MapKey[i] = Math.floor(((this[i] - min ) / (max - min)) * (end - 1)); hitCount[MapKey[i]]++; } //Optimization 2.ProxMaps store in the hitCount. hitCount[end-1] = end - hitCount[end - 1]; for(var i = end-1; i > start; i--){ hitCount[i-1] = hitCount[i] - hitCount[i - 1]; } //insert A[i]=this[i] to A2 correct position var insertIndex = 0; var insertStart = 0; for(var i = start; i < end; i++){ insertIndex = hitCount[MapKey[i]]; insertStart = insertIndex; while(A2[insertIndex] != null) {insertIndex++;} while(insertIndex > insertStart && this[i] < A2[insertIndex - 1]) { A2[insertIndex] = A2[insertIndex - 1]; insertIndex--; } A2[insertIndex] = this[i]; } for(var i = start; i < end; i++) {this[i] = A2[i];} }; 

Classificação de inserção de classificação de hash :: Classificação de hash


Bem, encerraremos nossa análise com o algoritmo sugerido pelo habraiser bobbyKdas há 6 anos. Este é um algoritmo híbrido no qual, além da distribuição e inserções, a mesclagem também é adicionada.

  1. A matriz é recursivamente dividida ao meio, até que, em algum momento, o tamanho das meias-sub-matrizes atinja o tamanho mínimo (o autor não possui mais de 500 elementos).
  2. No nível mais baixo de recursão, um algoritmo familiar é aplicado a cada semi-matriz - usando a mesma fórmula, ocorre uma distribuição aproximada dentro da matriz, com a classificação por inserções de seções locais não classificadas.
  3. Após o arranjo das duas metades, subarrays, elas se fundem.
  4. O ponto 3 (mesclagem de metades-subarranjos classificados) é repetido ao elevar os níveis de recursão até o topo, quando a matriz original é combinada de duas metades.

Classificar por inserção de hash em Java
 import java.util.Arrays; import java.util.Date; import java.util.Random; public class HashSort { //    static int SOURCELEN = 1000000; int source[] = new int[SOURCELEN]; //        int quick[] = new int[SOURCELEN]; //     static int SORTBLOCK = 500; static int k = 3; //  static int TMPLEN = (SOURCELEN < SORTBLOCK * k) ? SORTBLOCK * k : SOURCELEN; int tmp[] = new int[TMPLEN]; //    static int MIN_VAL = 10; static int MAX_VAL = 1000000; int minValue = 0; int maxValue = 0; double hashKoef = 0; //      public void randomize() { int i; Random rnd = new Random(); for(i=0; i<SOURCELEN; i++) { int rndValue = MIN_VAL + ((int)(rnd.nextDouble()*((double)MAX_VAL-MIN_VAL))); source[i] = rndValue; } } //         - public void findMinMax(int startIndex, int endIndex) { int i; minValue = source[startIndex]; maxValue = source[startIndex]; for(i=startIndex+1; i<=endIndex; i++) { if( source[i] > maxValue) { maxValue = source[i]; } if( source[i] < minValue) { minValue = source[i]; } } hashKoef = ((double)(k-1)*0.9)*((double)(endIndex-startIndex)/((double)maxValue-(double)minValue)); } // (  - )      public void stickParts(int startIndex, int mediana, int endIndex) { int i=startIndex; int j=mediana+1; int k=0; //      -    while(i<=mediana && j<=endIndex) { if(source[i]<source[j]) { tmp[k] = source[i]; i++; } else { tmp[k] = source[j]; j++; } k++; } //     -      if( i>mediana ) { while(j<=endIndex) { tmp[k] = source[j]; j++; k++; } } //     -      if(j>endIndex) { while(i<=mediana) { tmp[k] = source[i]; i++; k++; } } System.arraycopy(tmp, 0, source, startIndex, endIndex-startIndex+1); } //        //         boolean shiftRight(int index) { int endpos = index; while( tmp[endpos] != 0) { endpos++; if(endpos == TMPLEN) return false; } while(endpos != index ) { tmp[endpos] = tmp[endpos-1]; endpos--; } tmp[endpos] = 0; return true; } //-    public int hash(int value) { return (int)(((double)value - (double)minValue)*hashKoef); } //        public void insertValue(int index, int value) { int _index = index; //  ,    //            - while(tmp[_index] != 0 && tmp[_index] <= value) { _index++; } //       ,    if( tmp[_index] != 0) { shiftRight(_index);//      } tmp[_index] = value;//  -   } //        public void extract(int startIndex, int endIndex) { int j=startIndex; for(int i=0; i<(SORTBLOCK*k); i++) { if(tmp[i] != 0) { source[j] = tmp[i]; j++; } } } //   public void clearTMP() { if( tmp.length < SORTBLOCK*k) { Arrays.fill(tmp, 0); } else { Arrays.fill(tmp, 0, SORTBLOCK*k, 0); } } //  public void hashingSort(int startIndex, int endIndex) { //1.          findMinMax(startIndex, endIndex); //2.    clearTMP(); //3.       - for(int i=startIndex; i<=endIndex; i++) { insertValue(hash(source[i]), source[i]); } //4.         extract(startIndex, endIndex); } //         public void sortPart(int startIndex, int endIndex) { //    500,   - if((endIndex - startIndex) <= SORTBLOCK ) { hashingSort(startIndex, endIndex); return; } //  > 500         int mediana = startIndex + (endIndex - startIndex) / 2; sortPart(startIndex, mediana);//    sortPart(mediana+1, endIndex);//    stickParts(startIndex, mediana, endIndex);//   -   } //       public void sort() { sortPart(0, SOURCELEN-1); } public static void main(String[] args) { HashSort hs = new HashSort(); hs.randomize(); hs.sort(); } } 

A própria fórmula é chamada de função hash, e a matriz auxiliar para distribuição aproximada é chamada de tabela hash.

Referências


Interpolação e Histograma , Flash , Proxmap

Salomão , Tabela De Hash , Flash

Artigos da série:



A classificação por aproximação apareceu no aplicativo AlgoLab Excel (nesse caso, na matriz inicial não classificada, a parte fracionária aleatória é anexada aos números inteiros). Solomon e Flash estão lá há muito tempo, mas ainda não implementaram interpolação, hash e histograma.

Source: https://habr.com/ru/post/pt478654/


All Articles