Visualizar Combinar Ordenar

30

La ordenación por fusión es un algoritmo de ordenación que funciona dividiendo una lista dada por la mitad, ordenando recursivamente ambas listas más pequeñas y fusionándolas nuevamente en una lista ordenada. El caso base de la recursión es llegar a una lista singleton, que no se puede dividir más, pero por definición ya está ordenada.

La ejecución del algoritmo en la lista [1,7,6,3,3,2,5]se puede visualizar de la siguiente manera:

       [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]

La tarea

Escriba un programa o función que tome una lista de enteros de cualquier manera razonable como entrada y visualice las diferentes particiones de esta lista mientras se ordena mediante un algoritmo de clasificación de fusión. Esto significa que no tiene que generar un gráfico como el anterior, pero solo las listas están bien:

[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]

Además, cualquier notación de lista razonable está bien, por lo tanto, lo siguiente también sería una salida 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, la forma de dividir una lista en dos listas más pequeñas depende de usted, siempre que la longitud de ambas listas resultantes difiera como máximo en una. Eso significa que, en lugar de dividirse [3,2,4,3,7]en [3,2,4]y [3,7], también podría dividirse tomando elementos en índices pares e impares ( [3,4,7]y [2,3]) o incluso aleatorizar la división cada vez.

Este es el , por lo que gana el código más corto en cualquier idioma medido en bytes.

Casos de prueba

Como se señaló anteriormente, el formato real y la forma de dividir las listas a la mitad depende de usted.

[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]
Laikoni
fuente
55
@dylnan Puede utilizar otro algoritmo de clasificación o una construida en función de clasificación para realizar la ordenación ...
flawr
55
Alguna idea de golf: la mitad inferior del resultado se puede generar ordenando cada sublista en la primera mitad e invirtiendo el orden.
JungHwan Min
2
@Arnauld La [[1,2],[3],[4,5],[6]]etapa es en realidad la solución correcta, ya que la ordenación por fusión funciona de forma recursiva. Es decir, si comenzamos [1,2,3,4,5,6]y lo dividimos en [1,2,3]y [4,5,6], esas listas se procesan de forma independiente hasta que se fusionen en el paso final.
Laikoni
2
@ l4m2 Ok, último intento para obtener una respuesta: 1. necesita delimitadores porque también se deben admitir enteros> 9. 2. Esto no es válido por la misma razón que se indica en mi comentario anterior. Si nos dividimos en [3]y [2,1], entonces están en diferentes ramas, por lo que no podemos fusionar [3]y [2]luego [2,1]se divide en [2]y [1].
Laikoni
1
De hecho, la siguiente oración responde exactamente a mi pregunta. Perdón por perderte eso. : P
Zgarb

Respuestas:

8

Haskell , 137 128 127 125 121 109 106 bytes

(-2) + (- 4) = (- 6) bytes gracias a nimi ! Cambiarlo para recopilar todos los pasos en una lista (nuevamente debido a nimi) ahorra 12 bytes más.

Otros 3 bytes debido a Laikoni , con enlace de protección de patrón y un uso inteligente de la comprensión de la lista para codificar la protección.

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

Pruébalo en línea!

Dividir las listas en los elementos pares e impares es un código más corto que en las dos mitades secuenciales, porque nos ahorramos tener que medir el length, entonces.

Funciona "imprimiendo" las listas, luego recurriendo con las listas divididas ( x >>= h) si realmente se realizó una división e "imprimiendo" las listas ordenadas; comenzando con la lista de una entrada; asumiendo la entrada no vacía. Y en lugar de la impresión real, simplemente recopilarlos en una lista.

La lista producida por g[[6,5..1]], impresa línea por línea, es:

[[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]]
Will Ness
fuente
1
... p=printy tres veces p. Pruébalo en línea!
nimi
@nimi genial, de nuevo, y muchas gracias! ahora realmente se ve golfizado . :)
Will Ness
En lugar de imprimir dentro de la función g, puede recopilar todos los pasos de una lista y devolverlos. Pruébalo en línea!
nimi
3
No creo que tengamos una definición adecuada de "visualización". En términos más generales, los desafíos requieren "generar" las listas y, según nuestros valores predeterminados, esto se puede hacer mediante el valor de retorno de una función . Otras respuestas, por ejemplo, 1 , 2 , también lo hacen de esta manera. - No creo que mi sugerencia sea tan diferente, solo recopila las listas intermedias en lugar de imprimirlas. Siéntase libre de editarlo en.
nimi
3
g x|y<-x>>=h=x:[z|x/=y,z<-g y++[sort<$>x]] Guarda algunos bytes más.
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]&

Pruébalo en línea!

Devuelve un Listque contiene los pasos.

Explicación

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

En una entrada, divida todos los Lists que contengan 2 o más enteros por la mitad. La primera sublista tiene los elementos indexados impares (1 indexado), y el segundo tiene los elementos indexados pares.

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

Repita eso hasta que nada cambie (es decir, todas las sublistas son de longitud 1). Mantener todos los resultados intermedios. Almacene esto (el Listde todos los pasos) en u.

Reverse@Most@u

Suelta el último elemento de ue inviértelo.

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

Del resultado anterior, ordene todas las apariciones de una lista de enteros.

JungHwan Min
fuente
6

Limpias , 228 206 168 157 140 121 104 bytes

Construye la lista de etapas desde los extremos hacia adentro, utilizando el hecho de que el nelemento -th desde el final es la versión ordenada del nelemento -th desde el principio.

Idea del comentario 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]]

Pruébalo en línea!

Οurous
fuente
4

De carbón , 145 133 123 122 bytes

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

Pruébalo en línea! El enlace es a la versión detallada del código. Todavía tengo que trabajar con los errores del carbón ... Editar: ahorré 5 bytes usando un doble Mapcomo la comprensión de la matriz de un hombre pobre. Se guardaron 4 bytes al usar Poppara iterar dos veces sobre una matriz. Guardado 3 bytes mediante el uso de concatenación en lugar de Push. Ahorró 10 bytes al llegar a una whilecondición de golfista que también evita un error de carbón. Se guardó 1 byte al descubrir que el carbón tiene un operador de filtro. Explicación:

≔⟦⪪S ⟧θ

Divida la entrada en espacios y luego ajuste el resultado en una matriz externa, guardándolo en q.

W⊖L§θ⁰«

Repita mientras el primer elemento de qtiene más de un elemento. (El primer elemento de qsiempre tiene la mayoría de los elementos debido a la forma en que las listas se dividen en dos).

⟦⪫Eθ⪫κ ¦|⟧

Imprima los elementos de qunidos con espacios y líneas verticales. (La matriz hace que el resultado se imprima en su propia línea. Hay otras formas de lograr esto para el mismo recuento de bytes).

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

Cree una lista duplicando cada elemento de q, luego asigne esa lista y tome la mitad de cada lista (utilizando el enfoque de elemento alternativo), guardando el resultado nuevamente q.

⟦⪫ΦEθ⪫ι ι|⟧

Imprima los elementos de qunidos con espacios y líneas verticales. En realidad, en este punto, los elementos qson listas vacías o de un solo elemento, por lo que unirlos simplemente los convierte en cadenas vacías o sus elementos. Las cadenas vacías agregarían líneas finales innecesarias para que se filtren. Sin embargo, un operador de aplanamiento habría sido más golfista (algo así como ⟦⪫Σθ|⟧).

W⊖Lθ«

Repita mientras qtenga más de un elemento. (El siguiente código requiere un número par de elementos).

≔⮌θη≔⟦⟧θ

Copiar qa h, pero invertido (ver más abajo), y restablecer qa una lista vacía.

Wη«

Repita hasta que hesté vacío.

≔⊟ηε

Extrae el siguiente elemento de hinto e. ( Popextractos del final, por eso tengo que revertir q).

≔⟦⟧ζ

Inicializar za una lista vacía.

F⊟η«

Pase sobre los elementos del siguiente elemento de h.

≔εδ≔⟦⟧ε

Copiar ea dy restablecer ea una lista vacía.

Fδ

Recorrer los elementos de d.

⊞⎇‹IμIλζεμ

Empújelos hacia zo edependiendo de si son más pequeños que el elemento actual del siguiente elemento de h.

⊞ζλ»

Empuje el elemento actual del siguiente elemento de hto z.

⊞θ⁺ζε»

Concatene zcon cualquier elemento restante ey empuje eso hacia q. Esto completa la fusión de dos elementos de h.

⟦⪫Eθ⪫κ ¦|

Imprima los elementos de qunidos con espacios y líneas verticales.

Neil
fuente
Aférrate. Hay otro error? : /
Solo ASCII
@ Solo ASCII No, este fue el while (...Map(...)...)error del que ya te hablé.
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`|`:``)

Toma la entrada como una matriz dentro de una matriz, es decir f([[6,5,4,3,2,1]]). Funciona generando la primera y la última línea de la salida, luego dividiéndola y volviéndose a llamar, hasta que cada sub-matriz tenga longitud 1. Aquí hay una demostración básica de cómo 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
ETHproducciones
fuente
2
Entonces, ¿hubo un punto en el que hubo tres respuestas vinculadas en 145 bytes?
Neil
2

Casco , 14 bytes

S+ȯ†O↔hUmfL¡ṁ½

Toma una matriz que contiene una sola matriz. Pruébalo en línea!

Explicación

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.

El incorporado ½toma una matriz y la divide en el medio. Si su longitud es impar, la primera parte es más larga en un elemento. Una matriz singleton [a]resultados en [[a],[]]y una matriz vacía []da [[],[]], por lo que es necesario para eliminar las matrices vacías antes de aplicar U.

Zgarb
fuente
1

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

-9 bytes por comentario por @recursive. Ahora la entrada se da como un singleton cuyo único elemento es una matriz de los números que se ordenarán.

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

Pruébalo en línea!

Versión desempaquetada con 35 bytes:

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

Explicación

El código se puede dividir en dos partes. La primera parte visualiza la división y la segunda visualiza la fusión.

Código para visualizar la división:

{                      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.

El código para visualizar la fusión:

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

Versión antigua, en realidad construyendo la estructura de la lista anidada.

{{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+= se usa tres veces en el código para verificar si la parte superior de la pila es un escalar o una matriz.

{{cc0+=!{x!}Mm',*:}}Xconstruye un bloque que recursivamente se llama a sí mismo para generar una matriz anidada de números correctamente. (La salida predeterminada en Stax vectoriza una matriz anidada antes de imprimir).

{                  }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

Hay otros dos bloques que realizan la división y la fusión, respectivamente. Son demasiado detallados y no me importa explicar (hay un poco más de información en la versión histórica de esta publicación, pero no esperes demasiado).

Weijun Zhou
fuente
Muy buena mejora. Todavía no lo entiendo totalmente, pero creo que cH!se puede usar en lugar de cH%!.
recursivo
{Nd}MPuede ser reemplazado por Ttambién.
recursivo
Hice algunas adaptaciones más. staxlang.xyz/… La respuesta Husk toma la entrada como una matriz en una matriz, así que supongo que es legal.
recursivo
Encontré una solución que debería tener 2 caracteres ascii más cortos, pero descubrí un error en la transposición de la matriz. Específicamente, muta las filas de la matriz. Lo agregaré a la cartera de pedidos para 1.0.4
recursivo
OKAY. Espero con interés la actualización.
Weijun Zhou