Implementa il "Lazy Sort"


44

Dovrei ordinare un elenco di numeri, ma sono super pigro. È davvero difficile capire come scambiare tutti i numeri fino a quando non sono tutti in ordine crescente, quindi ho trovato il mio algoritmo che garantirà che il nuovo elenco sia ordinato¹. Ecco come funziona:

Per un elenco di dimensioni N , avremo bisogno di iterazioni N-1 . Ad ogni iterazione,

  • Controlla se l'ennesimo numero è più piccolo dell'N + 1º numero. In tal caso, questi due numeri sono già ordinati e possiamo saltare questa iterazione.

  • In caso contrario, è necessario ridurre continuamente i primi N numeri fino a quando questi due numeri non sono in ordine.

Facciamo un esempio concreto. Diciamo che l'input è stato

10 5 7 6 1

Nella prima iterazione, confronteremo 10 e 5. 10 è maggiore di 5, quindi lo diminuiamo fino a quando non è più piccolo:

4 5 7 6 1

Ora confrontiamo 5 e 7. 5 è più piccolo di 7, quindi non abbiamo bisogno di fare nulla su questa iterazione. Quindi andiamo al prossimo e confrontiamo 7 e 6. 7 è più grande di 6, quindi diminuiamo i primi tre numeri fino a quando è più piccolo di 6, e otteniamo questo:

2 3 5 6 1

Ora confrontiamo 6 e 1. Ancora una volta, 6 è più grande di 1, quindi diminuiamo i primi quattro numeri fino a quando è più piccolo di 1, e otteniamo questo:

-4 -3 -1 0 1

E abbiamo finito! Ora la nostra lista è in perfetto ordine. E, per rendere le cose ancora migliori, abbiamo dovuto solo scorrere l'elenco N-1 volte, quindi questo algoritmo ordina gli elenchi in O (N-1) tempo, che sono abbastanza sicuro che sia l'algoritmo più veloce che ci sia.

La tua sfida per oggi è implementare questo Lazy Sort. Al programma o alla funzione verrà assegnato un array di numeri interi in qualsiasi formato standard desiderato e sarà necessario eseguire questo ordinamento lento e restituire il nuovo elenco "ordinato" . L'array non sarà mai vuoto o conterrà numeri non interi.

Ecco alcuni esempi:

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

Come sempre, questo è , quindi scrivi il programma più corto che puoi!


¹ Ciò non significa che cosa significhi, ma è tecnicamente vero

² Sto scherzando completamente, per favore non odiarmi


6
Penso che non sei pigro se lo fai in questo modo
Jörg Hülsermann,

4
@ JörgHülsermann beh, alcuni numeri interi sono troppo pesanti ... non esattamente in vena di portare un tale peso, meglio togliersi solo le cose migliori
Erik the Outgolfer,

21
<sarcasm>Questo algoritmo di ordinamento in realtà continua a registrare una O(N^2)complessità temporale perché è necessario esaminare tutti gli elementi precedentemente consultati nell'elenco per ridurli. Consiglio invece di scorrere l'elenco indietro e di diminuire solo un numero per passaggio, se necessario. Questo ti darà vera O(N)complessità! </sarcasm>
Value Ink

1
@ValueInk O(n^2)in termini di accessi alla memoria, ma non è O(n)per i confronti?
Cole Johnson,

7
@ColeJohnson tecnicamente sì, ma la complessità del tempo deve prendere in considerazione tutti i passaggi dell'algoritmo. Devi ancora scorrere tutti gli indici precedenti su ogni iterazione, quindi esce comunque O(N^2).
Value Ink

Risposte:


12

Gelatina ,  14 12 11  9 byte

-2 byte grazie a ETHproductions (usa la diade minima, «)

I’«0Ṛ+\Ṛ+

Un collegamento monadico che prende e restituisce elenchi di numeri interi.

Provalo online! o vedi la suite di test .

Non credo proprio che questo sia abbastanza pigro!

Come?

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 byte

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

Casi test


7

Gelatina , 12 byte

I»1U
0ị;Ç_\U

Provalo online!

Come funziona

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.

L'idea di base in gioco è questa: se inverti gli array di input e output, l'output è semplicemente l'input con ogni delta di 0 o maggiore sostituito con -1. Per esempio:

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

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

Provalo online.

Spiegazione:

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

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

Provalo online!

Mantieni la prima parte dell'elenco nel parametro a. Ad ogni passaggio aggiungi l'elemento successivo xalla fine di ae aumenta tutti gli elementi di a del minimo di (y-x-1)e 0.


4

Python , 54 byte

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

Provalo online!

Accetta input splattati come f(1,2,3). Emette un elenco. Utilizza il tempo esponenziale.


3

C #, 76 byte

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

Questo modifica l'elenco in atto. Scorre l'elenco indietro e mantiene un totale parziale del delta da applicare a ciascun numero.


2

JavaScript (ES6), 59 byte

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

Wow. Stavo per scrivere una soluzione JS, ma poi ho visto questo. Non pensavo di usare l'operatore spread come quello nei parametri
andrewarchi il

Puoi lasciare le f=risposte a JS per salvare due byte
andrewarchi,

@andrewarchi Grazie, ma questa particolare funzione deve chiamare se stessa ( f(a)) quindi richiede ancora il nome.
ETHproductions

Ho dimenticato che era ricorsivo
andrewarchi il

2

Brain-Flak , 153 byte

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

Provalo online!

Questo include +1per la -rbandiera.

#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 byte

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


1
bello da usare diff, stavo cercando di capire come farlo funzionare ... A proposito, puoi sbarazzarti delle parentesi graffe attorno al corpo della funzione per -2 byte, ma meglio ancora, puoi usare al s=scan()posto di una funzione definizione per salvare qualche byte in più. Sarebbe bello se includessi un link per provarlo online in modo che altre persone possano verificare che questo codice funzioni per tutti i casi di test.
Giuseppe,

Nessun problema! iniziamo tutti da qualche parte :)
Giuseppe,

1

JavaScript (ES6), 68 byte

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 e output sono un array di numeri interi.

Test dello snippet

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 byte

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

Spiegazione:

Questa è una soluzione ricorsiva, che prima clona l'array, quindi diminuisce tutti i valori fino a quando un elemento è maggiore o uguale all'elemento successivo dell'array.

La funzione si chiama fino a quando tutti gli elementi sono fuori servizio. Quando gli elementi vengono infine ordinati, viene restituito il clone. (Non possiamo restituire l'array stesso, perché il some()metodo avrebbe diminuito tutti i suoi elementi, rendendoli tutti disattivati ​​di -1.)

Casi test:

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 byte

:-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]).

Potrebbe essere in grado di provarlo online qui: http://swish.swi-prolog.org/p/LazySort.pl

Ti chiedi l(L, [10,5,7,6,1]).che dice "risolvi per L, dove L è la versione ordinata pigra di questo elenco".

Le due funzioni sono:

  • lazysorted (A, B) - afferma che A è la versione lazysorted di B, se sono entrambi elenchi vuoti o se A può essere ottenuto invertendo B, chiamando una funzione di aiuto per percorrere l'elenco e fare una sottrazione con un accumulatore spingendo ciascun valore più in basso di quello precedente e invertendo il risultato di quel ritorno nel modo giusto.
  • fl'helper corrisponde a due elenchi, il valore del numero precedente nell'elenco e un accumulatore della differenza di rotazione e risolve il nuovo valore della posizione dell'elenco corrente che è il valore originale meno l'accumulatore della differenza, facoltativamente meno un nuovo valore richiesto per forzare questo valore inferiore al numero precedente nell'elenco e fdeve risolvere ricorsivamente la coda dell'elenco con l'accumulatore differenza ora aumentato.

Schermata dei casi di test su Swish:

immagine che mostra i casi di test in esecuzione su Swish


0

JavaScript (ES6), 61 byte

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

Non è la soluzione più breve, ma non ho potuto passare oltre l'opportunità di utilizzare reduceRight .


0

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

  • Salvato solo 1 byte con un approccio leggermente diverso.
  • Salvati altri 2 byte con una semplificazione della fors.
  • Risparmiato 7 byte grazie alle incredibili abilità di golf di 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;}

Provalo online!

Prima forscorre attraverso l'array, quindi calcola il decremento e infine il secondofor decrementa gli elementi, se necessario, fino alla iposizione th.

È valido solo modificare l'array originale invece di restituirne uno nuovo (abituandosi ancora alle regole)?


Sì, la modifica dell'array originale è perfettamente corretta. :)
DJMcMayhem

4
@DJMcMayhem grazie, mi sono sentito troppo pigro per crearne uno nuovo. :)
Charlie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.