Existe uma maneira de medir a classificação de uma lista?

161

Existe uma maneira de medir a classificação de uma lista?

Quero dizer, não se trata de saber se uma lista está classificada ou não (booleana), mas algo como uma proporção de "gentileza", algo como o coeficiente de correlação nas estatísticas.

Por exemplo,

  • Se os itens de uma lista estiverem em ordem crescente, sua taxa será 1,0

  • Se a lista for classificada como decrescente, sua taxa será -1,0

  • Se a lista estiver quase classificada em ordem crescente, sua taxa seria 0,9 ou algum valor próximo a 1.

  • Se a lista não for classificada (aleatoriamente), sua taxa será próxima de 0

Estou escrevendo uma pequena biblioteca em Scala para praticar. Acho que uma taxa de classificação seria útil, mas não encontro informações sobre algo assim. Talvez eu não conheça termos adequados para o conceito.

Josell
fonte
4
Isso seria usado para determinar o algoritmo ideal para classificar a lista? Por exemplo, para valores próximos a 0, o QuickSort seria o ideal, mas, em ambos os extremos da escala (quase ordenados ou quase reversos), o MergeSort seria muito mais rápido, pois o QC volta para O (N ^ 2) nesses casos.
Darrel Hoffman
8
+1 para "proporção de sortes"
0x499602D2 08/06
1
@ Fuhrmanator A versão estocástica do algoritmo não precisa executar uma classificação para chegar a uma estimativa probabilística da classificação. Somente se você deseja obter uma medida exata é necessário executar uma classificação.
Timothy Shields
1
Primeiro instinto sarcástico, mas engraçado: você pode inserir por ordem a lista e ver quanto tempo leva e depois comparar com o tempo que leva para classificar a lista (agora classificada) e o inverso dela.
kqr

Respostas:

142

Você pode simplesmente contar o número de inversões na lista.

Inversão

Uma inversão em uma sequência de elementos do tipo Té um par de elementos de sequência que aparecem fora de ordem, de acordo com algumas ordens <no conjunto de T's.

Da Wikipedia :

Formalmente, A(1), A(2), ..., A(n)seja uma sequência de nnúmeros.
Se i < je A(i) > A(j), então o par (i,j)é chamado de inversão de A.

O número de inversão de uma sequência é uma medida comum de sua ordenação.
Formalmente, o número de inversão é definido como o número de inversões, ou seja,

definição

Para tornar essas definições mais claras, considere a sequência de exemplo 9, 5, 7, 6. Esta sequência possui as inversões (0,1), (0,2), (0,3), (2,3) e o número de inversão 4 .

Se você deseja um valor entre 0e 1, pode dividir o número de inversão por N choose 2.

Para realmente criar um algoritmo para calcular essa pontuação pela classificação de uma lista, você tem duas abordagens:

Abordagem 1 (determinística)

Modifique seu algoritmo de classificação favorito para acompanhar quantas inversões está corrigindo à medida que é executado. Embora isso não seja trivial e tenha implementações variadas, dependendo do algoritmo de classificação escolhido, você terminará com um algoritmo que não é mais caro (em termos de complexidade) do que o algoritmo de classificação com o qual você iniciou.

Se você seguir esse caminho, saiba que não é tão simples quanto contar "trocas". O mergesort, por exemplo, é o pior caso O(N log N), mas se for executado em uma lista classificada em ordem decrescente, ele corrigirá todas as N choose 2inversões. Isso é O(N^2)inversões corrigidas nas O(N log N)operações. Portanto, algumas operações devem inevitavelmente corrigir mais de uma inversão de cada vez. Você precisa ter cuidado com sua implementação. Nota: você pode fazer isso com O(N log N)complexidade, é apenas complicado.

Relacionado: calculando o número de "inversões" em uma permutação

Abordagem 2 (Estocástica)

  • Amostra aleatória de pares (i,j), ondei != j
  • Para cada par, determine se list[min(i,j)] < list[max(i,j)](0 ou 1)
  • Calcule a média dessas comparações e depois normalize por N choose 2

Eu pessoalmente adotaria a abordagem estocástica, a menos que você exija uma exigência de exatidão - mesmo que seja tão fácil de implementar.


Se o que você realmente deseja é um valor ( z') entre -1(classificado como decrescente) e 1(classificado como crescente), você pode simplesmente mapear o valor acima ( z), que está entre 0(classificado como crescente) e 1(classificado como decrescente), para esse intervalo usando esta fórmula :

z' = -2 * z + 1
Timothy Shields
fonte
2
É meio fascinante para mim que classificar uma lista seja (normalmente) O (n * logn), e o método ingênuo / óbvio de calcular inversões seja O (n ^ 2). Gostaria de saber se existem algoritmos melhores por aí para calcular o número de inversões?
Mark Bessey
5
Existem algumas abordagens interessantes nessa questão do SO: stackoverflow.com/questions/6523712/… Basicamente, elas equivalem à classificação da matriz para descobrir quantas inversões existem.
Mark Bessey
4
Ingenuamente, pensei que você poderia contar pares adjacentes que estão fora de ordem. Mas isso terá uma subcontagem severa: 1 2 3 1 2 3 possui apenas uma inversão adjacente, mas é 50% invertida pela medida mais correta.
Barmar
2
@Barmar Eu acho que essa lista 1 2 3 1 2 3 se possa qualificar como sorta classificadas ;-)
scunliffe
2
@ TimothyShields, bem, não, não é. Mas não vou discutir o assunto. Apenas uma sugestão para adicionar uma definição não formal mais acessível aos menos inclinados a simbolizar.
precisa
24

A medida tradicional de como uma lista é classificada (ou outra estrutura seqüencial) é o número de inversões.

O número de inversões é o número de pares (a, b) st índice de a <b AND b <<a. Para esses fins, <<representa qualquer relação de pedido que você escolher para seu tipo específico.

Uma lista totalmente classificada não possui inversões e uma lista completamente revertida possui o número máximo de inversões.

Marcin
fonte
5
Tecnicamente, 5 4 3 2 1é totalmente classificado, uma vez que a ordem não está especificada, mas estou sendo pedante :-) #
388
7
@paxdiablo Isso depende da definição de <.
Marcin
@paxdiablo, bem, é possível medir a classificação pela distância entre o número de inversões e o mais próximo de 0 ou n choose 2.
huon
17

Você pode usar correlação real.

Suponha que, para cada item da lista classificada, você atribua uma classificação inteira começando do zero. Observe que um gráfico do índice de posição dos elementos versus classificação se parecerá com pontos em uma linha reta (correlação de 1,0 entre a posição e a classificação).

Você pode calcular uma correlação com esses dados. Para uma classificação inversa, você receberá -1 e assim por diante.

Kaz
fonte
1
Sinto muito, mas isso deixa muito inexplicável, como você atribui os números inteiros.
Marcin
2
Você precisa da lista classificada para atribuir os números inteiros; então é apenas uma enumeração dos itens.
Kaz
1
Exatamente o que eu ia sugerir. Determine a correlação entre a posição do objeto na lista original e sua posição na lista classificada. A má notícia é que as rotinas de correlação provavelmente são executadas em O (n ^ 2); a boa notícia é que eles provavelmente estão disponíveis para o seu ambiente.
Peter Webb
2
Sim, apenas rho de Spearman pt.wikipedia.org/wiki/…
Lucas
Estou curioso ... essa abordagem é equivalente a escalar a contagem do número de inversões?
precisa
4

Houve ótimas respostas, e eu gostaria de acrescentar um aspecto matemático para completar:

  • Você pode medir a classificação de uma lista, medindo o quanto ela está correlacionada a uma lista classificada. Para fazer isso, você pode usar a correlação de classificação (a mais conhecida é a de Spearman ), que é exatamente igual à correlação usual, mas usa a classificação de elementos em uma lista em vez dos valores analógicos de seus itens.

  • Existem muitas extensões, como um coeficiente de correlação (+1 para classificação exata, -1 para inversão exata)

  • Isso permite que você tenha propriedades estatísticas para essa medida, como o teorema do limite central permutacional, que permite conhecer a distribuição dessa medida para listas aleatórias.

meduz
fonte
3

Além da contagem de inversões, para listas numéricas, é possível imaginar a distância quadrada média do estado classificado:

#! ruby
d = -> a { a.zip( a.sort ).map { |u, v| ( u - v ) ** 2 }.reduce( :+ ) ** 0.5 }

a = 8, 7, 3, 4, 10, 9, 6, 2, 5, 1
d.( a ) #=> 15.556
d.( a.sort ) #=> 0.0
d.( a.sort.reverse ) # => 18.166 is the worrst case
Boris Stitnicky
fonte
Eu acho que esse é o quadrado da função de correlação padrão, consulte en.wikipedia.org/wiki/Correlation_ratio . E se aplica igualmente a listas não numéricas; os dois valores comparados são a posição do objeto nas duas listas.
Peter Webb
Eu sou um simplório. Eu nem sei o que é razão de correlação. Quando leio esse artigo da Wikipedia, bem no topo, sou convidado a aprender o que é "dispersão estatística", depois "desvio padrão", depois "variação" e, em seguida, "coeficiente de correlação entre classes". Aprendi tudo isso várias vezes e várias vezes esqueci novamente. Nesta minha resposta pragmática, simplesmente medo a distância entre os dois vetores com o teorema de Pitágoras, que me lembro da escola primária, é tudo.
Boris Stitnicky
1

Não tenho certeza do método "melhor", mas um método simples seria comparar todos os elementos com o que se segue, incrementando um contador se element2> elemento 1 (ou o que você deseja testar) e depois dividir pelo número total de elementos. Deve lhe dar uma porcentagem.

user2369405
fonte
1

Eu contaria comparações e dividiria para o número total de comparações. Aqui está um exemplo simples do Python .

my_list = [1,4,5,6,9,-1,5,3,55,11,12,13,14]

right_comparison_count = 0

for i in range(len(my_list)-1):
    if my_list[i] < my_list[i+1]: # Assume you want to it ascending order
        right_comparison_count += 1

if right_comparison_count == 0:
    result = -1
else:
    result = float(right_comparison_count) / float((len(my_list) - 1))

print result
ibrahim
fonte
0

Que tal algo como isso?

#!/usr/bin/python3

def sign(x, y):
   if x < y:
      return 1
   elif x > y:
      return -1
   else:
      return 0

def mean(list_):
   return float(sum(list_)) / float(len(list_))

def main():
   list_ = [ 1, 2, 3, 4, 6, 5, 7, 8 ]
   signs = []
   # this zip is pairing up element 0, 1, then 1, 2, then 2, 3, etc...
   for elem1, elem2 in zip(list_[:-1], list_[1:]):
      signs.append(sign(elem1, elem2))

   # This should print 1 for a sorted list, -1 for a list that is in reverse order
   # and 0 for a run of the same numbers, like all 4's
   print(mean(signs))

main()
Dstromberg
fonte
2
Isso conta apenas inversões adjacentes. Se você olhar para as outras respostas, verá que isso é insuficiente.
Konrad Rudolph
1
@ KonradRudolph: Penso que esta resposta satisfaz a pergunta que foi feita. O fato de outras respostas serem mais abrangentes não significa que essa seja insuficiente; isso depende dos requisitos do OP.
Larsh
0

Se você pegar sua lista, calcular as classificações dos valores nessa lista e chamar a lista de classificações Ye outra lista, Xque contém os números inteiros de 1até length(Y), poderá obter exatamente a medida de classificação que está procurando, calculando o coeficiente de correlação , rentre as duas listas.

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}} 

Para uma lista totalmente classificada ,, r = 1.0para uma lista classificada inversa r=-1.0, e a rvaria entre esses limites para graus variados de classificação.

Um possível problema com essa abordagem, dependendo do aplicativo, é que calcular a classificação de cada item na lista é equivalente a classificá-lo, portanto, é uma operação O (n log n).

Simon
fonte
Mas isso não ignorará a forma da curva. Se sua matriz for classificada, mas, digamos, contiver valores aumentando exponencialmente, a correlação será pequena onde ele deseja que seja 1,0.
Daniel Daniel Crocker
@LeeDanielCrocker: Sim, esse é um bom argumento. Eu alterei minha resposta para resolver isso, classificando os valores.
Simon