ฉันจะทำลายอาร์เรย์ของฉันได้ยากแค่ไหน?


30

ให้กำหนดกระบวนการในการบดขยี้อาร์เรย์ของตัวเลข ในความสนใจเราอ่านอาร์เรย์จากซ้ายไปขวา หาก ณ จุดที่เราพบสององค์ประกอบเดียวกันในแถวที่เราลบหนึ่งและสองครั้งที่สอง ตัวอย่างเช่นที่นี่เป็นกระบวนการของการบดขยี้อาร์เรย์ต่อไปนี้

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

ยกตัวอย่างเช่นองค์ประกอบเดียวกันสามารถยุบได้หลายครั้ง [1,1,2]กลายเป็น[4]เมื่อบด

เราจะเรียกใช้อาร์เรย์ที่ไม่สามารถย่อยสลายได้เมื่อกระบวนการบดอัดอาร์เรย์นั้นไม่เปลี่ยนแปลง ตัวอย่างเช่น[1,2,3]ยัง[1,2,3]หลังจากถูกบด

งานของคุณคือการใช้อาร์เรย์และกำหนดจำนวนของทับที่จำเป็นในการทำให้ไม่สามารถย่อยสลายได้ คุณต้องรองรับเฉพาะจำนวนเต็มในช่วง0ถึง2 32 -1

นี่คือ ดังนั้นคำตอบจะได้คะแนนเป็นไบต์โดยไบต์น้อยจะดีขึ้น

กรณีทดสอบ

[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
ควร[1,1,2,4,8]ส่งคืน 1 หรือ 4 หรือไม่
MooseBoys

2
@ThePirateBay ตกลงฉันจะลดมันลง แต่สำหรับบันทึกฉันคิดว่า Javascript ค่อนข้างโง่ในวิธีการจัดการ ints
ข้าวสาลีตัวช่วยสร้าง

2
หากคุณพยายามที่จะทำลาย [1 1 1 2] คุณจะจบลงด้วย [2 1 2] ถ้าคุณทำตามข้อมูลจำเพาะอย่างที่เขียนไว้ แต่คุณอาจจะจบลงด้วย [1 4] ถ้าคุณทำอย่างชาญฉลาดมากขึ้น [1 1 1 2] ควรทำอย่างไร
latias1290

4
@ latias1290 "ในความสนใจเราอ่านอาร์เรย์จากซ้ายไปขวา"

11
บางทีมันอาจจะเป็นเพียงฉัน แต่มันเอาฉันเป็นครั้งที่สองที่จะคิดออกว่าทำไมเป็นเพียง0,0,0,0 1มันอาจเป็นความคิดที่จะพูดถึงที่อื่นอย่างชัดเจนว่าเรากำลังนับจำนวนครั้งที่เราต้องวนรอบอาร์เรย์เพื่อบดขยี้มันอย่างเต็มที่และไม่ใช่อย่างที่ฉันคิดไว้ในตอนแรก
ปุย

คำตอบ:


12

ชุดประกอบ x86 (64 บิต), 66 65 ไบต์

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

คำแนะนำสตริงมีประโยชน์ ต้องแก้ไขข้อผิดพลาดแบบออฟไลน์ในสภาพแวดล้อม 64 บิตไม่ใช่

รหัสแหล่งความคิดเห็นเต็ม:

.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

ฉันอาจลองทำสิ่งนี้ในแบบ 32 บิตถ้าเพื่อความสนุกเท่านั้นเพราะคำนำหน้า REX เหล่านั้นฆ่าฉันจริงๆ

แก้ไข: โกนหนึ่งไบต์ด้วยการแทนที่ lodsq ด้วยการเพิ่ม,% rdx ด้วย% rax และยุบสอง cld ลงในที่เดียว



6

Haskell , 66 ไบต์

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)

ลองออนไลน์!

คำอธิบาย

fเป็นฟังก์ชันที่ทับรายการ มันจะทำการบดขยี้ตามที่กำหนดไว้ในคำถาม gเป็นฟังก์ชันที่นับจำนวนการทับอัด ถ้าf x==x, มิฉะนั้นg x=0g x=1+g(f x)


1
โกนไบต์ด้วยการเปลี่ยนg(f x)เป็นg$f x
ApproachingDarknessFish

3
@ApproachingDarknessFish นั่นใช้ไม่ได้เพราะ+มีความสำคัญมากกว่า$
Wheat Wizard

โอ้ฉันไม่ดี ตลกที่ฉันไม่เคยพบข้อผิดพลาดนั้นมาก่อน
ApproachDarknessFish

5

Paradoc (v0.2.10), 16 ไบต์ (CP-1252)

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

ลองออนไลน์! / with header / footer ที่ตรวจสอบกรณีทดสอบทั้งหมด

ทำรายการบนสแต็กและผลลัพธ์เป็นตัวเลขบนสแต็ก

การดำเนินการตรงไปตรงมาค่อนข้างจะซื่อสัตย์ บดขยี้รายการโดยเริ่มต้นด้วย Sentinel -1 วนลูปในรายการผลักแต่ละองค์ประกอบแล้วเพิ่มเข้าไปในองค์ประกอบที่อยู่ด้านล่างถ้ามันเท่ากัน ในตอนท้ายเราตัด -1 เราบดขยี้ตัวเลขให้เท่ากันเท่านั้นและปัญหาทั้งหมดนั้นไม่ใช่เชิงลบดังนั้น -1 sentinel จะไม่ส่งผลกระทบต่อกระบวนการแตกหัก เมื่อทำการบดแล้วมันเป็นเพียงเรื่องของการนับซ้ำไปยังจุดคงที่

คำอธิบาย:

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

หากเราสามารถสันนิษฐานได้ว่าอินพุตนั้นไม่ว่างเราก็ไม่จำเป็นต้องใช้ Sentinel และสามารถโกนได้ 2 ไบต์: {(\ε=k+x}]}IL(

ข้อเท็จจริงที่น่าสนุกอีกอย่างหนึ่ง: เราสูญเสีย 2 ไบต์เท่านั้นหากเราบังคับให้เราใช้เฉพาะ ASCII: {1m\{=k+x}e]1>}IL(


4

JavaScript (ES6), 86 ไบต์

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 และอธิบาย

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

การทดสอบ


a.length>n is the same as a[n]!=[]._. In this case (since all items in the array are numbers larger than -1), it is the same as a[n]>-1. Also, a[i]==a[++i]&&x is the same as a[i]-a[++i]||x.
Luke

I think 1/a[i] also works to save another byte.
Neil

4

JavaScript, 67 bytes

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

Try it online!


Nice! I thought I had golfed this as low as possible.
Rick Hitchcock

3

Brain-Flak, 144 bytes

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

Try it online!

Explanation

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

The crush function evaluates to the number of pairs of items that were crushed together:

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

A lambda from List<Long> to Integer. Input list must implement remove(int) (e.g. ArrayList). Assign to 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;}

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

c counts the number of crushes so far, i is the index into the list, and f indicates whether to continue crushing the list when an iteration finishes. Inside the loops, each adjacent pair is compared. i is incremented unconditionally, so if an element is removed by crushing, i is decremented first to cancel out the increment. The former element is removed from the list.

Acknowledgments

  • Bugfix thanks to Olivier Grégoire: boxed equality test

Doesn't work when the longs don't hit the valueOf cache. Example: {128L, 128L}. That's because of l.get(i)==l.get(i-1), which should be replaced by l.get(i).equals(l.get(i-1)).
Olivier Grégoire

Wow, embarassing...luckily l.get(i)-l.get(i-1)==0 will work. Thanks!
Jakob


2

JavaScript (ES6), 70 bytes

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)

Explanation:

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

Test Cases:


1
Hm.. It seems that we came up with the pretty much same idea :). After golfing mine answer I realized it is very similar to yours.

2

Python 2, 112 110 108 107 105 100 bytes

Edit: saved 2 bytes by removing or in return statement

Edit: saved 2 bytes by having i as the index of the second of the two elements to be accessed

Edit: saved 1 byte thanks to @Mr.Xcoder

Edit: saved 7 bytes thanks to @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)

Try it online!


2

JavaScript (ES6), 83 bytes

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

Explanation: The elements are recursively extracted from the original array and unique values are appended to b while c is a flag to indicate whether the array had been successfully crushed.


1

J, 54 bytes

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

Try it online!

Not my best golf by any means. Surely there has to be a better way of converting a list with one item to an atom.

Explanation

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

crush

This crushes an array once. It needs to be given the array in reverse since J's insert works right-to-left (something I learned today). This doesn't particularly matter, since all we need to output is the number of times we can crush the 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.

times

This is fairly straightforward: apply crush to the array until our result converges, but there are a few issues I had to deal with that result in much more code than I anticipated.

First, when crushing reduces to a single element, that element is actually in a one item list (i.e. it is nonatomic), so the function is applied again resulting in overcounting. To fix this, I used a hack I came up with to reduce a single element list to an atom which is ".@": (convert to string and then evaluate).

Second, crush errors on the empty list. I think you can define how a function should behave on receiving empty input with insert (/), but I couldn't find anything after a cursory look, so I'm using another workaround. This workaround is to prepend _ (infinity) to the list since it will never affect the number of times the array is crushed (_ > 2^64). However, this results in a single element list consisting of _ when given the empty list, so we need to convert to an atom again before crushing.

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

Horrific, I am sure there's a more clever way.

R integers are actually all at most 2^31-1.

Try it online!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.