Seberapa keras saya bisa menghancurkan array saya?

30

Mari kita tentukan proses penghancuran array angka. Dalam naksir kita membaca array kiri ke kanan. Jika pada suatu titik kita menemukan dua elemen yang sama dalam satu baris, kita menghapus yang pertama dan menggandakan yang kedua. Sebagai contoh di sini adalah proses menghancurkan array berikut

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

Elemen yang sama dapat dihancurkan beberapa kali, misalnya [1,1,2]menjadi [4]ketika dihancurkan.

Kami akan memanggil array yang tidak bisa dihancurkan ketika proses menghancurkan array itu tidak mengubahnya. Misalnya [1,2,3]masih [1,2,3]setelah dihancurkan.

Tugas Anda adalah mengambil array dan menentukan jumlah crush yang diperlukan untuk membuatnya tidak bisa dihancurkan. Anda hanya perlu mendukung bilangan bulat pada kisaran 0 hingga 2 32 -1

Ini adalah sehingga jawaban akan dinilai dalam byte dengan lebih sedikit byte lebih baik.

Uji Kasus

[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
Wisaya Gandum
sumber
5
Haruskah [1,1,2,4,8]mengembalikan 1 atau 4?
MooseBoys
2
@ThePirateBay Ok saya akan menurunkannya. Tetapi sebagai catatan saya berpikir bahwa Javascript agak bodoh dalam cara menangani ints.
Wheat Wizard
2
Jika Anda mencoba untuk menghancurkan [1 1 1 2] Anda akan berakhir dengan [2 1 2] jika Anda mengikuti spesifikasi persis seperti yang tertulis, tetapi Anda bisa berakhir dengan [1 4] jika Anda melakukannya dengan lebih cerdas. Apa yang harus [1 1 1 2] hasilkan?
latias1290
4
@ latias1290. "Dalam naksir kita membaca array dari kiri ke kanan."
11
Mungkin hanya aku, tapi butuh beberapa saat untuk mencari tahu mengapa 0,0,0,0hanya itu 1. Mungkin ide untuk secara eksplisit menyebutkan suatu tempat bahwa kita menghitung berapa kali kita harus melalui array untuk menghancurkannya secara penuh dan tidak , seperti yang saya pikirkan, jumlah total kali kita menghancurkan 2 angka bersama-sama.
Shaggy

Jawaban:

12

perakitan 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

Instruksi string sangat membantu. Harus memperbaiki kesalahan off-by-one dalam lingkungan 64-bit tidak.

Kode sumber yang sepenuhnya dikomentari:

.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

Saya dapat mencoba melakukan ini dalam 32-bit, jika hanya untuk bersenang-senang, karena awalan REX itu benar-benar membunuh saya.

Sunting: mencukur satu byte mati dengan mengganti lodsq dengan add,% rdx dengan% rax, dan menciutkan dua cld menjadi satu.

ObecessiousNewt
sumber
9

Pyth , 22 byte

tl.uu?&GqeGH+PGyH+GHN[

Verifikasi semua testcases.

Biarawati Bocor
sumber
Jeebus! Apakah Anda pertama kali menggunakan transpiler dan kemudian mengeditnya, atau apakah Anda benar-benar menulis Pyth dari awal?
oligofren
2
@oligofren yang terakhir.
Leaky Nun
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)

Cobalah online!

Penjelasan

fadalah fungsi yang menghancurkan daftar. Itu melakukan naksir seperti yang dijelaskan dalam pertanyaan. gadalah fungsi yang menghitung jumlah crush. Jika f x==x, g x=0sebaliknya g x=1+g(f x).

Wisaya Gandum
sumber
1
Shave off byte dengan mengubah g(f x)keg$f x
ApproachingDarknessFish
3
@ApproachingDarknessFish Itu tidak berfungsi karena +memiliki prioritas lebih tinggi daripada$
Wheat Wizard
Ah, salahku. Lucu bahwa saya belum pernah mengalami kesalahan itu sebelumnya.
ApproachingDarknessFish
5

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

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

Cobalah online! / dengan header / footer yang memeriksa semua kasus uji

Mengambil daftar di tumpukan, dan menghasilkan nomor di tumpukan.

Implementasi yang cukup mudah, cukup jujur. Menghancurkan daftar dengan memulai dengan sentinel -1, melewati daftar, mendorong setiap elemen dan menambahkannya ke elemen di bawahnya jika mereka sama. Pada akhirnya kita memotong -1. Kami hanya menghancurkan angka yang sama bersama-sama dan semua angka masalahnya tidak negatif, sehingga -1 sentinel tidak akan memengaruhi proses penghancuran. Dengan crushing diimplementasikan, itu hanya masalah menghitung iterasi ke titik tetapnya.

Penjelasan:

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

Jika kita dapat berasumsi bahwa inputnya bukan kosong, kita tidak akan memerlukan sentinel dan dapat mencukur 2 byte: {(\ε=k+x}]}IL(

Fakta menyenangkan lainnya: kita hanya kehilangan 2 byte jika kita memaksakan diri untuk hanya menggunakan ASCII: {1m\{=k+x}e]1>}IL(

betaveros
sumber
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

Tidak Diikat dan Dijelaskan

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

Tes

Justin Mariner
sumber
a.length>nsama dengan a[n]!=[]._. Dalam hal ini (karena semua item dalam array adalah angka lebih besar dari -1), itu sama dengan a[n]>-1. Juga, a[i]==a[++i]&&xsama dengan a[i]-a[++i]||x.
Luke
Saya pikir 1/a[i]juga berfungsi untuk menyimpan byte lain.
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

Cobalah online!


sumber
Bagus! Saya pikir saya telah bermain golf ini serendah mungkin.
Rick Hitchcock
3

Brain-Flak , 144 byte

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

Cobalah online!

Penjelasan

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

Fungsi crush mengevaluasi jumlah pasangan item yang dihancurkan bersama:

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

Java 8, 120 byte

Seekor lambda dari List<Long>ke Integer. Daftar input harus menerapkan remove(int)(mis ArrayList.). Tetapkan untuk 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;}

Cobalah secara Online

Lambda yang tidak tersentuh

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

cmenghitung jumlah crush sejauh ini, iadalah indeks ke dalam daftar, dan fmenunjukkan apakah akan terus menghancurkan daftar ketika iterasi selesai. Di dalam loop, setiap pasangan yang berdekatan dibandingkan. ibertambah tanpa syarat, jadi jika suatu elemen dihilangkan dengan menghancurkan, maka idikurangi terlebih dahulu untuk membatalkan kenaikan. Elemen sebelumnya dihapus dari daftar.

Ucapan Terima Kasih

  • Perbaikan bug berkat Olivier Grégoire: uji kesetaraan kotak
Jakob
sumber
Tidak berfungsi ketika rindu tidak mencapai valueOfcache. Contoh: {128L, 128L}. Itu karena l.get(i)==l.get(i-1), yang harus diganti l.get(i).equals(l.get(i-1)).
Olivier Grégoire
Wow, memalukan ... untungnya l.get(i)-l.get(i-1)==0akan berhasil. Terima kasih!
Jakob
2

Perl 5 , 96 byte

94 kode, 2 untuk -pa

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

Cobalah online!

Xcali
sumber
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)

Penjelasan:

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

Kasus uji:

Rick Hitchcock
sumber
1
Hm .. Sepertinya kami datang dengan ide yang hampir sama :). Setelah golf jawaban saya, saya menyadari itu sangat mirip dengan Anda.
2

Python 2 , 112 110 108 107 105 100 byte

Edit: disimpan 2 byte dengan menghapus ordalam pernyataan kembali

Sunting: menyimpan 2 byte dengan memiliki isebagai indeks kedua dari dua elemen yang akan diakses

Edit: disimpan 1 byte berkat @ Mr.Xcoder

Sunting: disimpan 7 byte berkat @ 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)

Cobalah online!

Halvard Hummel
sumber
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

Penjelasan: Elemen-elemen diekstraksi secara rekursif dari array asli dan nilai-nilai unik ditambahkan ke bsementara cadalah bendera untuk menunjukkan apakah array telah berhasil dihancurkan.

Neil
sumber
1

J, 54 byte

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

Cobalah online!

Bukan golf terbaik saya dengan cara apa pun. Tentunya harus ada cara yang lebih baik untuk mengubah daftar dengan satu item menjadi atom.

Penjelasan

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

menghancurkan

Ini menghancurkan array sekali. Perlu diberi array secara terbalik karena insert J bekerja dari kanan ke kiri (sesuatu yang saya pelajari hari ini). Ini tidak terlalu penting, karena yang kita butuhkan untuk output adalah berapa kali kita dapat menghancurkan 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.

waktu

Ini cukup mudah: menerapkan naksir ke array sampai hasil kami menyatu, tetapi ada beberapa masalah yang saya harus berurusan dengan hasil dalam kode lebih banyak daripada yang saya perkirakan.

Pertama, ketika penghancuran dikurangi menjadi satu elemen, elemen itu sebenarnya ada dalam satu daftar item (yaitu nonatomik), jadi fungsi tersebut diterapkan lagi yang menghasilkan penghitungan yang berlebihan. Untuk memperbaikinya, saya menggunakan peretasan yang saya buat untuk mengurangi satu daftar elemen menjadi atom".@": (dikonversi ke string dan kemudian mengevaluasi).

Kedua, crushkesalahan pada daftar kosong. Saya pikir Anda dapat menentukan bagaimana suatu fungsi harus berfungsi saat menerima input kosong dengan insert ( /), tapi saya tidak bisa menemukan apa pun setelah melihat sepintas, jadi saya menggunakan solusi lain. Solusi ini adalah dengan menambahkan _(tak terhingga) ke daftar karena itu tidak akan mempengaruhi berapa kali array dihancurkan ( _ > 2^64). Namun , ini menghasilkan daftar elemen tunggal yang terdiri dari _ketika diberikan daftar kosong, jadi kita perlu mengkonversi ke atom lagi sebelum dihancurkan.

<:@# [: ".@":@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
cole
sumber
1

Jelly , 21 byte

ṪḤ,⁼?⁹⁸;µ/Ḋ$¹L’$?ÐĿL’

Cobalah online!

Erik the Outgolfer
sumber
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))})

Mengerikan, saya yakin ada cara yang lebih pintar.

Bilangan bulat R sebenarnya paling banyak 2^31-1.

Cobalah online!

Giuseppe
sumber