知恵袋で見つけた判断推理のトランプの問題をPythonで解いてみた。(2)

 知恵袋で見つけた判断推理のトランプの問題Pythonで解いてみました。(^_^;

 トランプの4種類のマークのカードがそれぞれ5枚(うち1枚はエース)、計20枚ある。
これをA,B,C,Dの4人に5枚ずつ配ったところ、それぞれ次のように話した。
A 『同じ種類が4枚ある。エースが2枚ある』
B 『ダイヤが3枚、スペードが1枚、他のマークが1枚ある』
C 『ダイヤが1枚、クラブが3枚ある』
D 『すべての種類のカードがある。エースが1枚ある』
 このとき、次のア~ウのうち、常に正しいといえるものの組み合わせはどれか。
ア、ハートを持っている者は、スペードを持っている。
イ、スペードを持っている者は、ダイヤを持っている。
ウ、クラブを持っている者は、ダイヤを持っている。

 ただし、P,Q,R,XをA,B,C,Dに変えました。また、問題では、エースのことしか触れられていませんが、他は任意ということで番号をK,A,2,3,4と自分で設定しました。
 以前に作った拙プログラムTrump1.pyを雛形にしましたが、ちょっと改良しました。
 問題は、選択肢が大きく変わっていますが、条件部分はあまり変わっていないようです。選択肢の判定には、論理包含Impとall関数を使ってみました。(^_^;

● Trump2.py

# coding: UTF-8
# Trump2.py

import itertools
from time import time

SUIT = 'SDCH'
RANK = 'KA234'
N = len(RANK)   # 1人分の枚数

# sRank: 'K','A','2','3','4'
def countRank(sRank, cards):
    n = RANK.index(sRank)
    count = 0
    for c in cards:
        if c%N==n: count+=1
    return count

# sSuit: 'S','D','C','H'
def countSuit(sSuit, cards):
    n = SUIT.index(sSuit)
    count = 0
    for c in cards:
        if c//N==n: count+=1
    return count

def countSuits(cards):
    return [countSuit(SUIT[i],cards) for i in range(4)]

# suitとrankから通し番号を得る
def getN(sSuit,sRank):
    return SUIT.index(sSuit)*N+RANK.index(sRank)

def toStrCards(cards):
    li = []
    for c in cards:
        li.append(SUIT[c//N]+RANK[c%N])
    return "(" + ','.join(li) + ")"

# 論理包含Imp
def Imp(p,q):
    return not p or q

# 確実にいえる選択肢を得る
def getAns(cho,lbl='12345'):
    return ','.join([lbl[i] for i in range(len(cho)) if cho[i]])

def main():
    tm = time()  # Timer Start

    choices = [True]*3
    Cards = range(4*N)
    for a in itertools.combinations(Cards,N):
        if not 4 in countSuits(a): continue             # 条件A-1
        if countRank('A',a)!=2: continue                # 条件A-2
        B = tuple(set(Cards)-set(a))                # aを除いた残り
        for b in itertools.combinations(B,N):
            if countSuit('D',b)!=3: continue            # 条件B-1
            if countSuit('S',b)!=1: continue            # 条件B-2
            C = tuple(set(B)-set(b))                # a,bを除いた残り
            for c in itertools.combinations(C,N):
                if countSuit('D',c)!=1: continue        # 条件C-1
                if countSuit('C',c)!=3: continue        # 条件C-2
                d = tuple(set(C)-set(c))            # a,b,cを除いた残り
                if 0 in countSuits(d) : continue        # 条件D-1
                if countRank('A',d)!=1: continue        # 条件D-2
                # チェックを潜り抜けたものを表示
##                print(toStrCards(a),toStrCards(b),toStrCards(c),toStrCards(d))
                # 選択肢のチェック
                li = [a,b,c,d]
                choices[0] &= all([Imp(countSuit('H',x)!=0,countSuit('S',x)!=0) for x in li])
                choices[1] &= all([Imp(countSuit('S',x)!=0,countSuit('D',x)!=0) for x in li])
                choices[2] &= all([Imp(countSuit('C',x)!=0,countSuit('D',x)!=0) for x in li])

    print(u"∴%s"%getAns(choices,'アイウ'))
    print("Runtime : %.3f [sec]"%(time()-tm))   # Timer Stop & Disp

if __name__ == '__main__':
    main()

●実行結果

∴ア,ウ
Runtime : 6.953 [sec]

※参考URL
組み込み関数 — Python 3.9.0 ドキュメント
知恵袋で見つけた判断推理のトランプの問題をPythonで解いてみた。 - rscのブログ
知恵袋で見つけた判断推理のトランプの問題をPythonで解いてみた。(3) - rscのブログ