Ritter und Schurken

12

Das ist .

In dieser Herausforderung werden wir Programme / Funktionen schreiben, die " Knights and Knaves " -Puzzles lösen .

Hintergrund

Sie befinden sich auf einer Insel ... usw. ... jeder Mensch auf der Insel außer Ihnen ist entweder ein Ritter oder ein Schurke .

Ritter können nur wahre Aussagen machen.

Messer können nur falsche Aussagen machen.

Ich möchte "Aussage" nicht rigoros definieren, aber wir werden sagen, dass eine Aussage alles ist, was entweder "wahr" oder "falsch" ist. Beachten Sie, dass dies paradoxe Sätze ausschließt.

Für diese Herausforderung werden Sie auf Gruppen von Inselbewohnern treffen. sie werden dir gegenüber aussagen machen.

Ihre Aufgabe ist es, festzustellen, wer ein Ritter und wer ein Schurke ist.

Eingang:

Sie erhalten (in einem angemessenen Format) die folgenden Informationen:

  • Eine Liste der anwesenden Personen. Sie werden mit Großbuchstaben "AZ" benannt. Die dadurch auferlegte Personenanzahl wird nicht überschritten.

  • Die Aussagen, die jede Person macht. Im Folgenden finden Sie wichtige Details dazu.

Ausgabe

Sie werden dann (in jedem vernünftigen Format) ausgeben, was jede Person ist. Wenn zum Beispiel Spieler da wären A B C Dund Aein Ritter ist, der Rest aber ein Schurke, könnten Sie ausgeben

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

Wichtige Details:

  • Großbuchstaben von AZ beziehen sich auf Inselbewohner.

  • Die Zeichen 0(Null) und 1(Eins) beziehen sich jeweils auf einen "Knave" und einen "Knight". (Sie können zwei beliebige andere Zeichen als AZ verwenden, sofern Sie dies angeben.)

  • Jeder anwesende Insulaner kann eine natürliche Anzahl von Aussagen machen oder sich dafür entscheiden, nichts zu sagen.

  • Die normalen logischen Operatoren können in Anweisungen verwendet werden ( IS *, AND, OR, NOT ). Darüber hinaus können die Gesetze und Bedingungen von De Morgan angewendet werden. Die folgenden Beispiele zeigen, wie sie in einem gesprochenen Puzzle dargestellt werden und wie sie in Ihr Programm eingegeben werden können.

(* eher technisch. Der Operator "IS" wird wirklich als Containment verwendet (was kein logischer Operator ist). Wenn ich "A ist ein Ritter" sage, meine ich wirklich "A ist ein Mitglied der Menge von" Knights ". Der wahre Operator wäre 'ϵ'. Der Einfachheit halber werden wir stattdessen '=' verwenden.)

Ich verwende Folgendes (Sie können alles verwenden, solange es vernünftig und konsistent ist):

  • ^ UND
  • v ODER
  • = IST
  • ~ NICHT
  • => IMPLIZIERT
  • X:Person X behauptet, dass ...

Person Z kann eine beliebige Kombination der folgenden Aussagen treffen:

Person Z sagt, dass ...

  1. Person A ist ein Ritter.

    Z: A = 1

  2. Person Q ist ein Schurke.

    Z: Q = 0

  3. Ich bin ein Ritter.

    Z: Z = 1

  4. Person A ist ein Ritter ODER Person B ist ein Ritter.

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

  5. Person C ist ein Ritter und ich bin ein Schurke.

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

  6. Person R ist KEIN Ritter.

    Z: ~( R = 1 )

Darüber hinaus können für die Eingabe auch die Gesetze von De Morgan verwendet werden

  1. Es ist NICHT wahr, dass sowohl Person A als auch Person B Schurken sind

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

  2. Es ist falsch, dass entweder Person A oder Person B ein Ritter ist

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

Schließlich können Bedingungen und deren Negationen verwendet werden

  1. Wenn ich ein Ritter bin, ist Person B ein Schurke

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

  2. Es ist NICHT wahr, dass, wenn Person B ein Ritter ist, Person C ein Schurke ist.

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

Hinweise zu Bedingungen

Check out Wikipedia für weitere Informationen.

Eine bedingte Anweisung hat die Form p => q , wobei p und q selbst Anweisungen sind. p ist die "Vorgeschichte" und q ist die "Konsequenz". Hier finden Sie einige nützliche Informationen

  • Die Negation einer Bedingung sieht folgendermaßen aus: ~ (p => q) entspricht p ^ ~ q

  • Eine falsche Prämisse impliziert alles. Das heißt: Wenn p falsch ist, dann ist jede Aussage p => q wahr, unabhängig davon, was q ist. Zum Beispiel: "Wenn 2 + 2 = 5, dann bin ich Spiderman" ist eine wahre Aussage.

Einige einfache Testfälle

In diesen Fällen wird folgendermaßen angegeben (1), wie wir das Problem in der Sprache darstellen würden (2), wie wir es dem Computer darstellen könnten (3), welche Ausgabe der Computer möglicherweise korrekt ausgibt.

  1. Person A und Person B nähern sich Ihnen unterwegs und machen folgende Aussagen:

    A: B ist ein Schurke oder ich bin ein Ritter.

    B: A ist ein Ritter.

Antworten:

B ist ein Ritter und A ist ein Ritter.

Eingang:

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

Ausgabe:


    A = 1
    B = 1
    

  1. Personen A, B und F nähern sich Ihnen auf der Straße und geben die folgenden Aussagen ab:

    A: Wenn ich ein Ritter bin, dann ist B ein Schurke.

    B: Wenn das wahr ist, dann ist F auch ein Schurke.

Antworten:

A ist ein Ritter, B ist ein Knave, F ist ein Ritter.

Eingang

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

Ausgabe:


    A = 1
    B = 0
    F = 1
    

  1. Q, X und W nähern sich Ihnen auf der Straße und machen die folgenden Aussagen:

    W: Es ist nicht wahr, dass sowohl Q als auch X Ritter sind.

    F: Das stimmt.

    X: Wenn das, was W sagt, wahr ist, dann ist das, was Q sagt, falsch.

Antworten:

W und Q sind Ritter. X ist ein Schurke.

Eingang

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

Ausgabe


    W = 1
    Q = 1
    X = 0
    

Es gibt eine ähnliche Herausforderung von vor 3 Jahren, die sich auf das Parsen konzentriert und keine Bedingungen oder De Morgans enthält. Ich würde daher argumentieren, dass Fokus und Implementierung unterschiedlich genug sind, um zu vermeiden, dass dies ein Betrug ist.

Diese Herausforderung wurde kurzzeitig als Betrug abgeschlossen. Es wurde seitdem wieder geöffnet.

Ich behaupte, dass diese Herausforderung zunächst einmal einen anderen Schwerpunkt hat. Die andere Herausforderung konzentriert sich auf das Parsen in Englisch, dies jedoch nicht. Zweitens werden nur UND und ODER verwendet, wohingegen Bedingungen verwendet werden und das Lösen vieler weiterer Rätsel ermöglicht wird. Letztendlich stellt sich die Frage, ob Antworten aus dieser Herausforderung trivial durch Antworten aus dieser Herausforderung ersetzt werden können, und ich glaube, dass die Einbeziehung von Bedingungen und bedingten Negationen eine ausreichende Komplexität mit sich bringt, die robustere Änderungen erforderlich macht um dieser Herausforderung gerecht zu werden.

Liam
quelle
Was können wir daraus schließen, wenn ein Knave sagt (B=1)=>(C=0)? ~((B=1)=>(C=0))oder (B=1)=>(C=1)oder was anderes?
njpipeorgan
Dies ist nicht in weniger als 5 Minuten möglich. Dieses Problem ist als SAT bekannt und von exponentieller Komplexität. Somit ist es für n = 26 im allgemeinen Fall (nicht 2 SAT) unmöglich, auf einem Computer in angemessener Zeit zu lösen.
Labo
Dein erster Testfall hat 2 mögliche Ausgaben. Wie Sie logisch verwenden OR, könnte es sein, A:1 B:1oder A:1 B:0weil B B=1falsch sein könnte, während A noch wahr wäre.
Katenkyo
@njpipeorgan Wenn der Knave B ist, kann er das nicht sagen. Eine falsche Prämisse impliziert alles und daher wäre diese Aussage wahr. Wenn der Schurke was für ein anderer Charakter wäre, würdest du die Verneinung nehmen, die so ist, (B=1)^(C=1)wie mit Bedingungen normalerweise umgegangen wird
Liam
1
Für diejenigen, die sich wunderten, war das eigentliche Problem, dass ich die Eingabeabfrage betrachtete und er das Worträtsel betrachtete. Das wurde behoben
Cameron Aavik

Antworten:

6

Python 3, 450 342 307 Bytes

Edit: es stellt sich heraus, dass ich einen Import vergessen habe ...

Meine erste Lösung nutzt die flexible Benennung von Abfragen

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]]

Sie können die obige mit anrufen

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

Das nächste hier verwendet das gleiche Format von Abfragen wie im OP. Es enthält auch keine der Änderungen, die ich am ersten vorgenommen habe. Es ist 417 Bytes, da es zwischen den beiden Formaten konvertiert.

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]]

Und es kann aufgerufen werden von:

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

Sie kehren beide zurück

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

Ungolfed Erklärung:

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]]

Auch die zweite Lösung benötigt aufgrund der Verwendung von PEP 448 3,5 (nicht 3,4)

Cameron Aavik
quelle
1

Mathematica, 80 Bytes

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

Erläuterung

Die Funktion Fakzeptiert zwei Argumente:

  • c ist eine Liste aller Charakternamen,
  • s ist eine Liste von Aussagen, von denen jede zwei Teile enthält - wer sagt was.

Zum Beispiel gibt es drei Zeichen, Q, X und W.

characters={q,x,w};

Und sie sagen:

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

wo Trueund Falsebedeutet Ritter bzw. Knaves. Dann

F[characters, statements]

wird geben {{q->True, x->False, w->True}}, was bedeutet, dass es nur eine Lösung gibt, dass Q und W Ritter sind, während X ein Knave ist. Wenn es mehr als eine Lösung gibt, sieht die Ausgabe so aus{{...},{...},...}


Der Algorithmus ist sehr einfach. {True,False}~Tuples~Length@cgibt alle möglichen Kombinationen von Knights und Knaves unter den Charakteren an. Erstellen Sie dann Thread[c->#]&/@ein Array von Regeln, die auf diesen Kombinationen basieren. Im Fall von zwei Zeichen A und B ist das Array

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

Wenn Sie die Anweisungen durch eine Zeile dieser Regeln ersetzen, erhalten Sie ein Array, das wie folgt aussieht

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

Die erste Spalte dieses Arrays gibt die Identität der Sprecher an, und die zweite Spalte gibt an, ob ihre Aussagen wahr oder falsch sind. Eine gültige Lösung erfordert die Übereinstimmung der Identität der Sprecher mit ihren Aussagen. Das obige Array bedeutet, dass diese Kombination keine Lösung ist, da der zweite Sprecher, ein Ritter, eine falsche Aussage macht.

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

führt die Ersetzungen durch und wählt die Kombinationen aus, die die Bedingung erfüllen.

njpipeorgan
quelle
1

Rubin, 128

Dies ist der gleiche Algorithmus wie für alle anderen, probieren Sie alle möglichen Kombinationen von Schurken und Rittern und sehen Sie, welche Stöcke. Ich habe eine andere, an der ich arbeite, aber ich denke, sie wird länger dauern (wenn auch interessanter).

Die Anweisungseingaben müssen sein:

  • & UND
  • | ODER
  • == IST
  • ! NICHT
  • > IMPLIZIERT
  • X: Person X behauptet, dass ...

Ich fordere auch, dass jede Anweisung und jede Unteranweisung in Klammern steht. Das einzige Problem bei dieser Version ist, dass sie höchstens 2 ^ 26 Iterationen durchläuft, und wenn sie nicht alle Knappen sind, mindestens 2 ^ (26-n) Iterationen ! In der Perspektive bedeutet dies, dass bei zwei Personen und mindestens einer Person, die kein Schurke ist, mindestens 16.777.216 Iterationen erforderlich sind !

Um dies einzuschränken, übermittle ich Folgendes mit 168 Bytes. Sub in 26for #{o.size}, um es auf 161 zurückzuschneiden.

->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}

Aber wenn ich stattdessen eine Reihe von Personen und eine Karte von Aussagen verwenden kann, zB:

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

Dann kriege ich es auf 128 runter.

->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}
Nicht dieser Charles
quelle