Visualizar Mesclar Classificação


30

A classificação por mesclagem é um algoritmo de classificação que funciona dividindo uma determinada lista pela metade, classificando recursivamente ambas as listas menores e juntando-as novamente em uma lista classificada. O caso base da recursão está chegando a uma lista de singleton, que não pode ser dividida ainda mais, mas já está classificada por definição.

A execução do algoritmo na lista [1,7,6,3,3,2,5]pode ser visualizada da seguinte maneira:

       [1,7,6,3,3,2,5]
       /             \      split
   [1,7,6,3]       [3,2,5]
    /     \        /    \   split
 [1,7]   [6,3]   [3,2]  [5]
 /   \   /   \   /   \   |  split
[1] [7] [6] [3] [3] [2] [5]
 \   /   \   /   \   /   |  merge
 [1,7]   [3,6]   [2,3]  [5]
    \     /         \   /   merge
   [1,3,6,7]       [2,3,5]
       \             /      merge
       [1,2,3,3,5,6,7]

A tarefa

Escreva um programa ou função que pegue uma lista de números inteiros de qualquer maneira razoável como entrada e visualize as diferentes partições dessa lista enquanto estiver sendo classificado por um algoritmo de classificação de mesclagem. Isso significa que você não precisa gerar um gráfico como acima, mas apenas as listas são boas:

[1,7,6,3,3,2,5]
[1,7,6,3][3,2,5]
[1,7][6,3][3,2][5]
[1][7][6][3][3][2][5]
[1,7][3,6][2,3][5]
[1,3,6,7][2,3,5]
[1,2,3,3,5,6,7]

Além disso, qualquer notação razoável da lista é adequada; portanto, o seguinte também seria uma saída válida:

1 7 6 3 3 2 5
1 7 6 3|3 2 5
1 7|6 3|3 2|5
1|7|6|3|3|2|5
1 7|3 6|2 3|5
1 3 6 7|2 3 5
1 2 3 3 5 6 7

Finalmente, a maneira de dividir uma lista em duas listas menores é com você, desde que o comprimento das duas listas resultantes seja diferente, no máximo, uma. Isso significa que, em vez de dividir [3,2,4,3,7]em [3,2,4]e [3,7], você também pode dividir usando elementos em índices pares e ímpares ( [3,4,7]e [2,3]) ou mesmo aleatoriamente a divisão toda vez.

Isso é , portanto o código mais curto em qualquer idioma medido em bytes vence.

Casos de teste

Como observado acima, o formato real e a maneira de dividir as listas pela metade são com você.

[10,2]
[10][2]
[2,10]

[4,17,1,32]
[4,17][1,32]
[4][17][1][32]
[4,17][1,32]
[1,4,17,32]

[6,5,4,3,2,1]
[6,5,4][3,2,1]
[6,5][4][3,2][1]
[6][5][4][3][2][1]
[5,6][4][2,3][1] <- Important: This step cannot be [5,6][3,4][1,2], because 3 and 4 are on different branches in the the tree
[4,5,6][1,2,3]
[1,2,3,4,5,6]

5
@dylnan Você pode usar outro algoritmo de classificação ou um construído em função de classificação para fazer a classificação ...
flawr

5
Alguma idéia de golfe: a metade inferior do resultado pode ser gerada ordenando cada sub-lista na primeira metade e revertendo a ordem.
JungHwan Min 15/12

2
@ Arnauld O [[1,2],[3],[4,5],[6]]estágio é realmente a solução correta, pois a classificação por mesclagem está funcionando recursivamente. Ou seja, se começarmos [1,2,3,4,5,6]e dividirmos em [1,2,3]e [4,5,6], essas listas serão processadas independentemente até serem mescladas na etapa final.
Laikoni

2
@ l4m2 Ok, tentativa final para uma resposta: 1. você precisa de delimitadores porque também números inteiros> 9 devem ser suportados. 2. Isso não é válido pelo mesmo motivo que foi mencionado no meu comentário acima. Se dividirmos em [3]e [2,1], então eles estarão em ramos diferentes, portanto não podemos mesclar [3]e [2]depois [2,1]dividir em [2]e [1].
Laikoni

1
De fato, a frase depois disso responde exatamente à minha pergunta. Desculpe por falta disso. : P
Zgarb 15/12

Respostas:


8

Haskell , 137 128 127 125 121 109 106 bytes

(-2) + (- 4) = (- 6) bytes graças a nimi ! Alterá-lo para coletar todas as etapas de uma lista (novamente devido ao nimi) economiza mais 12 bytes.

Outros 3 bytes devido a Laikoni , com ligação de guarda de padrão e um uso inteligente de compreensão de lista para codificar a guarda.

import Data.List
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]
h[a]=[[a]]
h x=foldr(\x[b,a]->[x:a,b])[[],[]]x

Experimente online!

Dividir as listas nos elementos ímpares e pares é um código mais curto do que nas duas metades seqüenciais, porque somos poupados da necessidade de medir o valor length.

Funciona "imprimindo" as listas, repetindo com as listas divididas ( x >>= h) se realmente houve alguma divisão, e "imprimindo" as listas classificadas; começando com a lista de uma entrada; assumindo a entrada não vazia. E, em vez da impressão real, basta coletá- los em uma lista.

A lista produzida por g[[6,5..1]], impressa linha por linha, é:

[[6,5,4,3,2,1]]
[[6,4,2],[5,3,1]]
[[6,2],[4],[5,1],[3]]
[[6],[2],[4],[5],[1],[3]]
[[2,6],[4],[1,5],[3]]
[[2,4,6],[1,3,5]]
[[1,2,3,4,5,6]]

1
... p=printe três vezes p. Experimente online!
nimi

@ Nimi ótimo, mais uma vez, e muito obrigado! agora parece realmente jogado . :)
Will Ness

Em vez de imprimir na função, gvocê pode coletar todas as etapas de uma lista e devolvê-la. Experimente online!
nimi

3
Não acho que tenhamos uma definição adequada de "visualização". De maneira mais geral, os desafios solicitam a "saída" das listas e, por nossos padrões, isso pode ser feito através do valor de retorno de uma função . Outras respostas, por exemplo, 1 , 2 também são assim. - Não acho que minha sugestão seja muito diferente, apenas coleta as listas intermediárias em vez de imprimi-las. Sinta-se à vontade para editá-lo.
nimi

3
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]]economiza mais alguns bytes.
Laikoni

7

Wolfram Language (Mathematica) , 146 127 111 102 bytes

Join[u=Most[#/.a:{_,b=__Integer}:>TakeDrop[a,;;;;2]&~FixedPointList~#],Reverse@Most@u/.a:{b}:>Sort@a]&

Experimente online!

Retorna um Listque contém as etapas.

Explicação

#/.a:{_,b=__Integer}:>TakeDrop[a,;;;;2]&

Em uma entrada, divida todos os Lists contendo 2 ou mais números inteiros ao meio. A primeira sub-lista possui os elementos indexados por ímpares (indexados a 1) e o segundo, os elementos indexados por pares.

u=Most[... &~FixedPointList~#]

Repita isso até que nada mude (ou seja, todas as sublistas têm o comprimento 1). Mantenha todos os resultados intermediários. Armazene isso (a Listde todas as etapas) em u.

Reverse@Most@u

Solte o último elemento de ue inverta-o.

... /.a:{b}:>Sort@a

A partir do resultado acima, classifique todas as ocorrências de uma lista de números inteiros.


6

Limpo , 228 206 168 157 140 121 104 bytes

Constrói a lista de estágios do fim para dentro, usando o fato de que o n-ésimo elemento do final é a versão classificada do n-ésimo elemento desde o início.

Ideia do comentário de JungHwan Min

import StdEnv
@u#(a,b)=splitAt(length u/2)u
=if(a>[])[[u]:[x++y\\y<- @b&x<- @a++ @a++ @a]][]++[[sort u]]

Experimente online!


4

Carvão , 145 133 123 122 122 bytes

≔⟦⪪S ⟧θW⊖L§θ⁰«⟦⪫Eθ⪫κ ¦|⟧≔EE⊗Lθ§θ÷λ²✂κ﹪λ²Lκ²θ»⟦⪫ΦEθ⪫ι ι|⟧W⊖Lθ«≔⮌θη≔⟦⟧θWη«≔⊟ηε≔⟦⟧ζF⊟η«≔εδ≔⟦⟧εFδ⊞⎇‹IμIλζεμ⊞ζλ»⊞θ⁺ζε»⟦⪫Eθ⪫κ ¦|

Experimente online! Link é a versão detalhada do código. Ainda tendo que contornar os erros do carvão vegetal ... Edit: Salvo 5 bytes, usando um duplo Mapcomo compreensão de matriz de um pobre. Salva 4 bytes usando Poppara duplicar a iteração sobre uma matriz. Salva 3 bytes usando concatenação em vez de Push. Economizou 10 bytes ao criar uma whilecondição de golfista que também evita um bug no carvão. Economizou 1 byte ao descobrir que o carvão vegetal realmente possui um operador de filtro. Explicação:

≔⟦⪪S ⟧θ

Divida a entrada em espaços e, em seguida, agrupe o resultado em uma matriz externa, salvando isso em q.

W⊖L§θ⁰«

Repita enquanto o primeiro elemento de qpossui mais de um elemento. (O primeiro elemento de qsempre tem mais elementos devido à maneira como as listas são divididas em duas.)

⟦⪫Eθ⪫κ ¦|⟧

Imprima os elementos de qunião com espaços e linhas verticais. (A matriz faz com que o resultado seja impresso em sua própria linha. Existem outras maneiras de conseguir isso para a mesma contagem de bytes.)

≔EE⊗Lθ§θ÷λ²✂κ﹪λ²Lκ²θ»

Crie uma lista duplicando cada elemento de q, em seguida, mapeie essa lista e pegue metade de cada lista (usando a abordagem de elemento alternativo), salvando o resultado novamente q.

⟦⪫ΦEθ⪫ι ι|⟧

Imprima os elementos de qunião com espaços e linhas verticais. Na verdade, neste ponto, os elementos de qsão listas vazias ou de elemento único; portanto, juntá-los apenas os converte em cadeias vazias ou em seus elementos. As cadeias vazias adicionariam linhas finais desnecessárias para que sejam filtradas. Um operador de nivelamento teria sido mais golfista (algo como ⟦⪫Σθ|⟧).

W⊖Lθ«

Repita enquanto qtiver mais de um elemento. (O código a seguir requer um número par de elementos.)

≔⮌θη≔⟦⟧θ

Copie qpara h, mas invertido (veja abaixo) e redefina qpara uma lista vazia.

Wη«

Repita até hficar vazio.

≔⊟ηε

Extraia o próximo elemento de hem e. ( Popextratos do final, e é por isso que eu tenho que reverter q.)

≔⟦⟧ζ

Inicialize zpara uma lista vazia.

F⊟η«

Faça um loop sobre os elementos do próximo elemento de h.

≔εδ≔⟦⟧ε

Copie ea de reponha ea uma lista vazia.

Fδ

Loop sobre os elementos de d.

⊞⎇‹IμIλζεμ

Empurre-os para zou edependendo se eles são menores que o elemento atual do próximo elemento de h.

⊞ζλ»

Empurre o elemento atual do próximo elemento de hpara z.

⊞θ⁺ζε»

Concatene zcom todos os elementos restantes ee empurre para q. Isso completa a mesclagem de dois elementos de h.

⟦⪫Eθ⪫κ ¦|

Imprima os elementos de qunião com espaços e linhas verticais.


Espere. Há outro bug? : /
ASCII-only

@ Somente ASCII Não, esse foi o while (...Map(...)...)bug que eu já falei.
Neil


2

JavaScript (ES6), 145 bytes

f=a=>a.join`|`+(a[0][1]?`
${f([].concat(...a.map(b=>b[1]?[b.slice(0,c=-b.length/2),b.slice(c)]:[b])))}
`+a.map(b=>b.sort((x,y)=>x-y)).join`|`:``)

Recebe entrada como uma matriz dentro de uma matriz, ou seja f([[6,5,4,3,2,1]]). Funciona gerando a primeira e a última linha da saída, dividindo-se e chamando a si mesma novamente, até que cada sub-matriz tenha o comprimento 1. Aqui está uma demonstração básica de como funciona:

f([[6,5,4,3,2,1]]):
  6,5,4,3,2,1
  f([[6,5,4],[3,2,1]]):
    6,5,4|3,2,1
    f([[6,5],[4],[3,2],[1]]):
      6,5|4|3,2|1
      f([[6],[5],[4],[3],[2],[1]]):
        6|5|4|3|2|1
      end f
      5,6|4|2,3|1
    end f
    4,5,6|1,2,3
  end f
  1,2,3,4,5,6
end f

2
Então, houve um ponto em que havia três respostas vinculadas em 145 bytes?
Neil

2

Casca , 14 bytes

S+ȯ†O↔hUmfL¡ṁ½

Toma uma matriz que contém uma única matriz. Experimente online!

Explicação

S+ȯ†O↔hUmfL¡ṁ½  Implicit input, say A = [[4,17,32,1]].
           ¡    Iterate this function on A:
            ṁ½   Split each array in two, concatenate results: [[4,17],[32,1]]
                Result is [[[4,17,32,1]],
                           [[4,17],[32,1]],
                           [[4],[17],[32],[1]],
                           [[4],[],[17],[],[32],[],[1],[]],
                           ...
        mfL     Map filter by length, removing empty arrays.
                Result is [[[4,17,32,1]],
                           [[4,17],[32,1]],
                           [[4],[17],[32],[1]],
                           [[4],[17],[32],[1]],
                           ...
       U        Longest prefix of unique elements:
                       P = [[[4,17,32,1]],[[4,17],[32,1]],[[4],[17],[32],[1]]]
      h         Remove last element: [[[4,17,32,1]],[[4,17],[32,1]]]
     ↔          Reverse: [[[4,17],[32,1]],[[4,17,32,1]]]
   †O           Sort each inner array: [[[4,17],[1,32]],[[1,4,17,32]]]
S+ȯ             Concatenate to P:
                           [[[4,17,32,1]],
                            [[4,17],[32,1]],
                            [[4],[17],[32],[1]],
                            [[4,17],[1,32]],
                            [[1,4,17,32]]]
                Implicitly print.

O built-in ½pega uma matriz e divide-a no meio. Se seu comprimento for ímpar, a primeira parte será mais longa por um elemento. Uma matriz singleton [a]resulta em [[a],[]]e uma matriz vazia []fornece [[],[]], portanto é necessário remover as matrizes vazias antes de aplicar U.


1

Stax , 116 (÷ 3>) 38 29 bytes CP437

-9 bytes por comentário de @recursive. Agora a entrada é fornecida como um singleton cujo único elemento é uma matriz dos números a serem classificados.

ƒ3s}óºE/ßB╢↕êb∩áαπüµrL╞¶è,te+

Experimente online!

Versão descompactada com 35 bytes:

{c{Jm'|*Pc{2Mm:f{fc{Dm$wW{{eoJm'|*P

Explicação

O código pode ser dividido em duas partes. A primeira parte visualiza a divisão e a segunda visualiza a mesclagem.

Código para visualizar a divisão:

{                      w    Do the following while the block generates a true value
 c                          Copy current nested array for printing
  {Jm                       Use spaces to join elements in each part
     '|*                    And join different parts with vertical bar 
        P                   Pop and print

         c                  Copy current nested array for splitting
          {2Mm              Separate each element of the array to two smaller parts with almost the same size
                                That is, if the number of elements is even, partition it evenly.
                                Otherwise, the first part will have one more element than the second.
              :f            Flatten the array once
                {f          Remove elements that are empty arrays

                  c         Copy the result for checking 
                   {Dm$     Is the array solely composed of singletons?
                            If yes, ends the loop.

O código para visualizar a mesclagem:

W              Execute the rest of the program until the stack is empty
 {{eoJm        For each part, sort by numeric value, then join with space
       '|*     Join the parts with vertical bar
          P    Pop and print the result

Versão antiga, na verdade construindo a estrutura de lista aninhada.

{{cc0+=!{x!}Mm',*:}}Xd;%v:2^^{;s~{c^;<~sc%v,*{2M{s^y!svsm}M}YZ!x!Q,dmU@e;%v:2^{;%v:2^-N~0{c;={scc0+=Cc%v!C:f{o}{scc0+=C{s^y!svsm}?}Y!cx!P,dcm

cc0+= é usado três vezes no código para verificar se o topo da pilha é um escalar ou uma matriz.

{{cc0+=!{x!}Mm',*:}}Xcria um bloco que se chama recursivamente para gerar uma matriz aninhada de números corretamente. (A saída padrão no Stax vetoriza uma matriz aninhada antes da impressão).

{                  }X    Store the code block in X
 {           m           Map each element in the list with block
  cc                     Make two copies of the element
    0+                   + 0. If the element is a scalar, nothing will change
                              If the element is an array, the element 0 will be appended
      =!{  }M            If the element has changed after the `0+` operation
                             Which means it is an array
         x!              Recursively execute the whole code block on the element

              ',*        Join the mapped elements with comma
                 :}      Bracerizes the final result

Existem outros dois blocos que executam a divisão e a mesclagem, respectivamente. Eles são muito detalhados e eu não me importo de explicar (há um pouco mais de informações na versão histórica deste post, mas não esperamos muito).


Melhoria muito boa. Ainda não o entendo totalmente, mas acho que cH!pode ser usado no lugar de cH%!.
recursivo

{Nd}Mpode ser substituído por Ttambém.
recursivo

Fiz mais algumas adaptações. staxlang.xyz/… A resposta Husk recebe entrada como uma matriz em uma matriz, então acho que isso é legal.
recursivo

Encontrei uma solução que deveria ter 2 caracteres ASCII mais curtos, mas descobri um bug na transposição da matriz. Especificamente, ele modifica as linhas da matriz. Vou adicioná-lo ao backlog para 1.0.4
recursivo

ESTÁ BEM. Aguardo com expectativa a atualização.
Weijun Zhou
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.