判断推理の座席の位置関係の問題をPythonで解いてみた。(2)

 ネットで見つけた判断推理の座席の位置関係の問題Pythonで解いてみました。(^_^;

 図のように机が配置されている教室で、黒板に向かって座っているA~Lの12人の生徒の席順について、次のア~キのことが分かっている。
ア Aの両隣にはBとCが座っている。
イ Bのすぐ後ろにはDが座っている。
ウ Cのすぐ前にはEが座っている。
エ Dの隣にはLが座っている。
オ Fの隣にはKが座っている。
カ Gのすぐ後ろにはJが座っている。
キ Hの両隣にはGとIが座っている。
以上から判断して、確実にいえるのはどれか。
1 Aのすぐ後ろにはFが座っている。
2 Eの隣にはGが座っている。
3 Hのすぐ後ろにはAが座っている。
4 Kの隣にはBが座っている。
5 Lのすぐ前にはJが座っている。

+------------------+
|  ==============  |
|       黒板       |
| [ 0][ 1][ 2][ 3] |
|  ◎  ◎  ◎  ◎  |
| [ 4][ 5][ 6][ 7] |
|  ◎  ◎  ◎  ◎  |
| [ 8][ 9][10][11] |
|  ◎  ◎  ◎  ◎  |
+------------------+

(注)◎は各生徒の座席位置を示す

 ただし、各座席に0~11の通し番号を付けました。
 12個の順列は時間がかかるので枝が早く刈れそうなA~Eのグループと残りのグループに分け、A~Eのグループだけのマップを先に作成して、残りを順列で回してみました。(^_^;
 前半のA~Eのグループだけのマップを作成する場合、同じものを含む順列を使ってもいいですが、「set(itertools.permutations())」は、
全要素数が大きいと遅くなってしまうようなので、結果の出力数が少ないと速くなる拙ブログのPermSame1.pyで用いた「genPerm()」を使うとよいです。(^_^;
 ちなみに、subStr()関数は、Perlのsubstr()関数を参考にして作りました。ただし、Perlと違って、文字列 s は変更せずに、置換後の文字列を戻り値として返します。(^_^;

● seating4.py

# coding: UTF-8
# seating4c.py

import itertools
from time import time

RM, CM = 3+2, 4+2   # 行・列の最大数
# マップの初期状態(固定)
MAP = '''
######
#0123#
#4567#
#89ab#
######
'''
MAP = MAP.replace('\n','')
MAP1 = '#'*CM+'#....#'*3+'#'*CM
##print(MAP); print(MAP1); exit()

# 通し番号n番目の行数を得る(0スタート)
def getRow(n):
    return n//CM

# 通し番号n番目の列数を得る(0スタート)
def getCol(n):
    return n%CM

# r行c列の通し番号を得る(0スタート)
def getN(r,c):
    if not(0<=r< RM): return -1  # ドメインエラーは-1を返す
    if not(0<=c< CM): return -1
    return r*CM+c

# map上の人 c のすぐ前の人を文字で得る
def getFrontOf(c, map):
    n = map.index(c)
    r,c = getRow(n),getCol(n)
    return map[getN(r-1,c)]

# map上の人 c のすぐ後ろの人を文字で得る
def getBackOf(c, map):
    n = map.index(c)
    r,c = getRow(n),getCol(n)
    return map[getN(r+1,c)]

# 作成途中のマップ m と順列 p から新しいマップを作る
def mkNewMap(m,p):
    r = m
    li = LookUp('.',r)
    for i,n in enumerate(li):
        r = subStr(r,n,1,p[i])
    return r

# リストla中の要素xに対応するリストlb中の要素をリストですべて返す
# lbを省略すると、インデックスのリストを返す
def LookUp(x,la,lb=[]):
    if lb==[]: return [i for i,a in enumerate(la) if a==x]
    return [lb[i] for i,a in enumerate(la) if a==x]

# 文字列 s の m 番目(0スタート)から n 文字分を文字列 t で置換する
# t を省略すると、文字列 s の m 番目から n 文字分を取り出す
def subStr(s,m,n,t=None):
    if t is None: return s[m:m+n]
    return f'{s[:m]}{t}{s[m+n:]}'

# 結果の表示
def PrintResult(c,m):
    print(f"[{c:2d}]")
    print(f"{m[    :CM  ]}")
    print(f"{m[CM  :CM*2]}")
    print(f"{m[CM*2:CM*3]}")
    print(f"{m[CM*3:CM*4]}")
    print(f"{m[CM*4:    ]}")

# 確実にいえる選択肢を得る
def getAns(ch,lb='12345'):
    return ','.join([lb[i] for i,c in enumerate(ch) if c])

def main():
    tm = time() # Timer Start
    choices = [True]*5
    PLACE = '0123456789ab'
    P = 'ABCDE'     # 人(グループ1)
    Q = 'FGHIJKL'   # 人(グループ2)
    count = 0
    Maps = []
    for i in range(12):
        a = MAP.index(PLACE[i])                 # AのMAP上の通し番号
        for b,c in [(a-1,a+1),(a+1,a-1)]:       # B,CのMAP上の通し番号
            if '#' in (MAP[b], MAP[c]): continue        # 条件ア
            d = getN(getRow(b)+1,getCol(b))     # DのMAP上の通し番号
            if MAP[d]=='#': continue                    # 条件イ
            e = getN(getRow(c)-1,getCol(c))     # EのMAP上の通し番号
            if MAP[e]=='#': continue                    # 条件ウ
            pass # チェックを潜り抜けたものを追加
            m = MAP1    # A~Eだけのマップを作成
            for i,n in enumerate([a,b,c,d,e]):
                m = subStr(m,n,1,P[i])
            Maps.append(m)
##    print(Maps); exit()
    for p in Maps:
        for q in itertools.permutations(Q):
            m = mkNewMap(p,q)
            if not ('DL' in m or 'LD' in m): continue   # 条件エ
            if not ('FK' in m or 'KF' in m): continue   # 条件オ
            if not getBackOf('G',m)=='J': continue      # 条件カ
            if not ('GHI' in m or 'IHG' in m): continue # 条件キ
            pass # チェックを潜り抜けたものを表示
            count+=1
            PrintResult(count,m)
            # 選択肢のチェック
            choices[0] &= getBackOf('A',m)=='F'
            choices[1] &= ('EG' in m or 'GE' in m)
            choices[2] &= getBackOf('H',m)=='A'
            choices[3] &= ('KB' in m or 'BK' in m)
            choices[4] &= getFrontOf('L',m)=='J'

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

if __name__ == '__main__':
    main()

●実行結果

[ 1]
######
#EIHG#
#CABJ#
#FKDL#
######
[ 2]
######
#EIHG#
#CABJ#
#KFDL#
######
[ 3]
######
#GHIE#
#JBAC#
#LDFK#
######
[ 4]
######
#GHIE#
#JBAC#
#LDKF#
######
∴5
Runtime : 0.551 [sec]

※参考URL
質問の座席の問題をPythonで解いてみた。 - rscのブログ
知恵袋の判断推理の座席の位置関係の問題をPythonで解いてみた。 - rscのブログ
判断推理の座席の位置関係の問題をPythonで解いてみた。 - rscのブログ