Strategie vincenti per un gioco di costruzione di stringhe


14

sfondo

Alice e Bob giocano un gioco chiamato costruire una parola binaria . Per giocare, devi fissare una lunghezza n >= 0, una serie Gdi nparole binarie lunghezza chiamate il set obiettivo e una nstringa lunghezza tcontenente le lettere Ae B, chiamato ordine di turno . Il gioco dura nturni e, a turno i, il giocatore definito da t[i]seleziona un po ' w[i]. Quando il gioco termina, i giocatori guardano la parola binaria wche hanno costruito. Se questa parola viene trovata nel goal set G, Alice vince la partita; altrimenti vince Bob.

Per esempio, cerchiamo di correzione n = 4, G = [0001,1011,0010]e t = AABA. Alice ottiene il primo turno e sceglie w[0] = 0. Il secondo turno è anche quello di Alice, e lei sceglie w[1] = 0. Bob ha il terzo turno e sceglie w[2] = 0. Nel turno finale, Alice sceglie w[3] = 1. La parola risultante 0001, si trova in G, quindi Alice vince la partita.

Ora, se Bob avesse scelto w[2] = 1, Alice avrebbe potuto scegliere w[3] = 0nel suo turno finale e vincere ancora. Ciò significa che Alice può vincere la partita, indipendentemente da come Bob gioca. In questa situazione, Alice ha una strategia vincente . Questa strategia può essere visualizzata come un albero binario etichettato, che si ramifica ai livelli corrispondenti ai turni di Bob e il cui ogni ramo contiene una parola da G:

A A B A

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

Alice gioca semplicemente seguendo i rami nel suo turno; indipendentemente dal ramo scelto da Bob, Alice alla fine vince.

Ingresso

Ti viene dato come input la lunghezza ne il set Gcome un elenco (possibilmente vuoto) di stringhe di lunghezza n.

Produzione

Il tuo output è l'elenco degli ordini di turno per i quali Alice ha una strategia vincente, che equivale all'esistenza di un albero binario come descritto sopra. L'ordine degli ordini di turno non ha importanza, ma i duplicati sono vietati.

Regole dettagliate

È possibile scrivere un programma completo o una funzione. Nel caso di un programma, è possibile scegliere il delimitatore per l'input e l'output, ma deve essere lo stesso per entrambi. Vince il conteggio dei byte più corto e le scappatoie standard non sono ammesse.

Casi test

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]

Fatto divertente

Il numero di ordini di turno nell'output è sempre uguale al numero di parole nel set di obiettivi.


5
Sono piuttosto incuriosito dal fatto che input e output abbiano le stesse dimensioni. Hai una prova o una citazione per questo fatto? Mi chiedo se esiste un modo per calcolare questa funzione che preserva intuitivamente le dimensioni.
xnor

2
Il tuo caso di test n. 5 contraddice il tuo fatto divertente ...
mbomb007,

3
@ mbomb007 Il test case n. 5 elenca 11101due volte; il fatto divertente vale ancora per i set. Zgarb, l'input può contenere elementi ripetuti o è stato un errore?
xnor

@xnor Questo è qualcosa che è emerso nella mia ricerca qualche tempo fa. Ho una prova in questa prestampa , pagina 16, ma è essenzialmente la stessa della tua.
Zgarb,

1
@xnor Intuitivamente, in ogni turno, se sia 0 che 1 sono scelte vincenti, allora Alice o Bob possono scegliere la mossa successiva. Se c'è solo un'opzione vincente, Alice deve scegliere la prossima. Pertanto, il numero di scelte per la stringa è uguale al numero di scelte della strategia vincente. Difficilmente rigoroso, ma avvincente.
Alchymist

Risposte:


1

Dyalog APL, 59 byte

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

Stesso algoritmo della soluzione di @ 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}

Esempio di esecuzione:

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

Questo è solo un tipo di golf, principalmente per mostrare l'algoritmo. Gli input e output sono insiemi di stringhe. Python non sembra avere le caratteristiche giuste per esprimere parti di questo in modo compatto, quindi sarebbe bello se qualcuno lo scrivesse in un linguaggio più adatto.

Ecco come la ricorsione può essere espressa matematicamente. Sfortunatamente, a PPCG manca ancora il rendering matematico, quindi dovrò usare i blocchi di codice.

Gli oggetti di interesse sono insiemi di stringhe. Lasciate |rappresentare l'unione impostata e &rappresentate l'intersezione impostata.

Se cè un carattere, c#Srappresentiamo anteporre il carattere ca tutte le stringhe S. Al contrario, lascia che la contrazione c\Ssia la stringa di un carattere più corta di Squella che segue il carattere iniziale c, ad es 0\{001,010,110,111} = {01,10}.

Possiamo dividere in modo univoco un set di stringhe Scon caratteri 01dal loro primo carattere.

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

Quindi, possiamo esprimere la funzione desiderata fcome segue, con i casi di base nelle prime due righe e il barattolo ricorsivo nell'ultima riga:

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

Si noti che non è necessario utilizzare la lunghezza n.

Perché funziona? Pensiamo alle mosse che consentono ad Alice di vincere per un set di stringhe S.

Se il primo personaggio è A, Alice può scegliere la prima mossa ('0' o '1'), lasciandola scegliere di ridurre il problema a S0o S1. Quindi ora la stringa di movimento rimanente deve essere in almeno una f(S0)o f(S1), quindi prendiamo la loro unione |.

Allo stesso modo, se il primo carattere è "B", Bob può scegliere, e sceglierà quello peggiore per Alice, quindi la stringa di movimento rimanente deve essere nell'intersezione ( &).

I casi di base controllano semplicemente se Sè vuoto o no alla fine. Se stiamo monitorando la lunghezza delle stringhe n, sottraendo 1 ogni volta che facciamo ricorso, le basi possono invece essere scritte:

f(S) = S if n==0

La soluzione ricorsiva spiega anche il fatto divertente che f(S)ha le stesse dimensioni di S. È vero per i casi di base e per il caso induttivo

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

noi abbiamo

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)

L'esecuzione del codice dà TypeError: 'int' object is not subscriptable. Hai un link a un programma eseguibile? L'ho appena incollato e eseguito conprint f([0001,1011,0010],4)
mbomb007

@ mbomb007 La funzione deve essere invocata come f({'000','001','010','011','100','101','110','111'},3). Ricevi un errore in questo modo?
xnor

Ah, non ho visto che mi mancavano le citazioni, grazie. Funziona anche conprint f(['0001','1011','0010'],4)
mbomb007 il

Se si desidera eseguire il programma in modo nindipendente dai parametri, sarebben=len(S[0])if S!=[]else 0
mbomb007,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.