文字列構築ゲームの勝利戦略


14

バックグラウンド

アリスとボブは、バイナリワードの構築と呼ばれるゲームをプレイします。ゲームをプレイするには、長さn >= 0ゴールセットと呼ばれるG長さのnバイナリワードのセット、およびターン順序と呼ばれる文字とを含む長さのn文字列tを修正します。ゲームはターンの間続き、ターンでは、によって定義されたプレーヤーがbitを選択します。ゲームが終了すると、プレイヤーは構築したバイナリワードを確認します。この単語が目標セットで見つかった場合、アリスがゲームに勝ちます。そうでなければ、ボブが勝ちます。ABnit[i]w[i]wG

たとえば、の修正をしましょうn = 4G = [0001,1011,0010]t = AABA。アリスは最初のターンを取得し、彼女はを選択しw[0] = 0ます。2番目のターンもアリスであり、彼女はを選択しw[1] = 0ます。ボブは3番目のターンを持ち、彼はを選択しw[2] = 0ます。最終ターンで、アリスはを選択しw[3] = 1ます。結果の単語0001がにGあるため、アリスがゲームに勝ちます。

今、ボブがを選択した場合w[2] = 1、アリスはw[3] = 0彼女の最後のターンで選択し、まだ勝つことができました。これは、アリスがボブのプレー方法に関係なくゲームに勝つことができることを意味します。この状況では、アリスには勝利戦略があります。この戦略は、Bobのターンに対応するレベルで分岐し、すべての分岐に次の単語が含まれるラベル付きバイナリツリーとして視覚化できますG

A A B A

-0-0-0-1
    \
     1-0

アリスは、自分のターンに枝をたどるだけでプレーします。ボブがどのブランチを選んでも、最終的にアリスが勝ちます。

入力

入力として長さn、およびG長さの文字列の(空の場合もある)セットとしてのセットが与えられますn

出力

出力は、アリスが勝利戦略を持っているターン順のリストです。これは、上記のバイナリツリーの存在に相当します。順番の順番は関係ありませんが、重複は禁止されています。

詳細なルール

完全なプログラムまたは関数を作成できます。プログラムの場合、入力と出力の区切り文字を選択できますが、両方で同じにする必要があります。最短のバイトカウントが優先され、標準の抜け穴は許可されません。

テストケース

3 [] -> []
3 [000,001,010,011,100,101,110,111] -> [AAA,AAB,ABA,ABB,BAA,BAB,BBA,BBB]
4 [0001,1011,0010] -> [AAAA,BAAA,AABA]
4 [0001,1011,0010,0110,1111,0000] -> [AAAA,BAAA,ABAA,BBAA,AABA,AAAB]
5 [00011,00110,00111,11110,00001,11101,10101,01010,00010] -> [AAAAA,BAAAA,ABAAA,BBAAA,AABAA,AAABA,BAABA,AAAAB,AABAB]

楽しい事実

出力の順番の数は、目標セットの単語の数と常に等しくなります。


5
入力と出力のサイズが等しいという事実にかなり興味があります。この事実の証拠または引用はありますか?直感的にサイズを保存するこの関数を計算する方法があるのだろうか。
xnor

2
テストケース#5はあなたの楽しい事実と矛盾しています...
mbomb007

3
@ mbomb007テストケース#5には111012回リストされています。楽しい事実はまだセットに当てはまります。Zgarb、入力に繰り返し要素が含まれている可能性がありますか、またはこれはエラーでしたか?
xnor

@xnorこれは少し前に私の研究で出てきたものです。このプレプリントに16ページの証拠がありますが、基本的にはあなたのものと同じです。
ズガルブ

1
@xnor直感的に、いつでも、0と1の両方が勝ちの選択肢である場合、アリスまたはボブのいずれかが次の動きを選択できます。勝利オプションが1つしかない場合、アリスは次に選択する必要があります。したがって、文字列の選択肢の数は、勝つ戦略の選択肢の数と同じです。厳密ではありませんが、説得力があります。
アルキミスト

回答:


1

Dyalog APL、59バイト

{(a≡,⊂⍬)∨0=⍴a←∪⍵:a⋄(∇h/t)(('A',¨∪),'B',¨∩)∇(~h←⊃¨a)/t←1↓¨a}

@xnorのソリューションと同じアルゴリズム。

(a≡,⊂⍬)∨0=⍴a←∪⍵:a
           a←∪⍵    ⍝ "a" is the unique items of the argument
        0=⍴a       ⍝ is it empty?
 a≡,⊂⍬             ⍝ is it a vector that contains the empty vector?
       ∨       :a  ⍝ if any of the above, return "a"

(∇h/t)(('A',¨∪),'B',¨∩)∇(~h←⊃¨a)/t←1↓¨a
                                 t←1↓¨a  ⍝ drop an item from each of "a" and call that "t"
                         ~h←⊃¨a          ⍝ first of each of "a", call that "h", then negate it
                                /        ⍝ use "~h" as a boolean mask to select from "t"
                       ∇                 ⍝ apply a recursive call
(∇h/t)                                   ⍝ use "h" as a boolean mask on "t", then a recursive call
      (('A',¨∪),'B',¨∩)                  ⍝ apply a fork on the results from the two recursive calls:
       ('A',¨∪)                          ⍝   prepend 'A' to each of the intersection
               ,                         ⍝   concatenated with
                'B',¨∪                   ⍝   prepend 'B' to each of the union

13

Python、132

def f(S,n):
 if n<1:return S
 a,b=[f({x[1:]for x in S if x[0]==c},n-1)for c in'01']
 return{'A'+y for y in a|b}|{'B'+y for y in a&b}

実行例:

f({'000','001','010','011','100','101','110','111'},3) == 
{'ABA', 'ABB', 'AAA', 'AAB', 'BBB', 'BBA', 'BAB', 'BAA'}

これは主にアルゴリズムを示すための一種のゴルフです。入力と出力は文字列のセットです。Pythonには、この部分をコンパクトに表現するための適切な機能がないようですので、誰かがこれをより適切な言語で書いたらクールです。

再帰を数学的に表現する方法を次に示します。残念ながら、PPCGにはまだ数学レンダリングが欠けているため、コードブロックを使用する必要があります。

対象のオブジェクトは文字列のセットです。させる|セット組合を代表し、&セットの交差点を表します。

cが文字の場合、のすべての文字列のc#S先頭に文字cを追加することを表しますS。逆に、収縮c\Sを、S最初の文字のc後に続く1文字短い文字列とします0\{001,010,110,111} = {01,10}

文字Sを含む文字列のセットを01、最初の文字で一意に分割できます。

S = 0#(0\S) | 1#(1\S)

次に、f最初の2行に基本ケースを、最後の行に再帰缶を使用して、目的の関数を次のように表現できます。

f({})   = {}
f({''}) = {''}
f(S)    = A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S))

lengthを使用する必要がないことに注意してくださいn

なぜこれが機能するのですか?Aliceが一連の文字列で勝つことができるmove-stringsについて考えてみましょうS

最初のキャラクターがの場合A、アリスは最初の動き(「0」または「1」)を選択して、問題をS0またはに減らすことを選択できますS1。したがって、残りのmove-stringは少なくともf(S0)orのいずれかf(S1)になければならないため、それらのunionを取り|ます。

同様に、最初の文字が「B」の場合、ボブはピックを取得し、アリスの悪い方を選択するため、残りの移動文字列は交差点(&)にある必要があります。

基本ケースSは、最後に空かどうかを単純にチェックします。文字列の長さを追跡している場合n、再帰するたびに1を引くことで、代わりに基底を書くことができます:

f(S) = S if n==0

再帰的な解決策f(S)は、と同じサイズの楽しい事実も説明していSます。基本ケースと帰納的ケースに当てはまります

f(S) = A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S))

我々は持っています

size(f(S)) = size(A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S)))
           = size(A#(f(0\S)|f(1\S))) + size(B#(f(0\S)&f(1\S))))
           = size((f(0\S)|f(1\S))) + size((f(0\S)&f(1\S))))
           = size(f(0\S)) + size(f(1\S))  [since size(X|Y) + size(X&Y) = size(X) + size(Y)]
           = size(0\S) + size(1\S)
           = size(S)

コードを実行するとが得られTypeError: 'int' object is not subscriptableます。実行可能なプログラムへのリンクはありますか?私はちょうどそれを貼り付けて実行しましたprint f([0001,1011,0010],4)
mbomb007

@ mbomb007関数はのように呼び出す必要がありますf({'000','001','010','011','100','101','110','111'},3)。この方法でエラーが発生しますか?
-xnor

ああ、引用符が欠落しているとは思わなかった、ありがとう。また、print f(['0001','1011','0010'],4)
-mbomb007で

nパラメーターに依存せずにプログラムを実行する場合は、n=len(S[0])if S!=[]else 0
mbomb007

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