Quanto è difficile schiacciare il mio array?


30

Consente di definire il processo di frantumazione di una matrice di numeri. In una cotta leggiamo l'array da sinistra a destra. Se ad un certo punto incontriamo due dello stesso elemento di fila rimuoviamo il primo e raddoppiamo il secondo. Ad esempio, ecco il processo di compressione del seguente array

[5,2,2,3]
 ^
[5,2,2,3]
   ^
[5,2,2,3]
     ^
[5,4,3]
   ^
[5,4,3]
     ^

Lo stesso elemento può essere ripiegato più volte, ad esempio [1,1,2]diventa [4]schiacciati.

Chiameremo un array uncrushable quando il processo di schiacciamento di tale array non lo modifica. Ad esempio [1,2,3]è ancora [1,2,3]dopo essere stato schiacciato.

Il tuo compito è quello di prendere un array e determinare il numero di schiacciamenti necessari per renderlo incrollabile. Hai solo bisogno di supportare numeri interi compresi tra 0 e 2 32 -1

Questo è quindi le risposte verranno classificate in byte con meno byte migliori.

Casi test

[1] -> 0
[1,1] -> 1
[2,1,1] -> 2
[4,2,1,1] -> 3
[2,2,2,1,1] -> 3
[0,0,0,0] -> 1
[4,0,0,0,4] -> 1
[4,0,0,0,0,4] -> 1
[] -> 0

5
Dovrebbe [1,1,2,4,8]restituire 1 o 4?
MooseBoys,

2
@ThePirateBay Ok, lo abbasserò. Ma per la cronaca penso che Javascript sia piuttosto stupido nel modo in cui gestisce gli ints.
Mago del grano

2
Se provassi a schiacciare [1 1 1 2] finiresti con [2 1 2] se segui le specifiche esattamente come scritto, ma potresti finire con [1 4] se lo fai in modo più intelligente. Cosa dovrebbe comportare [1 1 1 2]?
latias1290,

4
@ latias1290. "In una cotta leggiamo l'array da sinistra a destra."

11
Forse sono solo io, ma mi ci è voluto un secondo per capire perché 0,0,0,0fosse solo 1. Potrebbe essere un'idea menzionare esplicitamente da qualche parte che stiamo contando il numero di volte in cui dobbiamo scorrere un array per comprimerlo completamente e non , come inizialmente pensavo, il numero totale di volte in cui schiacciamo 2 numeri insieme.
Shaggy,

Risposte:


12

assembly x86 (64 bit), 66 65 byte

31 c0 57 59 56 51 56 5f 4d 31 c0 48 83 c6 08 48
83 e9 01 76 1b fc f2 48 a7 75 15 48 d1 67 f8 51
56 57 f3 48 a5 5f 5e 59 fd 48 a7 49 ff c0 eb e5
59 5e 4c 29 c1 48 ff c2 4d 85 c0 75 c7 48 ff c8
c3

Le istruzioni per le stringhe sono state utili. Dover correggere errori off-by-one in un ambiente a 64 bit non lo era.

Codice sorgente completamente commentato:

.globl crush
crush:
/* return value */
xor %eax, %eax
/* save our length in rcx */
push %rdi
pop %rcx
pass:
/* save the start of the string and the length */
push %rsi
push %rcx
/* this is the loop */
/* first copy source to dest */
push %rsi
pop %rdi
/* and zero a variable to record the number of squashes we make this pass */
xor %r8, %r8
/* increment source, and decrement ecx */
add $8,%rsi
sub $1,%rcx
/* if ecx is zero or -1, we're done (we can't depend on the code to take care of this
automatically since dec will leave the zero flag set and cmpsq won't change it) */
jbe endpass
compare:
/* make sure we're going forward */
cld
/* compare our two values until we find two that are the same */
repne cmpsq
/* if we reach here, we either found the end of the string, or
we found two values that are the same. check the zero flag to
find out which */
jne endpass
/* okay, so we found two values that are the same. what we need
to do is double the previous value of the destination, and then
shift everything leftwards once */
shlq $1, -8(%rdi)
/* easiest way to shift leftwards is rep movsq, especially since
our ecx is already right. we just need to save it and the rsi/rdi */
push %rcx
push %rsi
push %rdi
rep movsq
pop %rdi
pop %rsi
pop %rcx
/* problem: edi and esi are now one farther than they should be,
since we can squash this dest with a different source. consequently
we need to put them back where they were. */
std
cmpsq
/* we don't need to put ecx back since the list is now one shorter
than it was. */
/* finally, mark that we made a squash */
inc %r8
/* okay, once we've reached this point, we should have:
 edi and esi: next two values to compare
 ecx: number of comparisons left
so we just jump back to our comparison operation */
jmp compare
endpass:
/* we reached the end of the string. retrieve our old ecx and esi */
pop %rcx
pop %rsi
/* rsi is accurate, but rcx is not. we need to subtract the number of squashes
that we made this pass. */
sub %r8, %rcx
/* record that we performed a pass */
inc %rax
/* if we did make any squashes, we need to perform another pass */
test %r8, %r8
jnz pass
/* we reached the end; we've made as many passes as we can.
decrement our pass counter since we counted one too many */
dec %rax
/* and finally return it */
ret

Potrei provare a farlo a 32 bit, anche solo per divertimento, dal momento che quei prefissi REX mi hanno davvero ucciso.

Modifica: eliminazione di un byte sostituendo lodsq con add,% rdx con% rax e comprimendo due cld in uno.



6

Haskell , 66 byte

f(a:b:x)|a==b=f$a+a:x|1>0=a:f(b:x)
f x=x
g x|f x==x=0|1>0=1+g(f x)

Provalo online!

Spiegazione

fè una funzione che schiaccia un elenco. Esegue la cotta come indicato nella domanda. gè una funzione che conta il numero di cotte. Se f x==x, g x=0altrimenti g x=1+g(f x).


1
Elimina un byte cambiando g(f x)ing$f x
ApproachingDarknessFish

3
@ApproachingDarknessFish Non funziona perché +ha una precedenza maggiore rispetto a$
Wheat Wizard

Ah, mio ​​cattivo. Divertente che non ho mai incontrato quell'errore prima.
avvicina

5

Paradoc (v0.2.10), 16 byte (CP-1252)

{—1\ε=k+x}]»}IL(

Provalo online! / con intestazione / piè di pagina che controlla tutti i casi di test

Prende un elenco nello stack e genera un numero nello stack.

Implementazione piuttosto semplice, a dire il vero. Schiaccia un elenco iniziando con una sentinella -1, scorrendo ciclicamente l'elenco, spingendo ciascun elemento e aggiungendolo all'elemento sottostante se sono uguali. Alla fine abbiamo tagliato -1. Frantumiamo solo numeri uguali insieme e tutti i numeri del problema sono non negativi, quindi la sentinella -1 non influirà sul processo di frantumazione. Con lo schiacciamento implementato, si tratta solo di contare le iterazioni fino al punto fisso.

Spiegazione:

{            }I  .. Iterate this block: repeatedly apply it until a fixed
                 .. point is reached, and collect all intermediate results
 —1              ..   Push -1 (note that that's an em dash)
   \             ..   Swap it under the current list of numbers
    ε    }       ..   Execute this block for each element in the list:
     =           ..     Check if it's equal to the next element on the stack...
      k          ..       ... while keeping (i.e. not popping either of) them
       +         ..     Add the top two elements of the stack...
        x        ..       ... that many times (so, do add them if they were
                 ..       equal, and don't add them if they weren't)
          ]      ..   Collect all elements pushed inside the block that
                 ..     we're iterating into a list
           »     ..   Tail: take all but the first element (gets rid of the -1)
              L  .. Compute the length of the number of intermediate results
               ( .. Subtract 1

Se potessimo supporre che l'input fosse vuoto, non avremmo bisogno della sentinella e potremmo radere 2 byte: {(\ε=k+x}]}IL(

Un altro fatto divertente: perdiamo solo 2 byte se ci forziamo a usare solo ASCII: {1m\{=k+x}e]1>}IL(


4

JavaScript (ES6), 86 byte

f=a=>a.length>eval("for(i=0;a[i]>-1;)a[i]==a[++i]&&a.splice(--i,2,a[i]*2);i")?1+f(a):0

Ungolfed e spiegato

f=a=>                           // function taking array a
    a.length > eval("           // if a.length > the result of the following...
        for(i=0; a[i]>-1;)      //   loop from 0 until the current value is undefined (which is not > -1)
            a[i] == a[++i] &&   //     if the current value equals the next one...
                a.splice(--i,   //       splice the array at the first index of the pair...
                    2,          //       by replacing 2 items...
                    a[i]*2);    //       with the current item * 2
                                //       this also decrements the counter, which means the current value is now the next
    i")                         //   return the counter, which is new a.length
        ? 1+f(a)                // if that was true, the array was crushed. add 1 and recur with the new array
        : 0                     // otherwise just return 0

test


a.length>nè lo stesso di a[n]!=[]._. In questo caso (poiché tutti gli elementi dell'array hanno numeri maggiori di -1), è uguale a a[n]>-1. Inoltre, a[i]==a[++i]&&xè lo stesso di a[i]-a[++i]||x.
Luca,

Penso che funzioni 1/a[i]anche per salvare un altro byte.
Neil,

4

JavaScript, 67 byte

f=a=>a.map(a=>k[k[d-1]!=a?d++:(a*=z=2,d-1)]=a,k=d=[z=0])&&z&&f(k)+1

Provalo online!


Bello! Pensavo di aver giocato a golf il più in basso possibile.
Rick Hitchcock,

3

Brain-Flak , 144 byte

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

Provalo online!

Spiegazione

([])                                                                 Push stack height (starts main loop if list nonempty)
     {                                                       }       Do while the last iteration involved at least one crush:
      <{}>                                                           Remove crush indicator
           <(...)>                                                   Do a crush iteration
                  {()(<{}>)}                                         Evaluate to 1 if list was changed
                            {}{}                                     Remove zeroes
                                <>                        <>         On other stack:
                                  <([]){{}        ([])}>{}           Do while stack is nonempty:
                                          ({}<>)<>                   Move to first stack
          (                                                 )        Push 1 if crush worked, 0 otherwise
    (                                                         <>)    Push sum of results on other stack and implicitly print

La funzione di schiacciamento valuta il numero di coppie di elementi che sono stati schiacciati insieme:

([][()]){[{}]                                                            ([][()])}    Do while stack height isn't 1:
              ({}[({})]      )                                                        Calculate difference between top two elements
                       <(())>                                                         Push a 1 below difference
                              {                    }                                  If difference was nonzero (don't crush this pair)
                               ({}    ({})<>)                                         Reconstruct top element and place on other stack
                                  <{}>       ((<>))                                   Push zeros to exit this conditional and skip next
             <                                      >{}                               Evaluate as zero
                                                       {              }{}             If difference was zero (crush this pair):
                                                        {}                            Evaluate as previously pushed 1
                                                          (<(({}){})>)                Double top of stack

3

Java 8, 120 byte

Un lambda da List<Long>a Integer. L'elenco di input deve essere implementato remove(int)(ad es ArrayList.). Assegna a Function<List<Long>, Integer>.

l->{int c=-1,i,f=1;for(;f>0;c++)for(f=i=0;++i<l.size();)if(l.get(i)-l.get(i-1)==0)l.set(i-=f=1,2*l.remove(i));return c;}

Provalo online

Lambda ungolfed

l -> {
    int
        c = -1,
        i,
        f = 1
    ;
    for (; f > 0; c++)
        for (f = i = 0; ++i < l.size(); )
            if (l.get(i) - l.get(i - 1) == 0)
                l.set(i -= f = 1, 2 * l.remove(i));
    return c;
}

cconta il numero di schiacciamenti finora, iè l'indice nell'elenco e findica se continuare a schiacciare l'elenco al termine di un'iterazione. All'interno dei loop, viene confrontata ogni coppia adiacente. iviene incrementato incondizionatamente, quindi se un elemento viene rimosso mediante schiacciamento, iviene prima decrementato per annullare l'incremento. Il primo elemento viene rimosso dall'elenco.

Ringraziamenti

  • Bugfix grazie a Olivier Grégoire: test di uguaglianza in scatola

Non funziona quando i long non colpiscono la valueOfcache. Esempio: {128L, 128L}. Questo perché l.get(i)==l.get(i-1), che dovrebbe essere sostituito da l.get(i).equals(l.get(i-1)).
Olivier Grégoire,

Wow, imbarazzante ... per fortuna l.get(i)-l.get(i-1)==0funzionerà. Grazie!
Jakob,

2

Perl 5 , 96 byte

94 codice, 2 per -pa

do{$\++;$l=@F;for($i=0;++$i<@F;){$F[$i]==$F[$i-1]&&splice@F,$i,2,$F[$i--]*2}}while$l!=@F}{$\--

Provalo online!


2

JavaScript (ES6), 70 byte

f=(a,j=m=0,t=[])=>a.map(e=>t[e==t[j-1]?(e*=m=2,j-1):j++]=e)&&m&&1+f(t)

Spiegazione:

f=(
  a,                  //the input
  j=m=0,              //j is the index into t; m starts out falsey
  t=[]                //t will hold the crushed array
)=>
  a.map(e=>           //for each element in the array
    t[e==t[j-1] ?     //if the element repeats:
      (e*=m=2,        //... multiply it by two, set m to truthy,
       j-1) :         //... and index the previous element of t.
      j++             //else append to t, and increment its index.
    ]=e               //set this index of t to the current value of e
  ) &&                //map is always truthy
  m &&                //if m is falsey, return 0
  1+f(t)              //else return 1 plus the recurse on t

Casi test:


1
Hm .. Sembra che ci sia venuta l'idea più o meno la stessa :). Dopo aver golfato la mia risposta mi sono reso conto che è molto simile alla tua.

2

Python 2 , 112 110 108 107 105 100 byte

Modifica: salvato 2 byte rimuovendo l' oristruzione return

Modifica: salvato 2 byte avendo icome indice il secondo dei due elementi a cui accedere

Modifica: salvato 1 byte grazie a @ Mr.Xcoder

Modifica: salvato 7 byte grazie a @jferard

def f(x):
 i=e=1
 while x[i:]:
	if x[~-i]==x[i]:del x[i];i-=1;x[i]*=2;e=2
	i+=1
 return~-e and-~f(x)

Provalo online!


2

JavaScript (ES6), 83 byte

f=([x,y,...a],b=[],c)=>1/x?x==y?f([x+y,...a],b,1):f([y,...a],[...b,x],c):c?1+f(b):0

Spiegazione: Gli elementi vengono estratti in modo ricorsivo dall'array originale e vengono aggiunti valori univoci bmentre cè un flag per indicare se l'array è stato frantumato correttamente.


1

J, 54 byte

[:<:@#[:".@":@(,`(+:@[,}.@])@.({.@]=[))/^:a:@".@":_,|.

Provalo online!

Non è il mio miglior golf in alcun modo. Sicuramente ci deve essere un modo migliore di convertire un elenco con un oggetto in un atomo.

Spiegazione

crush =. ,`(+:@[ , }.@])@.({.@] = [)/
times =. <:@# [: ".@":@crush^:a:@".@": _ , |.

schiacciare

Questo schiaccia un array una volta. Deve essere dato l'array al contrario poiché l'inserto di J funziona da destra a sinistra (qualcosa che ho imparato oggi). Ciò non importa in particolare, poiché tutto ciò che serve per l'output è il numero di volte in cui possiamo schiacciare l'array.

,`(+:@[ , }.@])@.({.@] = [)/
                           /  Fold/reduce from the right
                  {.@] = [    Head of the running array equals the left argument?
   +:@[ ,                     If so, prepend double the argument to 
          }.@]                the array minus its head
,                             Else, prepend the left argument.

volte

Questo è abbastanza semplice: applica la cotta all'array fino a quando il nostro risultato non converge, ma ci sono alcuni problemi che ho dovuto affrontare con quel risultato in molto più codice di quanto mi aspettassi.

Innanzitutto, quando la frantumazione si riduce a un singolo elemento, quell'elemento si trova in realtà in un elenco di elementi (ovvero non è anatomico), quindi la funzione viene nuovamente applicata con conseguente sovraccarico. Per risolvere questo problema, ho usato un hack che mi è venuto in mente per ridurre un singolo elenco di elementi in un atomo che è ".@":(converti in stringa e quindi valuta).

In secondo luogo, crusherrori nell'elenco vuoto. Io credo che si può definire come una funzione dovrebbe comportarsi riguardo alla ricezione di input vuoto con inserto ( /), ma non ho trovato nulla, dopo uno sguardo superficiale, quindi sto usando un'altra soluzione. Questa soluzione alternativa è anteporre _(infinito) all'elenco poiché non influirà mai sul numero di volte in cui l'array viene schiacciato ( _ > 2^64). Tuttavia , ciò si traduce in un singolo elenco di elementi costituito da _quando viene fornito l'elenco vuoto, quindi è necessario convertire nuovamente in un atomo prima di frantumarlo.

<:@# [: ".@":@crush^:a:@".@": _ , |.
                                  |.  Reverse input
                              _ ,     Prepend infinity
                        ".@":         Convert single-element list to atom
              crush                   Crush the list and after
        ".@":                         Convert single-element list to atom 
                   ^:a:               until it converges, storing each 
                                      iteration in an array
<:@#                                  Length of the resulting list minus 1


0

R , 142 byte

f=function(l,r=l,k=0,T=1)"if"(sum(l|1)<2,k,{while(T<sum(r|1))"if"(r[T]-r[T+1],T<-T+1,{r<-r[-T]
r[T]<-2*r[T]})
"if"(all(r==l),k,f(r,r,k+1,1))})

Orribile, sono sicuro che ci sia un modo più intelligente.

I numeri interi R sono in realtà tutti al massimo 2^31-1.

Provalo online!

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.