最適なニムの動きを見つける


8

ゲーム

Nimは数学的な戦略ゲームで、2人のプレイヤーが順番に、異なるヒープからアイテムを取ります。自分のターンでは、少なくとも1つのアイテムを取る必要があり、1つのヒープからのみ取得するという条件で、必要な数だけアイテムを取得できます。最後のアイテムを取るプレイヤーが勝ちます!これは解決済みのゲームです。戦略に入る前に、こちらでオンラインプレイしてみてください

戦略

勝利の戦略は、このリンクで非常に明確かつ簡単に説明されています。もう少し専門的な用語を使って説明します。このゲームに勝つには、binary-digital-sumが常に0になるように、常にできるだけ多くのアイテムを取ることです。次のボードについて考えてみます。

         *
       * *
     * * *
   * * * *
 * * * * *
 1 2 3 4 5

このボードのbinary-digital-sumを見つけるには、次のことを行う必要があります。

  1. 各行の数値をバイナリに変換します。したがって、001、010、011、100、101があります。

  2. すべての数字を一緒に追加し、キャリーは無視します。

     001
     010
     011
     100
    +101
    ----
     001
    

    各数値をビット単位でxorすることもでき、これにより同じ結果が得られます。

現在、この現在の構成の合計が001の場合、これは(まだ)勝利ボードではありません。しかし、あなたはそれを勝利ボードにすることができます!1列目、3列目、または5列目から1つのアイテムを取り出すと、合計は0になります。これは勝利ボードです。つまり、ミスをしなければ、次に移動するプレーヤーが負けます。したがって、常に勝利ボードでターンを終了する必要があります。列5から1つの項目を削除するとします。ボードは次のようになります。

       * *
     * * *
   * * * *
 * * * * *
 1 2 3 4 5

あなたが失敗しない限り、あなたは保証された勝利を持っています。対戦相手があなたを止めるためにできることは何もありません。列5からすべての項目を取得するとします。

       *
     * *
   * * *
 * * * *
 1 2 3 4 5

次にどこに行きますか?まだ下にスクロールしないで、自分でそれを理解してみてください。


現在、合計は100です。最良の動き(そして勝った動きのみ)は、列4からすべてを取り出すことです。これにより、ボードは次のようになります。

     * 
   * * 
 * * * 
 1 2 3 4 5

そして、このような合計

 001
 010
+011
----
 000

つまり、あなたは勝利ボードにいるということです!わーい!

チャレンジ

nimボードが与えられた場合に、勝つ手か、勝つ手がない場合は誤った値を返すプログラムまたは関数を記述する必要があります。

あなたの入力:

  • 言語のネイティブリスト形式になります。リストの各項目は、指定された列の項目数に対応しています。たとえば、入力{4、2、1、0、3}は次のnimボードに対応します。

    *
    *           *
    *  *        *
    *  *  *     *
    1, 2, 3, 4, 5
    
  • (オプション)行の数。(C / C ++のような言語の場合、これはリスト自体からはわかりません。)

あなたの出力:

  • STDOUTに進むか、関数から戻ることができます

  • 2つの数値でなければなりません。1)削除する列(列には0からインデックスが付けられることに注意してください)と2)その行から削除するアイテムの数です。これは、2項目の配列、2つの数値の文字列などになる可能性があります。答えが2桁を超える可能性があることに注意してください。 「列11から1つのアイテムを削除」または「列1から11のアイテムを削除」を意味します。「1,11」または「11,1」はどちらも使用できます。

  • 答えがない場合は、偽の値を返すか印刷します。言語が1種類の変数(ここでもC / C ++など)しか返せない場合は、列に負の数、または削除する数値に0以下を指定すると、どちらも誤った値になります。

  • 列番号または削除する番号が大きすぎる場合、これは無効な出力と見なされます。

入力/出力の例

[1, 2, 3, 4, 5]---> [0, 1]または[4, 1]または[2, 1]

[1, 3, 5, 6]---> [0, 1]または[1, 1]または[2, 1]

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

[1, 2, 3] ---> ERROR

完全なプログラムの代わりに関数を実行することを選択した場合、関数の動作を示す完全なプログラムを作成する必要があります。これはあなたのフルスコアにカウントされません。また、プログラムは妥当な時間内に実行されることが期待されています。極端に大きな入力を入力するつもりはありません。プログラムがゲームツリー全体に対してブルートフォース検索を実行していない限り、問題はありません。

いつものように、これはコードゴルフなので、標準のループホールが適用され、回答はバイト単位でカウントされます。

リーダーボード

これは、通常のリーダーボードと言語ごとの勝者の概要の両方を生成するスタックスニペットです。

回答が確実に表示されるようにするには、次のMarkdownテンプレートを使用して、見出しから回答を始めてください。

# Language Name, N bytes

N提出物のサイズはどこですか。スコアを向上させる場合、古いスコアを打ち消すことで見出しに残すことができます。例えば:

# Ruby, <s>104</s> <s>101</s> 96 bytes


入力リストに少なくとも1つの正の数が含まれていると想定できますか?
ジャクベ2015年

@Jakubeはい、できます。
Jamesが

回答:


3

Pyth、33 22 21 20 19バイト

eS.e*<JxxFQbb,k-bJQ

こちらのオンラインコンパイラでお試しいただけます。

12バイトを削除してくれたJakubeと、追加のバイトを削除してくれたMaltysenに感謝します。

これは、現在の位置からの勝利の動きを印刷します。勝つ手がない場合、何も印刷しません。

ウィキペディアでアルゴリズムを使用しました。内訳は次のとおりです。

  .e              Q    While enumerating every element in the input:
      J                    Assign variable J to:
        xFQ                 Every element of the input bitwise xor'ed together,
       x   b                bitwise xor'ed with the current element of the input
             ,             Create a tuple containing:
              k             The index of the current element, and
               -bJ          the difference between the current element and J
    *<J     b              Put that tuple into a list if J < b, otherwise put an empty tuple
 S                     Sort the list
e                      Print the last element of the list

3

Pyth、23バイト

eSs.em*,kd!xxxFQb-bdSbQ

オンラインで試す:デモ

これはすべての可能な動きを反復し、並べ替えも行います。したがって、それはの時間複雑度O(N*log(N))とのメモリ複雑度を持ちますO(N)。ここNで、は入力リストまたは項目数の合計です。

時間の複雑さが悪いため、これは有効な答えではない場合があります。すべてのゲームを解決しますが、実際のオブジェクトを使って実生活で即座にプレイできます。

説明:

                          implicit: Q = input list
   .e                 Q   map each (index k, item b) of Q to:
     m              Sb      map each d of [1, 2, ..., b] to:
       ,kd                      the pair (k, d)
      *                       multiplied by
             xFQ                xor of all numbers in Q
            x   b               xor with b
           x     -bd            xor with b-d
          !                     not (1 if xor==0 else 0)

So the input [1, 2, 0, 0, 5] gives [[[]], [[], []], [], [], [[], [4, 2], [], [], []]]

  s                       add all lists to one big list
 S                        sort

Now it looks like this: [[], [], [], [], [], [], [], [4, 2]]

e                         pick the last element and print

3

CJam、21 20バイト

元のバージョンと比較してバイトを節約し、エラー処理も機能するようになりました:

l~__:^f^.-_:g1#_@=S\

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

入力はCJam配列です。例:

[1 2 3 4 5]

解決策が見つからない場合-1 0は、が出力されます。これは、出力要件に関する私の理解を満たしています。

説明:

l~      Get input.
__      Make two copies for following operations.
:^      Reduce with xor operator, producing xor of all columns.
f^      xor all input values with the result. For each column, this calculates
        the value that the column would have to change to make the overall
        xor zero. Consider this the "target values".
.-      Subtract the target values from the input values.
_:g     Per element signum of difference between target value and input value.
1#      Find index with value 1, which is the first positive value.
_@=     Get the value at the index.
S\      Put a space between index and value.

1

Ruby、95

->(a){r=[false,c=0]
a.each{|e|c^=e}
a.each_index{|i|(n=a[i]-(c^a[i]))>0&&n>r[1]?(r=[i,n]):0}
r}

2つの別個の匿名関数を作成しました。最初のコードfは、以下のプログラムで割り当てられており、すべてのソリューションを出力し、2番目のコードはg(上記のスコアに対応し、どちらも短く、仕様に準拠しているため)、最大数を削除する必要があるソリューションのみを返します

どちらの場合も、数字の合計はで合計されcます。次に、配列がループされ、式n=a[i]-(c^a[i])を使用して削除するカウンターの数が計算されます(明らかに、これはゼロを超える場合にのみ実行できます)。

ではf、考えられるすべてのソリューションが出力されます(ある場合c0errorループせずに出力されます)。パイルが異なると、削除する必要のあるカウンターの数がまったく異なる場合があることに驚きました。

g出力アレイをr削除するカウンタの数が前の数を超えた場合にのみ更新されます。配列r = [pile index, number to be removed]が返されます。解決策がない場合、削除されるカウンターの数は常にゼロであり、r変更されないままであり、r =の初期値[false,0]が返されます。

falseたとえば"!"、に変更できる場合や、最大のものではなく有効なソリューションを(を削除することで)返すことができる場合、わずかな節約が可能です&&n>r[1]

テストプログラムでフォーマット

f=->(a){
  c=0
  a.each{|e|c^=e}
  c<1?(p "error"):(a.each_index{
     |i|(n=a[i]-(c^a[i]))>0?(p"#{i},#{n}"):0
   })
}

g=->(a){
  r=[false,c=0]
  a.each{|e|c^=e}
  a.each_index{
     |i|(n=a[i]-(c^a[i]))>0&&n>r[1]?(r=[i,n]):0
  }
  r
}

#Change the following two lines according to the number of values youj want to use for testing.
t=[rand(15),rand(15),rand(15),rand(15)] #Generate some random numbers for testing.
t=[gets.to_i,gets.to_i,gets.to_i]       #User input. Delete this line if you want to test with random numbers.
print t,"\n"

f.call(t)
puts g.call(t)

実際、r[1]は常に少なくとも0 なので、以下に>0&&n>r[1]短縮できると思います >r[1]
Level River St

0

Mathematica、73バイト

{f=BitXor;p=Position[#,x_/;BitAnd[x,a=f@@#]==a][[1,1]],(n=#[[p]])-n~f~a}&

1
何。Mathematicaにはこれが組み込まれていませんか?
Draco18sは、2017

0

JavaScript(ES6)、54バイト

ペア[column, number]またはfalseを返します

a=>a.some((n,i)=>r=(n-=n^eval(a.join`^`))>0&&[i,n])&&r

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

コメントしました

a =>                        // a[] = input array
  a.some((n, i) =>          // for each value n at position i in a[]:
    r = (                   //   save the result of the iteration in r
      n -=                  //   subtract from n:
        n ^ eval(a.join`^`) //     n XOR (all values in a[] XOR'ed together)
    ) > 0                   //   if the above value is positive:
    && [i, n]               //     yield the solution [i, n] and exit the some() loop
                            //   (otherwise: yield false and go on with the next value)
  ) && r                    // end of some(); return r
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.