アレイをどれだけ激しく粉砕できますか?


30

数字の配列を粉砕するプロセスを定義しましょう。クラッシュでは、配列を左から右に読み取ります。ある時点で2つの同じ要素が連続して出現した場合、最初の要素を削除し、2番目の要素を2倍にします。たとえば、次の配列を粉砕するプロセスです

[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 OK下げます。しかし、記録については、Javascriptはintを処理する方法がかなり馬鹿だと思います。
小麦ウィザード

2
[1 1 1 2]を粉砕しようとした場合、仕様どおりに仕様に従えば[2 1 2]になりますが、より賢く行うと[1 4]になります。[1 1 1 2]の結果はどうなりますか?
latias1290

4
@ latias1290。「クラッシュでは、配列を左から右に読み取ります。」

11
多分それは私だけかもしれませんが、なぜ0,0,0,0だけだったのかを理解するのに少し時間がかかりました1。私が最初に考えたように、2つの数字を一緒に押しつぶした合計回数ではなく、完全に押しつぶすために配列をループする必要がある回数を数えていることをどこかに明示的に言及することは考えかもしれません。
シャギー

回答:


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ビット環境でoff-by-oneエラーを修正する必要はありませんでした。

完全にコメント化されたソースコード:

.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

これらのREXプレフィックスが本当に私を殺したので、楽しみのためだけに、32ビットでこれを試してみることができます。

編集:lodsqをaddに、%rdxを%raxに、2つのcldを1つにまとめることにより、1バイト削りました。



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==xg x=0そうでなければg x=1+g(f x)


1
変更することで、バイトを剃り落とすg(f x)g$f x
ApproachingDarknessFish

3
@ApproachingDarknessFish +よりも優先順位が高いため、機能しません$
ウィートウィザード

ああ、私の悪い。おかしなことに、これまでにこのエラーに遭遇したことは一度もありません。
近づいている

5

Paradoc(v0.2.10)、16バイト(CP-1252)

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

オンラインでお試しください!/すべてのテストケースをチェックするヘッダー/フッター付き

スタック上のリストを取得し、スタック上の数値を生成します。

正直に言うと、かなり簡単な実装です。センチネル-1で開始してリストを押しつぶし、リストをループし、各要素をプッシュして、等しい場合はその下の要素に追加します。最後に-1を切り取りました。等しい数だけを一緒に粉砕し、問題の数はすべて負ではないため、-1センチネルは粉砕プロセスに影響しません。粉砕を実装すると、反復をその固定小数点までカウントするだけで済みます。

説明:

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

入力が空でないと仮定できる場合、センチネルは必要なく、2バイトを削ることができます。 {(\ε=k+x}]}IL(

もう1つの面白い事実:ASCIIのみを使用するように強制した場合、2バイトしか失われません。 {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

非ゴルフと説明

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はと同じa[n]!=[]._です。この場合(配列内のすべての項目が-1より大きい数値であるため)、それはと同じa[n]>-1です。また、a[i]==a[++i]&&xと同じa[i]-a[++i]||xです。
ルーク

1/a[i]また、別のバイトを保存するのに役立つと思います。
ニール


3

Brain-Flak、144バイト

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

オンラインでお試しください!

説明

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

クラッシュ関数は、一緒にクラッシュしたアイテムのペアの数を評価します。

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

ラムダList<Long>Integer。入力リストは実装する必要がありますremove(int)(例ArrayList)。に割り当て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;}

オンラインで試す

ゴルフされていないラムダ

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これまでのクラッシュの数をカウントiし、リストへのインデックスでありf、反復が終了したときにリストのクラッシュを続行するかどうかを示します。ループ内で、隣接する各ペアが比較されます。iは無条件にインクリメントされるため、要素が粉砕によって削除された場合、i最初にデクリメントされてインクリメントがキャンセルされます。前の要素はリストから削除されます。

謝辞

  • OlivierGrégoireのバグ修正:箱入りの等価性テスト

ロングがvalueOfキャッシュにヒットしない場合は機能しません。例:{128L, 128L}。これはl.get(i)==l.get(i-1)、に置き換える必要があるためですl.get(i).equals(l.get(i-1))
オリビエグレゴワール

うわー、恥ずかしい... l.get(i)-l.get(i-1)==0うまくいきます。ありがとう!
ヤコブ


2

JavaScript(ES6)、70バイト

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)

説明:

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

テストケース:


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.