Wie stark kann ich mein Array zerkleinern?


30

Definieren wir den Prozess des Zerquetschens einer Reihe von Zahlen. In einem Schwarm lesen wir das Array von links nach rechts. Wenn wir an einem Punkt zwei gleiche Elemente in einer Reihe antreffen, entfernen wir das erste und verdoppeln das zweite. Hier ist zum Beispiel der Prozess des Zerquetschens des folgenden Arrays

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

Das gleiche Element kann mehrfach zusammengebrochen werden, zum Beispiel [1,1,2]wird , [4]wenn zerkleinert.

Wir werden ein Array als nicht zerlegbar bezeichnen, wenn der Prozess des Zerlegens dieses Arrays es nicht ändert. Zum Beispiel [1,2,3]ist immer noch [1,2,3]nach zerkleinert.

Ihre Aufgabe ist es, ein Array zu nehmen und die Anzahl der Crushs zu bestimmen, die erforderlich sind, um es unzerbrechlich zu machen. Sie benötigen nur Ganzzahlen im Bereich von 0 bis 2 32 -1

Dies ist daher werden die Antworten in Bytes bewertet, wobei weniger Bytes besser sind.

Testfälle

[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
Sollte [1,1,2,4,8]1 oder 4 zurückgeben?
MooseBoys

2
@ThePirateBay Ok, ich werde es senken. Aber für die Aufzeichnung denke ich, dass Javascript in der Art, wie es mit Ints umgeht, ziemlich dumm ist.
Weizen-Zauberer

2
Wenn Sie versuchen, [1 1 1 2] zu vernichten, erhalten Sie [2 1 2], wenn Sie die Spezifikation genau wie beschrieben befolgen. Wenn Sie dies jedoch intelligenter tun, erhalten Sie möglicherweise [1 4]. Was soll [1 1 1 2] ergeben?
Latias1290

4
@ latias1290. "In einem Schwarm lesen wir das Array von links nach rechts."

11
Vielleicht bin ich es nur, aber ich brauchte eine Sekunde, um herauszufinden, warum 0,0,0,0es nur so war 1. Es könnte eine Idee sein, irgendwo explizit zu erwähnen, dass wir zählen, wie oft wir ein Array durchlaufen müssen, um es vollständig zu zerstören, und nicht , wie ich anfangs dachte, wie oft wir insgesamt 2 Zahlen zusammen zerstören.
Shaggy

Antworten:


12

x86-Assembly (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

Zeichenfolgenanweisungen waren hilfreich. Off-by-One-Fehler in einer 64-Bit-Umgebung zu beheben, war nicht der Fall.

Voll Quellcode kommentiert:

.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

Ich kann versuchen, dies in 32-Bit zu tun, wenn auch nur zum Spaß, da mich diese REX-Präfixe wirklich umgebracht haben.

Bearbeiten: ein Byte weniger, indem lodsq durch add,% rdx durch% rax ersetzt und zwei cld's zu einem reduziert werden.



6

Haskell , 66 Bytes

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)

Probieren Sie es online!

Erläuterung

fist eine Funktion, die eine Liste zerquetscht. Es macht den Crush wie in der Frage beschrieben. gist eine Funktion, die die Anzahl der Quetschungen zählt. Wenn f x==x, g x=0anders g x=1+g(f x).


1
Rasieren Sie ein Byte ab, indem Sie g(f x)aufg$f x
ApproachingDarknessFish

3
@ ApproachingDarknessFish Das funktioniert nicht, weil es +eine höhere Priorität hat als$
Wheat Wizard

Ah, mein Schlimmes. Komisch, dass ich noch nie auf diesen Fehler gestoßen bin.
ApproachingDarknessFish

5

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

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

Probieren Sie es online! / mit Kopf- / Fußzeile, die alle Testfälle prüft

Nimmt eine Liste auf den Stapel und ergibt eine Zahl auf dem Stapel.

Um ehrlich zu sein, ist die Implementierung ziemlich unkompliziert. Zerlegt eine Liste, indem sie mit einem Sentinel -1 beginnt, die Liste durchläuft, jedes Element verschiebt und dem Element darunter hinzufügt, wenn sie gleich sind. Am Ende haben wir die -1 abgeschnitten. Wir zerkleinern nur gleiche Zahlen zusammen und alle Zahlen des Problems sind nicht negativ, so dass der -1-Sentinel den Zerkleinerungsprozess nicht beeinflusst. Wenn die Zerkleinerung implementiert ist, müssen nur die Iterationen bis zum festgelegten Punkt gezählt werden.

Erläuterung:

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

Wenn wir annehmen könnten, dass die Eingabe nicht leer ist, bräuchten wir den Sentinel nicht und könnten 2 Bytes sparen: {(\ε=k+x}]}IL(

Eine weitere lustige Tatsache: Wir verlieren nur 2 Bytes, wenn wir uns zwingen, nur ASCII zu verwenden: {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 und erklärt

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

Tests


a.length>nist das gleiche wie a[n]!=[]._. In diesem Fall (da alle Elemente im Array Zahlen größer als -1 sind) ist es dasselbe wie a[n]>-1. Auch a[i]==a[++i]&&xist das gleiche wie a[i]-a[++i]||x.
Luke

Ich denke, 1/a[i]funktioniert auch, um ein weiteres Byte zu speichern.
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

Probieren Sie es online!


Nett! Ich dachte, ich hätte so günstig wie möglich Golf gespielt.
Rick Hitchcock

3

Brain-Flak , 144 Bytes

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

Probieren Sie es online!

Erläuterung

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

Die Zerkleinerungsfunktion berechnet die Anzahl der Artikelpaare, die zusammen zerkleinert wurden:

([][()]){[{}]                                                            ([][()])}    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 Bytes

Ein Lambda von List<Long>bis Integer. Eingabeliste muss implementieren remove(int)(zB ArrayList). Zuweisen zu 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;}

Probieren Sie es online

Ungolfed Lambda

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

czählt die Anzahl der bisherigen Crushs, iist der Index in der Liste und fgibt an, ob die Liste nach Abschluss einer Iteration weiter zerkleinert werden soll. Innerhalb der Schleifen wird jedes benachbarte Paar verglichen. iwird bedingungslos inkrementiert. Wenn also ein Element durch Quetschen entfernt wird, iwird es zuerst dekrementiert, um das Inkrement aufzuheben. Das vorherige Element wird aus der Liste entfernt.

Danksagung

  • Bugfix dank Olivier Grégoire: Boxed Equality Test

Funktioniert nicht, wenn die Longs den valueOfCache nicht erreichen. Beispiel: {128L, 128L}. Das liegt daran l.get(i)==l.get(i-1), was durch ersetzt werden sollte l.get(i).equals(l.get(i-1)).
Olivier Grégoire

Wow, peinlich ... wird zum Glück l.get(i)-l.get(i-1)==0funktionieren. Vielen Dank!
Jakob


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)

Erläuterung:

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

Testfälle:


1
Hm .. Es scheint, dass wir auf die gleiche Idee gekommen sind :). Nachdem ich meine Antwort golfen hatte, wurde mir klar, dass sie deiner sehr ähnlich ist.

2

Python 2 , 112 110 108 107 105 100 Bytes

Bearbeiten: 2 Bytes durch Entfernen orin der return-Anweisung gespeichert

Bearbeiten: Speichert 2 Bytes, indem ials Index das zweite der beiden Elemente verwendet wird, auf die zugegriffen werden soll

Bearbeiten: 1 Byte dank @ Mr.Xcoder gespeichert

Bearbeiten: 7 Bytes dank @jferard gespeichert

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)

Probieren Sie es 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

Erläuterung: Die Elemente werden rekursiv aus dem ursprünglichen Array extrahiert, und es werden eindeutige Werte angehängt, bwährend cein Flag angibt, ob das Array erfolgreich komprimiert wurde.


1

J, 54 Bytes

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

Probieren Sie es online!

Auf keinen Fall mein bestes Golf. Sicherlich muss es eine bessere Möglichkeit geben, eine Liste mit einem Element in ein Atom umzuwandeln.

Erläuterung

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

zerquetschen

Dies zerquetscht ein Array einmal. Das Array muss umgekehrt angegeben werden, da die Einfügung von J von rechts nach links funktioniert (was ich heute gelernt habe). Dies ist nicht besonders wichtig, da wir nur die Anzahl der möglichen Ausdrücke für das Array ausgeben müssen.

,`(+:@[ , }.@])@.({.@] = [)/
                           /  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.

mal

Dies ist ziemlich einfach: Wenden Sie Crush auf das Array an, bis das Ergebnis konvergiert. Es gibt jedoch einige Probleme, mit denen ich mich befassen musste, und die viel mehr Code ergeben, als ich erwartet hatte.

Erstens, wenn sich die Zerkleinerung auf ein einzelnes Element reduziert, befindet sich dieses Element tatsächlich in einer Liste mit einem Element (dh es ist nicht atomar), sodass die Funktion erneut angewendet wird, was zu einer Überzählung führt. Um dies zu beheben, benutzte ich einen Hack, der mir einfiel, um eine einzelne Elementliste auf ein Atom zu reduzieren, das ist".@": (in einen String konvertieren und dann auswerten).

Zweitens crushFehler in der leeren Liste. Ich denke, Sie können mit insert ( /) definieren, wie sich eine Funktion beim Empfang leerer Eingaben verhalten soll , aber ich konnte nach einer flüchtigen Betrachtung nichts finden, daher verwende ich eine andere Problemumgehung. Diese Problemumgehung besteht darin _, der Liste (unendlich) voran zu stellen, da sie sich nie darauf auswirkt, wie oft das Array komprimiert wird ( _ > 2^64). Dies führt jedoch zu einer einzelnen Elementliste, die aus _dem Vorhandensein der leeren Liste besteht, sodass wir vor dem Zerkleinern erneut in ein Atom konvertieren müssen .

<:@# [: ".@":@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 Bytes

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

Schrecklich, ich bin sicher, dass es einen klügeren Weg gibt.

R ganze Zahlen sind eigentlich alle höchstens 2^31-1.

Probieren Sie es online!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.