抽象ゲームの最適戦略


12

私はインタビューで次の問題を与えられました(過去に自分のやり方をだまそうとするのではなく、すでに解決できていません):ゲームは正の整数始まります。(たとえば、A 0 = 1234。)この数値はバイナリ表現に変換され、N1に設定されたビット数です。(たとえば、A 0 = b 100 1101 0010N = 5。A0A0=1234N1A0=b100 1101 0010N=5.

プレイヤー1は、A 0よりも小さい数字選択しますB 0は 1に1ビットのみが設定されていなければならない(例:B 0 = B 10 0000 0000 = 512ましょう。)A 1 = A 0 - B 0。(例A 1 = 1234 - 512 = 722 = B 10 1101 0010)の動きは、有効な場合はB 0B0A0B0B0=b10 0000 0000=512A1=A0B0A1=1234512=722=b1011010010B0前の制約を満たし、設定されビット数がまだNに等しい場合A1

プレーヤー2は有効なB 1を選択してから続行し、プレーヤー1A 2から続行します。有効な手札が残っていない場合、プレーヤーは負けとなります。A1B1A2

両方のプレイヤーが最適にプレイすると仮定して、合理的に効率的な方法を使用して勝者を決定します。(私の問題の定義では、これに対する制約は、プログラムが符号付き32ビット整数に適合する数百万の入力数に対してソリューションを提供できなければならないことでした。)つまり、ソリューションはそうである必要はありません。完全に分析的。


ここでの私の個人的な関心は、与えられた120分間で正確性に関するフィードバックがまったくない状態で正しいソリューションを見つけて実装したという期待が妥当であったかどうかを判断することです。または、これが「以前にこのパズルを見たことがあるかどうかを確認しましょう」という質問の1つであった場合。

私が失敗したのは、合理的な戦略のように見えるものを実装することを選択したためです。時間がなくなったときの完全な出力。

振り返ってみると、ブルートフォース検索を実装し、少数の開始番号に対して部分的なソリューションを記憶する必要がありましたが、後知恵は常に20/20です。しかし、私がふざけているとは思えない別の一般的なアプローチがあるかどうか興味があります。


説明から、選択されたムーブには1ビットが1に設定されている必要があるとは理解していませんでした(例の一部に過ぎないと考えました)。
-jjmontes

@jjmontes-B番号を選択するための最初のルールとして記載されています-すべての例はそのように示されており、括弧以外はすべて一般的です。どのようにすればより明確になり得たかという提案はありますか?
ミリムース

1
多分「プレーヤー1は、1ビットのみを1に設定する必要があるA 0よりも小さい数選択しますか?」(たぶん私だけでしたが、これが制約であることを理解するために@orlpの回答を読む必要がありました)。B0A0
jjmontes

@Veedrac-男、私が知っていたなら、ビットカウントを合理的に速く実行するために費やされた私の努力はすべて無駄ではなかったでしょう。それがなぜ機能するのかを説明する答えは素晴らしいでしょう。
ミリムース

@millimoose Bitcountは、ほとんどの最新のCPUのハードウェアにあります!
-Veedrac

回答:


21

2の累乗しか減算できず、ポップカウントを変更できない場合は、他の数値が10である位置でを減算する必要があることを理解してください。その結果は常にその位置で01であり、数値は他のどこでも変更されません。011001

言い換えると、ゲームは一連のスワップであり、すべてが右側にあればゲームは終了します。このゲームが早期に終了することは不可能であることに注意してください-あなたが立ち往生することはできません。常にすべてのゼロが左側にあり、すべて1が右側にある位置になります。1001

したがって、ゲームの唯一の決定要因は、すべてのものが正しい状態に到達するために必要なスワップ数であり、勝ち負けの戦略はありません。スワップの数のパリティが唯一の決定要因です。

それで、スワップは何回かかりますか?なお、 sが、我々はそれらの番号が付けられ、彼らは最終状態で同じ順序のままにしたいスワップを介してそれらを追跡しそうならば、お互いを越えることはできません。各スワップは、最終位置にそれらを近づけます。1

したがって、番目の1(右から数えて右端の10番目の1)が右からk番目の位置にある場合、正しい位置に到達するにはk - iのスワップが必要です。これにより、必要なスワップの量をカウントするアルゴリズムが得られます。1101kk

i = 0
k = 0
total = 0
while n > 0:
    if n & 1:
       total += k - i
       i += 1
    n >>= 1
    k += 1

total誰が勝つかを確認するために、パリティを見ることができます。時間の複雑さはです。Oログn


これはほぼ正しいように見えます。私はこのアプローチの一部をつまずきました。コーディングの開始時に銃を飛び越えて、結果として生じる野生のガチョウの追跡に夢中になることで私がやったと思います。
ミリムース

これについての思考、戦略は、私は私の実装ではなく、無名のバグを持っているか、それが正しくゲームを果たしている任意の他の実装と同様の結果が得られているはず手段は重要ではありません事実は...
millimoose

5

このような問題を解決する1つの方法は次のとおりです。

  • 提案している「メモされたブルートフォース」アプローチを使用して、いくつかの単純な値の解決策を見つけてください。

  • 答えを推測します(どのポジションが勝ち、どのポジションが負けているか)。

  • 答えを証明してください。成功したら、素晴らしい。それ以外の場合は、反例を見つけて、それを使用して別の答えを推測してください。ここで、さらにいくつかのケースを解決すると役立つ場合があります。

どのくらい時間がかかるかを言うのは本当に難しいです。ただし、インタビューでは、必ずしも解決策を見つけるとは限りません。むしろ、インタビュアーは、あなたどのように問題を解決したか、どのように進歩したかを知りたいと思っています。


ええ、いや、彼らは私の出力が間違っていて時間を使い果たしたので私を拒否しました。
ミリムース

メモされたブルートフォースアプローチは、戦略に関するショートカットを必要としないため、正しいはずです。しかし、それは-私が考えた-あまりにも遅く、そして愚かな量のメモリを使用せずに、おそらく多くの助けが得られないためにメモ化は上であまりにも多くの作業をしたかもしれません。おそらくそうではないかもしれませんが、これをシステムから消去するために後で試します。
ミリムース

5

@orlpの回答から、開始位置から終了位置までの変位の合計のパリティが必要であることに注意してください。これに注釈を付けましょう:

       9876543210
       9 76 4  1    (positions at start)
start: 1011010010
end:   0000011111
            43210   (positions at end)

だから欲しい

  ((1 - 0) + (4 - 1) + (6 - 2) + (7 - 3) + (9 - 4)) & 1
= ((1 + 4 + 6 + 7 + 9) - (0 + 1 + 2 + 3 + 4)) & 1
= ((1 + 0 + 0 + 1 + 1) - (0 + 1 + 0 + 1 + 0)) & 1

最初の部分は、奇数位置のビット数のパリティです。最大の符号なし整数を取得し、0b11で除算して否定することで、これをマスクできます。

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (0 + 1 + 0 + 1 + 0)) & 1

2番目の部分は、のビット数の半分のパリティですx

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (bitcount(x) >> 1)) & 1

bitcount ハードウェアを使用できます popcnt命令を、またはで、いずれか一方のみの最後または最後から2番目のビットが必要であることを利用して手動で実施することができる。このような高速の減少

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.