嘘つき族と正直族の問題
何番煎じか知れませんが、おもしろそうなのでやってみました。
import itertools
# プレーヤーとカードデッキと言い分から、矛盾のない答を返す
consistents = (
lambda players: lambda card_deck: lambda statements:
(
(hands, is_each_honest)
for hands in handss(card_deck)
for is_each_honest in is_each_honests
if statements(hands) == is_each_honest
)
)
# それぞれのカードの手のタプルを返すジェネレータ
handss = lambda card_deck: itertools.permutations(card_deck)
# それぞれのプレーヤーが正直かどうか のタプルのタプル (決め打ち2)
is_each_honests = (
(True, False, False, True)
,(False, True, True, False)
)
# データ
players = (0, 1, 2, 3)
card_deck = (1, 2, 3, 4)
statements = lambda hands: (
hands[0] % 2 == 0
, hands[1] in (3, 4)
, hands[1] in (3, 4) # (決め打ち1)
, hands[3] == 1
)
# 関数適用と表示
for e in consistents(players)(card_deck)(statements):
print(e)
# 結果:
((1, 3, 2, 4), (False, True, True, False))
((1, 3, 4, 2), (False, True, True, False))
((1, 4, 2, 3), (False, True, True, False))
((1, 4, 3, 2), (False, True, True, False))
((3, 4, 1, 2), (False, True, True, False))
((4, 2, 3, 1), (True, False, False, True))
決め打ちしてるところがあります。
決め打ち1:「Bは正直者」=>「Bの言ってることは正しい」=>Bの言ってること=>「Bのカードは3か4」 に変換
カードの手が決まればそれぞれの言い分の真偽が決まるので、問題がシンプルになります。
決め打ち2:嘘つき/正直は二人づつ=>BとCは同じ言い分=>BとCは同族=>AとDは逆の方の同族
制約がなければ全部で 6 通りですが、BとCが同族となると、バリエーションは 2 通りまで減少します。
>>> players = 0, 1, 2, 3
# 制約なし
>>> tuple( tuple( not e in liars for e in players ) for liars in itertools.combinations(players, 2))
((False, False, True, True), (False, True, False, True), (False, True, True, False), (True, False, False, True), (True, False, True, False), (True, True, False, False)) # 6 通り
# 制約あり 嘘つきは ADか、でなければBC
>>> tuple( tuple( not e in liars for e in players ) for liars in itertools.combinations(players, 2) if liars in ((0,3),(1,2)))
((False, True, True, False), (True, False, False, True)) # 2 通り
計算してもいいけど、どうせふたつしかないんで、そこは直書きでいいかな、と。
以上のことをふまえて、
もちょっと短く
してみます。
###決め打ち1=>BとCをまとめる
BとCは 言い分 も 正直かどうか も一緒なので。
###決め打ち2=>True/False にする
2通りしかないんだったら、タプルじゃなくて単純な論理値で表現できる。
表示が重要なら、そっちでちょっと工夫すればよい。
import itertools
# プレーヤーとカードデッキと言い分から、矛盾のない答を返す
consistents = (
lambda players: lambda card_deck: lambda statements:
(
(hands, is_AD_honest)
for hands in itertools.permutations(card_deck)
for is_AD_honest in (True, False) # (決め打ち2)
if statements(hands) == (is_AD_honest, not is_AD_honest, is_AD_honest) # (決め打ち1)
)
)
# データ
players = (0, 1, 2, 3)
card_deck = (1, 2, 3, 4)
statements = lambda hands: (
hands[0] % 2 == 0
, hands[1] in (3, 4) # (決め打ち1)
, hands[3] == 1
)
# 関数適用と表示
for hands, is_AD_honest in consistents(players)(card_deck)(statements):
print(
(hands, (is_AD_honest, not is_AD_honest, not is_AD_honest, is_AD_honest)) # (決め打ち2)
)
わかりやすいか はおいといて、ちょっと短くはなりました。
でも、そもそも...
決め打ちしすぎじゃない?
それぞれの言い分が違ったら(たとえば、Cが「Bは嘘つき」と言ったら)、決め打ちした部分は役に立たなくなります。いちからやりなおしです。
それぞれの言い分 statements
の内容を書き換えても動きそうなものはこれです。
import itertools
# プレーヤーとカードデッキと言い分から、矛盾のない答を返す
consistents = (
lambda players: lambda card_deck: lambda statements:
(
(hands, is_each_honest)
for hands in handss(card_deck)
for is_each_honest in is_each_honests(players)
if statements(hands) == is_each_honest
)
)
# それぞれのカードの手のタプルを返すジェネレータ
handss = lambda card_deck: itertools.permutations(card_deck)
# それぞれのプレーヤーが正直かどうか のタプルを返すジェネレータ
is_each_honests = lambda players:(
tuple( e in honests for e in players )
for honests in itertools.combinations(players, 2)
)
# データ
players = (0, 1, 2, 3)
card_deck = (1, 2, 3, 4)
statements = lambda hands: (
hands[0] % 2 == 0
, hands[1] in (3, 4)
, hands[1] in (3, 4)
, hands[3] == 1
)
# 関数適用と表示
for e in consistents(players)(card_deck)(statements):
print(e)
ただし、決め打ち1 でやった 「人への言及 を カードの手に関する言い分に変換」するのは引き続き有効です。