Knights and Knaves


12

Questo è .

In questa sfida, scriveremo programmi / funzioni che risolvono enigmi " Knights and Knaves ".

sfondo

Ti ritrovi su un'isola ... ecc. ... Ogni persona sull'isola tranne te è un cavaliere o un furfante .

I cavalieri possono solo fare affermazioni vere .

I furfanti possono solo fare false dichiarazioni.

Non voglio definire rigorosamente "affermazione", ma diremo che un'affermazione è qualsiasi cosa che sia "vera" o "falsa". Si noti che ciò esclude frasi paradossali.

Ai fini di questa sfida, incontrerai gruppi di isolani; ti faranno delle dichiarazioni.

Il tuo compito è determinare chi è un cavaliere e chi è un furfante.

Ingresso:

Ti verranno fornite (in qualsiasi formato ragionevole) le seguenti informazioni:

  • Un elenco delle persone presenti. Verranno nominati con i caratteri alfabetici "AZ". Il limite al numero di persone imposto da questo non sarà superato.

  • Le dichiarazioni che ogni persona fa. Vedi sotto per dettagli importanti su questo.

Produzione

Verrà quindi emesso (in qualsiasi formato ragionevole) ciò che ogni persona è. Ad esempio, se ci fossero giocatori A B C Ded Aè un cavaliere, ma il resto sono furfanti, potresti produrre

A: 1
B: 0
C: 0
D: 0

Dettagli importanti:

  • I caratteri alfabetici in maiuscolo AZ si riferiscono agli isolani.

  • I caratteri 0(zero) e 1(uno) si riferiscono rispettivamente a "Fante" e "Cavaliere". (Puoi specificare altri due caratteri non AZ, purché tu specifichi)

  • Ogni isolano presente può fare qualsiasi numero naturale di affermazioni o può scegliere di non dire nulla.

  • I normali operatori logici possono essere utilizzati nelle istruzioni ( IS *, AND, OR, NOT ). Inoltre, possono essere utilizzate le leggi e le condizioni di De Morgan . I seguenti sono esempi di come potrebbero essere presentati in un puzzle parlato seguito da come potrebbero essere inseriti nel tuo programma.

(* su una nota più tecnica. L'operatore "IS" è veramente usato come contenimento (che non è un operatore logico). Quando dico "A è un cavaliere", intendo davvero "A è un membro dell'insieme di Knights ". Il vero operatore utilizzato sarebbe 'ϵ'. Per semplicità, useremo invece '='.)

Uso quanto segue (puoi usare qualunque cosa, purché sia ​​ragionevole e coerente):

  • ^ E
  • v O
  • = È
  • ~ NON
  • => IMPLICA
  • X:La persona X afferma che ...

La persona Z potrebbe fare qualsiasi combinazione di uno dei seguenti tipi di affermazioni:

La persona Z dice che ...

  1. La persona A è un cavaliere.

    Z: A = 1

  2. La persona Q è un furfante.

    Z: Q = 0

  3. Sono un cavaliere.

    Z: Z = 1

  4. La persona A è un cavaliere O la persona B è un cavaliere.

    Z: ( A = 1 ) v ( B = 1)

  5. La persona C è un cavaliere E io sono un furfante.

    Z: ( C = 1 ) ^ ( Z = 0 )

  6. La persona R NON è un cavaliere.

    Z: ~( R = 1 )

Inoltre, l'input può anche usare le leggi di De Morgan

  1. NON è vero che sia la persona A che la persona B siano furfanti

    Z: ~( ( A = 0 ) ^ ( B = 0 ) )

  2. È falso che una persona A o una persona B sia un cavaliere

    Z: ~( ( A = 1 ) v ( B = 1) )

Infine, possono essere usati i condizionali e le loro negazioni

  1. Se io sono un cavaliere, allora la persona B è un furfante

    Z: ( Z = 1 ) => ( B = 0 )

  2. NON è vero che se la persona B è un cavaliere, allora la persona C è un furfante.

    Z: ~( ( B = 1 ) => ( C = 0 ) )

Note sui condizionali

Dai un'occhiata a Wikipedia per maggiori informazioni.

Un'istruzione condizionale assume la forma p => q , dove p e q sono esse stesse istruzioni. p è l '"antecedente" e q è il "conseguente". Ecco alcune informazioni utili

  • La negazione di una condizione è simile alla seguente: ~ (p => q) è equivalente a p ^ ~ q

  • Una premessa errata implica qualcosa. Cioè: se p è falso, qualsiasi istruzione p => q è vera, indipendentemente da q . Ad esempio: "if 2 + 2 = 5 then I am Spiderman" è una vera affermazione.

Alcuni semplici casi di test

Questi casi sono indicati nel modo seguente (1) come potremmo porre il problema nel discorso (2) come potremmo porlo al computer (3) l'output corretto che il computer potrebbe fornire.

  1. La persona A e la persona B ti avvicinano sulla strada e fanno le seguenti dichiarazioni:

    A: B è un furfante o io sono un cavaliere.

    B: A è un cavaliere.

Risposta:

B è un cavaliere e A è un cavaliere.

Ingresso:

A B        # Cast of Characters
A: ( B = 0 ) v ( A = 1)
B: A = 1

Produzione:


    A = 1
    B = 1
    

  1. Le persone A, B e F ti avvicinano sulla strada e fanno le seguenti dichiarazioni:

    A: Se sono un cavaliere, allora B è un furfante.

    B: Se questo è vero, anche F è un furfante.

Risposta:

A è un cavaliere, B è un furfante, F è un cavaliere.

Ingresso

A B F
A: ( A = 1 ) => ( B = 0 )
B: ( A = 1 ) => ( F = 0 ) 

Produzione:


    A = 1
    B = 0
    F = 1
    

  1. Q, X e W si avvicinano alla strada e fanno le seguenti affermazioni:

    W: Non è vero che sia Q sia X siano Knights.

    D: Questo è vero.

    X: Se ciò che dice W è vero, allora ciò che dice Q è falso.

Risposta:

W e Q sono cavalieri. X è un furfante.

Ingresso

Q X W
W: ~( ( Q = 1 ) ^ ( X = 1 ) )
Q: W = 1
X: ( W = 1 ) => ( Q = 0 )

Produzione


    W = 1
    Q = 1
    X = 0
    

C'è una sfida simile di 3 anni fa che si concentra sull'analisi e non contiene condizionali o De Morgan. E quindi, direi, è abbastanza diverso nella messa a fuoco e nell'attuazione per evitare che questo sia un inganno.

Questa sfida è stata brevemente chiusa come un duplicato. Da allora è stato riaperto.

Sostengo che questa sfida sia, innanzitutto, diversa in primo piano. L'altra sfida si concentra sull'analisi dell'inglese, no. In secondo luogo utilizza solo AND e OR mentre questo utilizza condizionali e consente la risoluzione di molti più enigmi. Alla fine della giornata, la domanda è se le risposte a quella sfida possano essere sostituite banalmente a questa sfida, e credo che l'inclusione di condizionali e negazioni condizionali aggiunga una complessità sufficiente che sarebbe necessario apportare cambiamenti più solidi per per rispondere a questa sfida.


Cosa possiamo concludere se dice un furfante (B=1)=>(C=0)? ~((B=1)=>(C=0))o (B=1)=>(C=1)qualcos'altro?
njpipeorgan,

Questo è impossibile da fare in meno di 5 minuti. Questo problema è noto come SAT ed è esponenziale nella complessità. Quindi per n = 26 nel caso generale (non 2 SAT), è impossibile risolvere su un computer in un tempo ragionevole.
Labo,

Il tuo primo test case ha 2 possibili output. Come stai usando logico OR, potrebbe essere A:1 B:1o A:1 B:0perché B B=1potrebbe essere falso mentre A sarebbe ancora vero.
Katenkyo,

@njpipeorgan Se il Fante è B, non può dirlo. Una premessa errata implica qualcosa e quindi tale affermazione sarebbe vera. Se il Fante fosse un personaggio diverso, prenderesti la negazione, che è (B=1)^(C=1)secondo come vengono normalmente trattati i condizionali
Liam,

1
Per quelli che si chiedevano, il vero problema era perché stavo guardando la query di input e lui stava guardando il puzzle scritto. Ciò è stato risolto
Cameron Aavik il

Risposte:


6

Python 3, 450 342 307 byte

Modifica: si scopre che ho dimenticato un'importazione ...

La mia prima soluzione si avvale di una denominazione flessibile per le query

from functools import*
def g(c,r):c=c.split();l,y=len(c),range;d=[dict((c[i],n>>i&1)for i in y(l))for n in y(2**l)];return[n[1]for n in[[eval(reduce(lambda x,y:x.replace(y,str(d[i][y])),d[i],')and '.join(['not',''][d[i][s[0]]]+'('+s[2:].replace('->','<1or')for s in r)+')')),d[i]]for i in y(len(d))]if n[0]]

Puoi chiamare quello sopra con

g('Q X W', ['W: not( ( Q == 1 ) and ( X == 1 ) )','Q: W == 1', 'X: ( W == 1 ) -> ( Q == 0 )'])

Il prossimo qui usa lo stesso formato di query visto nell'OP, inoltre non ha alcune delle modifiche che ho apportato al primo. Sono 417 byte perché converte tra i due formati.

from functools import*
def g(c,r):c=c.split();l,y=len(c),range;d=[{**dict((c[i],n>>i&1)for i in y(l)),**{'v':'or','^':'and','=':'==','~':'not'}}for n in y(2**l)];f=lambda r,c:reduce(lambda x,y:x.replace(y,str(c[y])),c,('(0<1'+''.join([')^ '+['~',''][c[t[0]]]+'('+t[1]for t in[s.split(":")for s in r]])+')').replace('=>','<1or'));return[dict((o,j) for o,j in n[0].items() if o in c) for n in[[d[i],eval(f(r,d[i]))]for i in y(len(d))]if n[1]]

E può essere chiamato da:

g('Q X W', ['W: ~( ( Q = 1 ) ^ ( X = 1 ) )','Q: W = 1', 'X: ( W = 1 ) => ( Q = 0 )'])

Entrambi ritornano

[{'X': 0, 'W': 1, 'Q': 1}]

Spiegazione non golfata:

from functools import *
def knight_and_knaves(cast,rules):
    # turns 'A B C' into ['A','B','C']
    cast = cast.split()
    # gets all numbers that can fit in len(cast) bits
    bitmasks = range(2 ** len(cast))
    # for every bitmask, apply the value for a bit to the boolean value for each cast member.
    # This returns a dictionary of all possible outcomes.
    d=[dict((cast[i], n>>i & 1) for i in range(len(cast))) for n in bitmasks]
    # Split rules at colon
    rules = [s.split(":")for s in rules]
    # Turns list of rules into one python expression, joins each rule with ')and ', maybe a 'not' depending on if the hypothesis has the rule as a lie, and '('.
    # Also replaces '->' with '<1or' which is equivalent to it. Also starts with '(True' and ends with ')' to resolve missing parentheses
    transform_rules = lambda d, rules: ('(True' + ''.join([')and ' + ['not', ''][d[rule[0]]] + '(' + rule[1].replace('->','<1or') for rule in rules]) + ')')
    # Applys transform_rules on each outcome and evaluates the result, storing it into a list of lists where each element is [outcome, value]
    outcomes=[[d[i],eval(reduce(lambda x,y:x.replace(y,str(d[i][y])),d[i],transform_rules(d[i], rules)))] for i in range(len(d))]
    # Filters outcomes if value is True
    return[n[0]for n in outcomes if n[1]]

Inoltre, la seconda soluzione necessita di 3,5 (non 3,4) a causa dell'uso di PEP 448


1

Mathematica, 80 byte

F[c_,s_]:=Select[Thread[c->#]&/@{True,False}~Tuples~Length@c,And@@Equal@@@s/.#&]

Spiegazione

La funzione Faccetta due argomenti,

  • c è un elenco di tutti i nomi dei personaggi,
  • s è un elenco di affermazioni, ognuna delle quali contiene due parti: chi dice cosa.

Ad esempio, ci sono tre caratteri, Q, X e W.

characters={q,x,w};

E dicono

statements=
   {{w, !((q==True)&&(x==True))   },
    {q, w==True                   },
    {x, Implies[w==True,q==False] }};

dove Truee Falsesignifica rispettivamente cavalieri e furfanti. Poi

F[characters, statements]

darà {{q->True, x->False, w->True}}, il che significa che c'è solo una soluzione che Q e W sono Cavalieri mentre X è un Fante. Se ci sono più soluzioni, l'output sarà simile{{...},{...},...}


L'algoritmo è molto semplice. {True,False}~Tuples~Length@coffre tutte le possibili combinazioni di Cavalieri e Fanti tra i personaggi. Quindi Thread[c->#]&/@costruire una serie di regole basate su queste combinazioni. Nel caso di due caratteri A e B, l'array sarà

{{a->True, b->True },
 {a->True, b->False},
 {a->False,b->True },
 {a->False,b->False}}

Sostituendo le istruzioni con una riga di queste regole, otterremo un array simile

{{True,True},{True,False},{False,False}}

La prima colonna di questo array è l'identità dei relatori e la seconda colonna indica se le loro affermazioni sono vere o false. Una soluzione valida richiede la conformità tra le identità degli oratori e le loro dichiarazioni. L'array sopra indica che questa combinazione non è una soluzione, dal momento che il secondo oratore, un Cavaliere, fa una dichiarazione errata.

Select[...,And@@Equal@@@s/.#&]

fa le sostituzioni e seleziona quelle combinazioni che soddisfano la condizione.


1

Ruby, 128

Questo è lo stesso algoritmo di tutti gli altri, prova tutte le possibili combinazioni di furfanti e cavalieri e vedi quali bastoni. Ne ho un altro su cui sto lavorando, ma penso che sarà più lungo (anche se più interessante).

Gli input dell'istruzione devono essere:

  • & E
  • | O
  • == È
  • ! NON
  • > IMPLICA
  • X: La persona X afferma che ...

Richiedo anche che ogni frase e sotto-dichiarazione sia tra parentesi. L'unico problema con questa versione è che attraversa al massimo 2 ^ 26 iterazioni e, se non sono tutti furfanti, almeno 2 ^ (26-n) iterazioni ! Per dirlo in prospettiva, ciò significa che se ci sono due persone, e almeno una non è un furfante, ci vorranno un minimo di 16.777.216 iterazioni !

Per limitare ciò, invio quanto segue a 168 byte. Sottrai 26per #{o.size}ridurlo a 161.

->s{o=s[/.*?$/].split
i=0
eval h=o.zip(("%0#{o.size}b"%i+=1).chars).map{|k|k*?=}*?;until h&&o.all?{|t|!s[/#{t}:(.*)$/]||eval("(#{t}<1)^(#{$1.gsub(?>,'!=true||')})")}
h}

Ma se invece posso usare un array di persone e una mappa di dichiarazioni, ad esempio:

c[[?A, ?B],
  {
    ?A=> "( B == 0 ) | ( A == 1)",
    ?B=> "A == 1"
  }
 ]

Quindi lo prendo a 128.

->o,s{i=0
eval h=o.zip(("%026b"%i+=1).chars).map{|k|k*?=}*?;until h&&s.all?{|t,k|eval("(#{t}<1)^(#{k.gsub(?>,'!=true||')})")}
h}
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.