Cavaleiros e Cavaleiros

12

Isso é .

Neste desafio, escreveremos programas / funções que resolvem quebra-cabeças " Cavaleiros e Cavaleiros ".

fundo

Você encontra-se em uma ilha ... etc ... cada pessoa na ilha, exceto para você ou é um cavaleiro ou de um patife .

Cavaleiros só podem fazer afirmações verdadeiras .

Knaves só podem fazer declarações falsas .

Não quero definir rigorosamente "afirmação", mas diremos que uma afirmação é qualquer coisa que seja "verdadeira" ou "falsa". Observe que isso exclui sentenças paradoxais.

Para os propósitos deste desafio, você encontrará grupos de ilhéus; eles farão declarações para você.

Sua tarefa é determinar quem é um cavaleiro e quem é um valete.

Entrada:

Você receberá (em qualquer formato razoável) as seguintes informações:

  • Uma lista das pessoas presentes. Eles serão nomeados com caracteres do alfabeto em maiúsculas "AZ". O limite do número de pessoas imposto por isso não será excedido.

  • As declarações que cada pessoa faz. Veja abaixo detalhes importantes sobre isso.

Resultado

Você então produzirá (em qualquer formato razoável) o que cada pessoa é. Por exemplo, se houvesse jogadores A B C De Afosse um cavaleiro, mas o resto fosse um cavaleiro, você poderia produzir

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

Detalhes importantes:

  • Os caracteres do alfabeto em maiúsculas AZ referem-se aos ilhéus.

  • Os caracteres 0(zero) e 1(um) referem-se a um "Valete" e um "Cavaleiro", respectivamente. (Você pode outros dois caracteres que não sejam AZ, desde que você especifique)

  • Cada ilhéu presente pode fazer qualquer número natural de declarações ou optar por não dizer nada.

  • Os operadores lógicos normais podem ser usados ​​em instruções ( IS *, AND, OR, NOT ). Além disso, as Leis e Condicionais de De Morgan podem ser usadas. A seguir, exemplos de como eles podem ser apresentados em um quebra-cabeça falado, seguidos de como eles podem ser inseridos no seu programa.

(* em uma nota mais técnica. O operador "IS" é realmente usado como contenção (o que não é um operador lógico). Quando digo "A é um cavaleiro", quero dizer realmente "A é um membro do conjunto de Knights ". O verdadeiro operador usado seria 'ϵ'. Por uma questão de simplicidade, usaremos '='.)

Eu uso o seguinte (você pode usar o que for, desde que seja razoável e consistente):

  • ^ E
  • v OU
  • = É
  • ~ NÃO
  • => IMPLIES
  • X:A pessoa X afirma que ...

A pessoa Z pode fazer qualquer combinação de qualquer um dos seguintes tipos de declarações:

A pessoa Z diz que ...

  1. A pessoa A é um cavaleiro.

    Z: A = 1

  2. A pessoa Q é um valete.

    Z: Q = 0

  3. Eu sou um cavaleiro.

    Z: Z = 1

  4. A pessoa A é um cavaleiro OU a pessoa B é um cavaleiro.

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

  5. A pessoa C é um cavaleiro E eu sou um valete.

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

  6. A pessoa R NÃO é um cavaleiro.

    Z: ~( R = 1 )

Além disso, as informações também podem usar as Leis de De Morgan

  1. NÃO é verdade que a pessoa A e a pessoa B são escravas

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

  2. É falso que a pessoa A ou B seja um cavaleiro

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

Finalmente, condicionais e suas negações podem ser usados

  1. Se eu sou um Cavaleiro, a pessoa B é um Valete

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

  2. NÃO é verdade que, se a pessoa B é um cavaleiro, a pessoa C é um valete.

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

Notas sobre condicionais

Confira a Wikipedia para mais informações.

Uma declaração condicional assume a forma p => q , onde p e q são elas mesmas. p é o "antecedente" e q é o "conseqüente". Aqui estão algumas informações úteis

  • A negação de uma condição é assim: ~ (p => q) é equivalente a p ^ ~ q

  • Uma premissa falsa implica qualquer coisa. Ou seja: se p for falso, qualquer afirmação p => q é verdadeira, independentemente do que seja q . Por exemplo: "se 2 + 2 = 5, então eu sou o Homem-Aranha" é uma afirmação verdadeira.

Alguns casos de teste simples

Esses casos são apresentados da seguinte maneira (1) como colocaríamos o problema na fala (2) como poderíamos colocá-lo no computador (3) a saída correta que o computador poderia fornecer.

  1. A pessoa A e a pessoa B abordam você na estrada e fazem as seguintes declarações:

    A: B é um cavaleiro ou eu sou um cavaleiro.

    B: A é um cavaleiro.

Responda:

B é um cavaleiro e A é um cavaleiro.

Entrada:

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

Resultado:


    A = 1
    B = 1
    

  1. As pessoas A, B e F se aproximam de você na estrada e fazem as seguintes declarações:

    A: Se eu sou um Cavaleiro, então B é um Valete.

    B: Se isso é verdade, então F também é um valete.

Responda:

A é um cavaleiro, B é um valete, F é um cavaleiro.

Entrada

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

Resultado:


    A = 1
    B = 0
    F = 1
    

  1. Q, X e W se aproximam de você na estrada e faça as seguintes declarações:

    W: Não é verdade que Q e X são cavaleiros.

    P: Isso é verdade.

    X: Se o que W diz é verdadeiro, o que Q diz é falso.

Responda:

W e Q são cavaleiros. X é um valete.

Entrada

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

Resultado


    W = 1
    Q = 1
    X = 0
    

Há um desafio semelhante de 3 anos atrás que se concentra na análise e não contém condicionais ou de De Morgan. E, portanto, eu diria, é diferente o suficiente no foco e na implementação para evitar que isso seja um engodo.

Este desafio foi brevemente encerrado como um tolo. Desde então, foi reaberto.

Afirmo que esse desafio é, em primeiro lugar, diferente em foco. O outro desafio se concentra na análise em inglês, isso não acontece. Segundo, ele usa apenas AND e OR, enquanto isso usa condicionais e permite a solução de muitos outros quebra-cabeças. No final das contas, a questão é se as respostas desse desafio podem ou não ser trivialmente substituídas por essa, e acredito que a inclusão de condicionais e negações condicionais adiciona complexidade suficiente para que mudanças mais robustas precisem ser feitas para que para se encaixar nesse desafio.

Liam
fonte
O que podemos concluir se um Valete diz (B=1)=>(C=0)? ~((B=1)=>(C=0))ou (B=1)=>(C=1)ou algo mais?
Njpipeorgan
Isso é impossível em menos de 5 minutos. Esse problema é conhecido como SAT e é exponencial em complexidade. Assim, para n = 26 no caso geral (não 2 SAT), é impossível resolver em um computador em um tempo razoável.
Labo
Seu primeiro caso de teste possui 2 saídas possíveis. Como você está usando a lógica OR, pode ser A:1 B:1ou A:1 B:0porque os B B=1podem ser falsos enquanto A ainda é verdadeiro.
precisa
@njpipeorgan Se o Valete é B, ele não pode dizer isso. Uma premissa falsa implica qualquer coisa e, portanto, essa afirmação seria verdadeira. Se o Valete fosse um personagem diferente, você aceitaria a negação, que é de (B=1)^(C=1)acordo com o modo como os condicionais são normalmente tratados.
Liam
1
Para aqueles que se perguntam, o verdadeiro problema era porque eu estava olhando para a consulta de entrada e ele estava olhando para o quebra-cabeça com palavras. Isso foi corrigido
Cameron Aavik

Respostas:

6

Python 3, 450 342 307 bytes

Edit: Acontece que eu esqueci uma importação ...

Minha primeira solução tira vantagem de ter nomes flexíveis para consultas

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

Você pode ligar para o acima com

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

O próximo aqui usa o mesmo formato de consultas do OP, mas também não possui algumas das modificações que fiz no primeiro. É 417 bytes porque converte entre os dois formatos.

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 pode ser chamado por:

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

Ambos retornam

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

Explicação Ungolfed:

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

Além disso, a segunda solução precisa de 3,5 (não 3,4) devido ao uso do PEP 448

Cameron Aavik
fonte
1

Mathematica, 80 bytes

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

Explicação

A função Fusa dois argumentos,

  • c é uma lista dos nomes de todos os personagens,
  • s é uma lista de declarações, cada uma das quais contém duas partes - quem diz o quê.

Por exemplo, existem três caracteres, Q, X e W.

characters={q,x,w};

E eles dizem:

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

onde Truee Falsesignifica Cavaleiros e Cavaleiros, respectivamente. Então

F[characters, statements]

dará {{q->True, x->False, w->True}}, o que significa que há apenas uma solução que Q e W são cavaleiros enquanto X é um valete. Se houver mais de uma solução, a saída será semelhante a{{...},{...},...}


O algoritmo é muito simples. {True,False}~Tuples~Length@cdá todas as combinações possíveis de cavaleiros e cavaleiros entre os personagens. Em seguida, Thread[c->#]&/@construa uma matriz de regras com base nessas combinações. No caso de dois caracteres A e B, a matriz será

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

Substituindo as instruções por uma linha dessas regras, obteremos uma matriz semelhante a

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

A primeira coluna dessa matriz é a identidade dos alto-falantes e a segunda coluna indica se suas declarações são verdadeiras ou falsas. Uma solução válida exige o acordo entre as identidades dos oradores e suas declarações. A matriz acima significa que essa combinação não é uma solução, pois o segundo orador, um Knight, faz uma declaração incorreta.

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

faz as substituições e seleciona as combinações que atendem à condição.

njpipeorgan
fonte
1

Ruby, 128

Este é o mesmo algoritmo que todos os outros, tente todas as combinações possíveis de knaves e knights e veja quais paus. Tenho outro em que estou trabalhando, mas acho que será mais longo (embora mais interessante).

As entradas da instrução devem ser:

  • & E
  • | OU
  • == É
  • ! NÃO
  • > IMPLIES
  • X: A pessoa X afirma que ...

Também exijo que cada declaração e sub-declaração esteja entre parênteses. O único problema com esta versão é que ela passa por no máximo 2 ^ 26 iterações e, se não forem todos knaves, pelo menos 2 ^ (26-n) iterações ! Para colocar isso em perspectiva, isso significa que, se houver duas pessoas, e pelo menos uma não for um knave, serão necessárias no mínimo 16.777.216 iterações !

Para limitar isso, envio o seguinte em 168 bytes. Submarino 26para #{o.size}reduzi-lo 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}

Mas se eu puder usar um conjunto de pessoas e um mapa de declarações, por exemplo:

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

Então eu diminuo para 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}
Não que Charles
fonte