Ksatria dan Knave

12

Ini adalah .

Dalam tantangan ini, kita akan menulis program / fungsi yang memecahkan teka-teki " Knights and Knaves ".

Latar Belakang

Anda menemukan diri Anda di sebuah pulau ... dll ... setiap orang di pulau kecuali untuk Anda adalah baik ksatria atau penjahat .

Ksatria hanya bisa membuat pernyataan yang benar .

Knave hanya bisa membuat pernyataan palsu .

Saya tidak ingin mendefinisikan "pernyataan," tetapi kami akan mengatakan bahwa pernyataan adalah segala sesuatu yang "benar" atau "salah." Perhatikan bahwa ini tidak termasuk kalimat paradoks.

Untuk keperluan tantangan ini, Anda akan menjumpai sekelompok pulau; mereka akan membuat pernyataan kepada Anda.

Tugas Anda adalah menentukan siapa yang seorang Ksatria dan siapa yang Knave.

Memasukkan:

Anda akan diberi (dalam format apa pun yang masuk akal) informasi berikut:

  • Daftar orang-orang yang hadir. Mereka akan diberi nama dengan karakter alfabet huruf besar "AZ". Batas jumlah orang yang diberlakukan oleh ini tidak akan terlampaui.

  • Pernyataan yang dibuat setiap orang. Lihat di bawah untuk detail penting tentang ini.

Keluaran

Anda kemudian akan menampilkan (dalam format apa pun yang masuk akal) apa yang masing-masing orang. Misalnya, jika ada pemain A B C Ddan Aadalah seorang ksatria, tetapi sisanya adalah para ksatria, Anda bisa menghasilkan

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

Detail penting:

  • Huruf alfabet huruf besar AZ mengacu pada penduduk pulau.

  • Karakter 0(nol) dan 1(satu) masing-masing merujuk pada "Knave" dan "Knight". (Anda dapat menggunakan dua karakter non AZ lainnya, selama Anda tentukan)

  • Setiap pulau yang hadir dapat membuat sejumlah pernyataan, atau mungkin memilih untuk tidak mengatakan apa pun.

  • Operator logika normal dapat digunakan dalam pernyataan ( IS *, DAN, ATAU, TIDAK ). Di atas ini, Hukum dan Kondisional De Morgan dapat digunakan. Berikut ini adalah contoh bagaimana mereka dapat disajikan dalam puzzle yang diucapkan diikuti oleh bagaimana mereka dapat dimasukkan ke dalam program Anda.

(* pada catatan yang lebih teknis. Operator "IS" benar-benar digunakan sebagai penahanan (yang bukan operator logis). Ketika saya mengatakan "A adalah seorang Ksatria", saya benar-benar berarti "A adalah anggota dari set Ksatria ". Operator sejati yang digunakan adalah 'ϵ'. Demi kesederhanaan, kita akan menggunakan '='.)

Saya menggunakan yang berikut ini (Anda dapat menggunakan apa pun, asalkan masuk akal dan konsisten):

  • ^ DAN
  • v ATAU
  • = ADALAH
  • ~ TIDAK
  • => TERSIRAT
  • X:Orang X mengklaim bahwa ...

Orang Z dapat membuat kombinasi dari salah satu dari jenis pernyataan berikut:

Orang Z mengatakan bahwa ...

  1. Orang A adalah seorang Ksatria.

    Z: A = 1

  2. Orang Q adalah seorang Knave.

    Z: Q = 0

  3. Saya seorang Ksatria.

    Z: Z = 1

  4. Orang A adalah Ksatria ATAU Orang B adalah Ksatria.

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

  5. Orang C adalah Ksatria DAN Saya seorang Knave.

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

  6. Orang R BUKAN Ksatria.

    Z: ~( R = 1 )

Di atas ini, input juga dapat menggunakan Hukum De Morgan

  1. BUKANLAH benar bahwa baik orang A maupun orang B adalah Knave

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

  2. Adalah salah bahwa orang A atau orang B adalah Ksatria

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

Akhirnya, persyaratan dan negasinya dapat digunakan

  1. Jika saya seorang Ksatria, maka orang B adalah seorang Knave

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

  2. BUKANLAH jika orang B adalah seorang Ksatria, maka Orang C adalah seorang Knave.

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

Catatan tentang persyaratan

Lihat wikipedia untuk info lebih lanjut.

Pernyataan bersyarat mengambil bentuk p => q , di mana p dan q sendiri adalah pernyataan. p adalah "anteseden" dan q adalah "konsekuen". Berikut ini beberapa info bermanfaat

  • Negasi dari suatu kondisi terlihat seperti ini: ~ (p => q) setara dengan p ^ ~ q

  • Premis palsu menyiratkan apa pun. Yaitu: jika p salah, maka pernyataan apa pun p => q benar, terlepas dari apa q itu. Misalnya: "jika 2 + 2 = 5 maka saya Spiderman" adalah pernyataan yang benar.

Beberapa test case sederhana

Kasus-kasus ini diberikan dengan cara berikut (1) bagaimana kita akan menimbulkan masalah dalam perkataan (2) bagaimana kita dapat mengajukannya ke komputer (3) hasil yang benar yang mungkin diberikan oleh komputer.

  1. Orang A dan Orang B mendekati Anda di jalan dan membuat pernyataan berikut:

    A: B adalah knave atau saya seorang knight.

    B: A adalah seorang ksatria.

Menjawab:

B adalah Ksatria dan A adalah Ksatria.

Memasukkan:

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

Keluaran:


    A = 1
    B = 1
    

  1. Orang A, B, dan F mendekati Anda di jalan dan membuat pernyataan berikut:

    A: Jika saya seorang Ksatria, maka B adalah Knave.

    B: Jika itu benar, maka F juga Knave.

Menjawab:

A adalah Knight, B adalah Knave, F adalah Knight.

Memasukkan

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

Keluaran:


    A = 1
    B = 0
    F = 1
    

  1. Q, X, dan W mendekati Anda di jalan dan membuat pernyataan berikut:

    W: Tidak benar bahwa Q dan X adalah Knights.

    T: Itu benar.

    X: Jika apa yang dikatakan W benar, maka apa yang dikatakan Q itu salah.

Menjawab:

W dan Q adalah Knights. X adalah Knave.

Memasukkan

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

Keluaran


    W = 1
    Q = 1
    X = 0
    

Ada tantangan serupa dari 3 tahun lalu yang berfokus pada penguraian dan tidak mengandung persyaratan atau De Morgan. Dan oleh karena itu, saya berpendapat, cukup berbeda dalam fokus dan implementasi untuk menghindari ini menjadi penipuan.

Tantangan ini ditutup sebentar sebagai penipu. Sejak itu telah dibuka kembali.

Saya mengklaim bahwa tantangan ini, pertama, berbeda dalam fokus. Tantangan lain berfokus pada penguraian bahasa Inggris, ini tidak. Kedua hanya menggunakan AND dan OR sedangkan ini menggunakan kondisi dan memungkinkan untuk memecahkan lebih banyak teka-teki. Pada akhirnya, pertanyaannya adalah apakah jawaban atau tidak dari tantangan itu dapat diganti secara sepele untuk yang satu ini, dan saya percaya bahwa penyertaan kondisional dan negasi bersyarat menambah kompleksitas yang cukup sehingga perubahan yang lebih kuat perlu dilakukan secara berurutan agar sesuai dengan tantangan ini.

Liam
sumber
Apa yang bisa kita simpulkan jika Knave mengatakan (B=1)=>(C=0)? ~((B=1)=>(C=0))atau (B=1)=>(C=1)sesuatu yang lain?
njpipeorgan
Ini tidak mungkin dilakukan dalam waktu kurang dari 5 menit. Masalah ini dikenal sebagai SAT, dan eksponensial dalam kompleksitas. Jadi untuk n = 26 dalam kasus umum (bukan 2 SAT), tidak mungkin diselesaikan pada komputer dalam waktu yang wajar.
Labo
Kasing uji pertama Anda memiliki 2 kemungkinan keluaran. Saat Anda menggunakan logika OR, bisa jadi A:1 B:1atau A:1 B:0karena B B=1bisa salah sementara A masih benar.
Katenkyo
@ njpipeorgan Jika Knave adalah B, dia tidak bisa mengatakan itu. Premis palsu menyiratkan sesuatu dan oleh karena itu pernyataan itu akan benar. Jika Knave karakter yang berbeda, Anda akan mengambil negasi, yang (B=1)^(C=1)sesuai dengan bagaimana kondisi biasanya ditangani
Liam
1
Bagi mereka yang bertanya-tanya, masalah sebenarnya adalah karena saya melihat permintaan input dan dia melihat teka-teki worded. Itu telah diperbaiki
Cameron Aavik

Jawaban:

6

Python 3, 450 342 307 byte

Edit: ternyata saya lupa impor ...

Solusi pertama saya memanfaatkan penamaan yang fleksibel untuk kueri

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

Anda dapat memanggil yang di atas dengan

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

Yang berikutnya di sini menggunakan format kueri yang sama seperti yang terlihat di OP, itu juga tidak memiliki beberapa modifikasi yang saya buat untuk yang pertama. Ini adalah 417 byte karena mengkonversi antara dua format.

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

Dan itu bisa disebut dengan:

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

Mereka berdua kembali

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

Penjelasan Tidak Dikunci:

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

Juga, solusi kedua membutuhkan 3,5 (bukan 3,4) karena penggunaan PEP 448

Cameron Aavik
sumber
1

Mathematica, 80 byte

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

Penjelasan

Fungsi ini Fmengambil dua argumen,

  • c adalah daftar nama semua karakter,
  • s adalah daftar pernyataan, yang masing-masing berisi dua bagian - siapa bilang apa.

Misalnya, ada tiga karakter, Q, X dan W.

characters={q,x,w};

Dan mereka berkata,

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

di mana Truedan Falseberarti Ksatria dan Knave masing-masing. Kemudian

F[characters, statements]

akan memberikan {{q->True, x->False, w->True}}, yang berarti hanya ada satu solusi yang Q dan W adalah Knights sementara X adalah Knave. Jika ada lebih dari satu solusi, hasilnya akan terlihat seperti{{...},{...},...}


Algoritma ini sangat sederhana. {True,False}~Tuples~Length@cmemberikan semua kemungkinan kombinasi Knights and Knaves di antara para karakter. Kemudian Thread[c->#]&/@buat susunan aturan berdasarkan kombinasi ini. Dalam kasus dua karakter A dan B, array akan menjadi

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

Mengganti pernyataan dengan satu baris aturan ini, kita akan mendapatkan tampilan seperti array

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

Kolom pertama dari array ini adalah identitas pembicara, dan kolom kedua menunjukkan apakah pernyataan mereka benar atau salah. Solusi yang valid membutuhkan kesesuaian antara identitas pembicara dan pernyataan mereka. Array di atas berarti bahwa kombinasi ini bukan solusi, karena pembicara kedua, Knight, membuat pernyataan yang salah.

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

melakukan pergantian dan memilih kombinasi yang memenuhi syarat.

njpipeorgan
sumber
1

Ruby, 128

Ini adalah algoritme yang sama dengan yang lainnya, cobalah semua kemungkinan kombinasi knave dan knight dan lihat stik mana. Saya punya yang lain yang sedang saya kerjakan, tapi saya pikir itu akan lebih lama (meskipun lebih menarik).

Input pernyataan harus:

  • & DAN
  • | ATAU
  • == ADALAH
  • ! TIDAK
  • > TERSIRAT
  • X: Orang X mengklaim bahwa ...

Saya juga mengharuskan setiap pernyataan dan sub-pernyataan berada dalam tanda kurung. Satu-satunya masalah dengan versi ini adalah bahwa ia melewati paling banyak 2 ^ 26 iterasi, dan jika mereka tidak semua tahu, setidaknya 2 ^ (26-n) iterasi ! Untuk menempatkan itu dalam perspektif, itu berarti bahwa jika ada dua orang, dan setidaknya satu bukanlah suatu pertalian, itu akan mengambil minimal 16.777.216 iterasi !

Untuk membatasi itu, saya mengirimkan berikut ini di 168 byte. Masuk 26untuk #{o.size}memotongnya kembali ke 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}

Tetapi jika saya bisa menggunakan array orang dan peta pernyataan misalnya:

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

Lalu saya turunkan ke 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}
Bukan itu Charles
sumber