Implementar o "Lazy Sort"


44

Eu deveria classificar uma lista de números, mas sou super preguiçosa. É realmente difícil descobrir como trocar todos os números até que todos estejam em ordem crescente, então criei meu próprio algoritmo que garantirá que a nova lista seja classificada¹. Veja como funciona:

Para uma lista do tamanho N , precisaremos de iterações N-1 . Em cada iteração,

  • Verifique se o número N é o menor que o número N + 1 . Se for, esses dois números já estão classificados e podemos pular essa iteração.

  • Se não estiverem, será necessário decrementar continuamente os primeiros N números até que esses dois números estejam em ordem.

Vamos dar um exemplo concreto. Digamos que a entrada foi

10 5 7 6 1

Na primeira iteração, compararemos 10 e 5. 10 é maior que 5, portanto, a diminuímos até que seja menor:

4 5 7 6 1

Agora, comparamos 5 e 7. 5 é menor que 7, portanto, não precisamos fazer nada nessa iteração. Então, vamos para a próxima e comparamos 7 e 6. 7 é maior que 6, então diminuímos os três primeiros números até que seja menor que 6 e obtemos o seguinte:

2 3 5 6 1

Agora, comparamos 6 e 1. Novamente, 6 é maior que 1, então diminuímos os quatro primeiros números até que seja menor que 1 e obtemos o seguinte:

-4 -3 -1 0 1

E nós terminamos! Agora nossa lista está em perfeita ordem de classificação. E, para tornar as coisas ainda melhores, tivemos apenas que percorrer a lista N-1 vezes, então esse algoritmo classifica as listas em tempo O (N-1) , o que tenho certeza de que é o algoritmo mais rápido que existe.²

Seu desafio hoje é implementar esse Lazy Sort. Seu programa ou função receberá uma matriz de números inteiros no formato padrão que você desejar, e você deverá executar essa classificação lenta e retornar a nova lista "classificada" . A matriz nunca estará vazia ou conterá números não inteiros.

aqui estão alguns exemplos:

Input: 10 5 7 6 1
Output: -4 -3 -1 0 1

Input: 3 2 1
Output: -1 0 1

Input: 1 2 3
Output: 1 2 3

Input: 19
Output: 19

Input: 1 1 1 1 1 1 1 1 1 
Output: -7 -6 -5 -4 -3 -2 -1 0 1 

Input: 5 7 11 6 16 2 9 16 6 16
Output: -27 -25 -21 -20 -10 -9 -2 5 6 16

Input: -8 17 9 7
Output: -20 5 6 7

Como sempre, isso é , então escreva o programa mais curto possível!


¹ Isso não significa o que parece, mas é tecnicamente verdade

² Estou completamente brincando, por favor, não me odeie


6
Eu acho que você não é preguiçoso, se você fazê-lo desta forma
Jörg Hülsermann

4
@ JörgHülsermann bem alguns inteiros são muito pesados ... não exatamente com vontade de realizar um tal peso, melhor para decolar apenas o material superior para fora
Erik o Outgolfer

21
<sarcasm>Na verdade, esse algoritmo de classificação ainda se destaca na O(N^2)complexidade do tempo, porque você precisa passar por todos os itens acessados ​​anteriormente na lista para diminuí-los. Eu recomendo ir através da lista para trás em vez e diminuir apenas um número por etapa conforme necessário. Isso lhe dará verdadeira O(N)complexidade! </sarcasm>
Valor de tinta

1
@ValueInk O(n^2)em termos de acessos à memória, mas não é O(n)para comparações?
Cole Johnson

7
@ColeJohnson tecnicamente sim, mas a complexidade do tempo precisa levar em consideração todas as etapas do algoritmo. Você ainda precisa percorrer todos os índices anteriores em todas as iterações, para que ainda saia O(N^2).
Valor de tinta

Respostas:


12

Geléia ,  14 12 11  9 bytes

-2 bytes graças a ETHproductions (use a díade mínima «)

I’«0Ṛ+\Ṛ+

Um link monádico que recebe e retorna listas de números inteiros.

Experimente online! ou veja a suíte de testes .

Eu realmente não acho que isso seja o suficiente para o Lazy ™!

Quão?

I’«0Ṛ+\Ṛ+ - Link: list of integers, a              e.g. [ 8, 3, 3, 4, 6, 2]
I         - increments between consecutive items of a   [-5, 0, 1, 2,-4 ]
 ’        - decrement (vectorises)                      [-6,-1, 0, 1,-5 ]
   0      - literal 0
  «       - minimum of decremented increments and zero  [-6,-1, 0, 0,-5 ]
    Ṛ     - reverse                                     [-5, 0, 0,-1,-6 ]
      \   - cumulative reduce with:
     +    -   addition                                  [-5,-5,-5,-6,-12]
       Ṛ  - reverse                                     [-12,-6,-5,-5,-5]
        + - addition (with a)                           [-4,-3,-2,-1, 1, 2]


8

JavaScript (ES6), 61 bytes

a=>a.map((b,i)=>a=(b-=a[i+1])>0?a.map(c=>i--<0?c:c-b-1):a)&&a

Casos de teste


7

Gelatina , 12 bytes

I»1U
0ị;Ç_\U

Experimente online!

Como funciona

I»1U  Helper link. Argument: l (list of integers)
I     Compute the increments (difference between items) of l.
 »1   For each item n, take the maximum of n and 1.
   U  Reverse.

0ị;Ç_\U  Main link. Argument: l (list of integers)
   Ç     Call the helper link with argument l.
  ;      Concatenate this with
0ị       the 0th last item of the (1-indexed) l. (Can't use Ṫ because it modifies l)
    _\   Cumulatively reduce the result by subtraction.
      U  Reverse.

A idéia básica em jogo é a seguinte: se você reverter as matrizes de entrada e saída, a saída será simplesmente a entrada com cada delta de 0 ou mais substituído por -1. Por exemplo:

[10,  5,  7,  6,  1]   input
[ 1,  6,  7,  5, 10]   reverse
[   5,  1, -2,  5  ]   deltas
[  -1, -1, -2, -1  ]   min(deltas, -1)
[ 1, -1, -2, -1, -1]   reverse and concat the last item of the original
[ 1,  0, -2, -3, -4]   re-apply deltas
[-4, -3, -2,  0,  1]   reverse

5

k, 20 bytes

{x-|+\0,1_0|1+-':|x}

Experimente online.

Explicação:

{                  } /function, x is input
                 |x  /reverse x
              -':    /difference between every element
            1+       /add one to each difference
          0|         /make minimum difference be 0
      0,1_           /swap first difference with a 0
    +\               /cumulative sum
   |                 /reverse again
 x-                  /subtract from x

4

Haskell, 56 bytes

a#(x:y:z)=map(+min(y-x-1)0)(a++[x])#(y:z)
a#x=a++x
([]#)

Experimente online!

Manter a primeira parte da lista de parâmetro a. Em cada etapa, adicione o próximo elemento xao final de ae aumente todos os elementos de a pelo mínimo de (y-x-1)e 0.


4

Python , 54 bytes

f=lambda a,*r:r and[f(*r)[0]-max(r[0]-a,1)]+f(*r)or[a]

Experimente online!

Toma entrada como splatted f(1,2,3). Mostra uma lista. Usa tempo exponencial.


3

C #, 76 bytes

a=>{for(int d=0,i=a.Length-1;i>0;a[--i]-=d)d=a[i-1]-d<a[i]?d:a[i-1]-a[i]+1;}

Isso modifica a lista no local. Ele percorre a lista ao contrário e mantém um total em execução do delta a ser aplicado a cada número.


2

JavaScript (ES6), 59 bytes

f=([n,...a],p=a[0]-n)=>a+a?[(a=f(a))[0]-(p>1?p:1),...a]:[n]

Uau. Eu estava prestes a escrever uma solução JS, mas vi isso. Eu não acho que usar o operador se espalhou como que nos parâmetros
andrewarchi

Você pode deixar f=para as respostas JS salvar dois bytes
andrewarchi 15/17

@andrewarchi Obrigado, mas essa função específica precisa se chamar ( f(a)), portanto ainda requer o nome.
ETHproductions

Eu esqueci que era recursivo #
6133

2

Flak cerebral , 153 bytes

{(({})<>[({})])(({}({}))[({}[{}])])<>(([{}]({})))([({}<(())>)](<>)){({}())<>}{}{((<{}>))<>{}}{}<>{}{{}({}<>{}())((<>))}{}{}}{}<>{}([]){{}({}<>)<>([])}<>

Experimente online!

Isso inclui +1a -rbandeira.

#While True
{

    #Push the last value left in the array minus the counter onto the alternate stack
    (({})<>[({})])

    #Put the counter back on top of the alternate stack
    (({}({}))[({}[{}])])

    #Toggle
    <>

    #Find the difference between the last two inputs left on the array
    (([{}]({})))

    #Greater than or equal to 0?
    ([({}<(())>)](<>)){({}())<>}{}{((<{}>))<>{}}{}<>{}

    #If So:
    {

      #Pop the truthy/falsy value
      {}

      #Increment the counter by the difference between elements +1
      ({}<>{}())

      #Push two falsys
      ((<>))

    #Endwhile
    }

    #Pop the two falsys
    {}{}

#Endwhile
}

#Pop the falsy

{}

#Toggle back
<>

#Pop the counter

#Reverse the stack
{}
([]){{}({}<>)<>([])}<>

2

R, 56 bytes

function(s){s-c(rev(cumsum(rev(pmax(0,-diff(s)+1)))),0)}


1
bom uso diff, eu estava tentando descobrir como fazer isso funcionar ... Aliás, você pode se livrar das chaves ao redor do corpo da função por -2 bytes, mas, melhor ainda, você pode usar em s=scan()vez de uma função definição para salvar mais alguns bytes. Seria ótimo se você incluísse um link para Experimente online, para que outras pessoas possam verificar se esse código funciona para todos os casos de teste.
Giuseppe

Não se preocupe! todos nós em algum lugar start :)
Giuseppe

1

JavaScript (ES6), 68 bytes

a=>a.map((v,i)=>(d=v-o[i+1]+1)>1?o=o.map((v,j)=>j>i?v:v-d):0,o=a)&&o

Entrada e saída é uma matriz de números inteiros.

Snippet de teste

f=
a=>a.map((v,i)=>(d=v-o[i+1]+1)>1?o=o.map((v,j)=>j>i?v:v-d):0,o=a)&&o
<input id=I oninput="O.value=f(this.value.split` `.map(x=>+x)).join` `">
<input id=O disabled>


1

JavaScript (ES6), 50 bytes

f=a=>(b=[...a]).some((_,i)=>a[i]-->=a[i+1])?f(a):b

Explicação:

Essa é uma solução recursiva, que primeiro clona a matriz e depois diminui todos os valores até que um elemento seja maior ou igual ao próximo elemento da matriz.

A função chama a si mesma desde que qualquer elemento esteja fora de ordem. Quando os elementos são finalmente classificados, o clone é retornado. (Não podemos retornar a matriz em si, porque o some()método teria diminuído todos os seus elementos, desativando-os em -1.)

Casos de teste:

f=a=>(b=[...a]).some((_,i)=>a[i]-->=a[i+1])?f(a):b

console.log(f([10,5,7,6,1])+'');
console.log(f([1,1,1,1,1,1,1,1,1])+'');
console.log(f([5,7,11,6,16,2,9,16,6,16])+'');
console.log(f([19])+'');
console.log(f([-8,17,9,7])+'');
console.log(f([1,2,3,4,5,6,7])+'');


1

SWI-Prolog, 194 bytes

:-use_module(library(clpfd)).
f([],[],_,_).
f([A|B],[M|N],P,D):-A#=M-D-E,A#<P,abs(M,S),T#=S+1,E in 0..T,label([E]),f(B,N,A,D+E).
l([],[]).
l(A,B):-reverse(Z,B),f([X|Y],Z,X+1,0),reverse(A,[X|Y]).

Pode ser possível experimentá-lo on-line aqui: http://swish.swi-prolog.org/p/LazySort.pl

Você pergunta o l(L, [10,5,7,6,1]).que diz "resolva para L, onde L é a versão ordenada lenta desta lista".

As duas funções são:

  • lazysorted (A, B) - indica que A é a versão preguiçosa de B, se ambas são listas vazias ou se A pode ser obtido ao reverter B, chamando uma função auxiliar para percorrer a lista e fazer uma subtração com um acumulador empurrando cada valor abaixo do valor anterior e revertendo o resultado para o caminho certo.
  • fO auxiliar corresponde a duas listas, o valor do número anterior na lista e um acumulador de diferenças contínuas, e resolve o novo valor da posição atual da lista, sendo o valor original menos o acumulador de diferenças, opcionalmente menos um novo valor necessário para forçar isso. abaixo do número anterior na lista e fdeve resolver o final da lista recursivamente com o acumulador de diferenças agora aumentado.

Captura de tela dos casos de teste no Swish:

imagem mostrando os casos de teste em execução no Swish


0

JavaScript (ES6), 61 bytes

a=>a.reduceRight((r,e)=>[e-(d=(c=e-r[0]+1)>d?c:d),...r],d=[])

Não é a solução mais curta, mas não pude deixar passar a oportunidade de usar reduceRight.


0

C # (.NET Core) , 89 88 86 79 bytes

  • Economizou apenas 1 byte com uma abordagem um pouco diferente.
  • Salvo outros 2 bytes com uma simplificação dos fors.
  • Economizou 7 bytes graças às incríveis habilidades de golfe do VisualMelon.
a=>{for(int i=0,j,k;++i<a.Length;)for(k=a[i-1]-a[j=i]+1;--j>=0;)a[j]-=k>0?k:0;}

Experimente online!

Primeiro foritera através da matriz, depois calcula o decremento e, finalmente, o segundo fordecrementa os elementos, se necessário, até a iposição th.

É válido apenas modificar a matriz original em vez de retornar uma nova (ainda se acostumando às regras)?


Sim, a modificação da matriz original está perfeitamente correta. :)
DJMcMayhem

4
@DJMcMayhem obrigado, me senti com preguiça de criar um novo. :)
Charlie
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.