知恵袋で見つけた判断推理の家の位置関係の問題をPythonで解いてみました。(^_^;
次の図のような十字型の道路に面してA~Jの10軒の家が並んでいる。今、次のア~エのことが分かっているとき、Aの家の位置として有り得るのはどれか。ただし、各家の玄関は道路に面して1つであり、図では東西南北の方向は示されていない。
ア Aの家の道路をはさんだ正面の家の東隣にBの家がある。
イ Aの家の玄関は南を向いている。
ウ Cの家は、Eの家の北隣にあり、玄関は西を向いている。
エ Dの家は、Eの家の道路をはさんだ正面にあり、玄関は北を向いている。1 あ 2 い 3 か 4 き 5 こ
| | +--+ +--+ |あ| |い| +--+--+ +--+--+ |う|え| |お|か| --+--+--+ +--+--+-- --+--+--+ +--+--+-- |き|く| |け|こ| +--+--+ +--+--+ | |
拙ブログのプログラムBoardPuz1.pyを雛形にしました。
A~Eまでしか、条件に出てこないので、A~Eとx5個の同じものを含む順列(30240個)で回してみました。(^_^;
今回、選択肢のチェックには、「有り得るのはどれか」ということなので論理和を用いました。
● House1.py
# coding: UTF-8 # House1.py from time import time import itertools RM, CM = 6, 7 # 行・列の最大数 # マップの初期状態 MAP = ''' +-----+ | ア#イ | |ウエ#オカ| |#####| |キク#ケコ| +-----+ ''' ##print(MAP) MAP = MAP.replace('\n','') # 通し番号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 # 家の順列pを加えて新しいマップを作る PLACE = 'アイウエオカキクケコ' def mkNewMap(p): r = MAP for n in range(len(p)): if p[n]!='x': r = r.replace(PLACE[n],p[n]) return r # map上の通し番号n番目のとこの上下左右の文字のリストを得る def getSurroundings(n, map): li = [] if n< 0 or RM*CM-1< n: return [] # ドメインエラーなら[]を返す r,c = getRow(n),getCol(n) for i,j in [(-1,0),(1,0),(0,-1),(0,1)]: m = getN(r+i,c+j) if m!=-1: li.append(map[m]) return li # 北が上下左右(UDLR)方向uのとき、東西南北(EWSN)方向eが上下左右方向でどの方向になるか UDLR = 'URDL'*2 EWSN = 'NESW' def getDir(u,e): return UDLR[UDLR.index(u)+EWSN.index(e)] # map上でA~Eの家hの上下左右(UDLR)方向dの隣のものを得る def getNeighborOf(h,d, map): n = map.find(h) li = getSurroundings(n, map) return li['UDLR'.find(d)] # map上でA~Eの家hの道路を挟んで真向かいの家のリストを得る def getHousesAcrossTheRoadFrom(h, map): n = map.find(h) li = getSurroundings(n, map) if li.count('#')==0: return [] r,c = getRow(n),getCol(n) lr = [] for i in range(len(li)): if li[i]=='#': if i==0: m = getN(r-2,c ) elif i==1: m = getN(r+2,c ) elif i==2: m = getN(r ,c-2) elif i==3: m = getN(r ,c+2) lr.append(map[m]) return lr # 結果を表示 def PrintMap(m): for i in range(RM): print(m[i*CM:(i+1)*CM]) ## print('') # 確実にいえる選択肢を得る def getAns(cho,lbl='12345'): return ','.join([lbl[i] for i in range(len(cho)) if cho[i]]) def main(): tm = time() # Timer Start HOUSE = 'ABCDExxxxx' choices = [False]*5 cnt = 0 for d in 'UDLR': # 上下左右 for p in set(itertools.permutations(HOUSE)): # 同じものを含む順列 map = mkNewMap(p) nb = getNeighborOf('B',getDir(d,'W'),map) # Bの西隣 if not nb in getHousesAcrossTheRoadFrom('A', map): continue # 条件ア if not getNeighborOf('A',getDir(d,'S'),map)=='#': continue # 条件イ if not getNeighborOf('E',getDir(d,'N'),map)=='C': continue # 条件ウ if not 'D' in getHousesAcrossTheRoadFrom('E', map): continue # 条件エ if not getNeighborOf('D',getDir(d,'N'),map)=='#': continue # 条件エ pass # チェックを潜り抜けたものだけを表示 cnt+=1 print('[%d] %s %s'%(cnt,d,MAP[map.find('A')])) PrintMap(map) pass # 選択肢のチェック choices[0] |= (map.find('A')==MAP.find('ア')) choices[1] |= (map.find('A')==MAP.find('イ')) choices[2] |= (map.find('A')==MAP.find('カ')) choices[3] |= (map.find('A')==MAP.find('キ')) choices[4] |= (map.find('A')==MAP.find('コ')) print("∴%s"%getAns(choices)) print("Runtime : %.3f [sec]"%(time()-tm)) # Timer Stop & Disp if __name__ == '__main__': main()
●実行結果
[1] U ウ +-----+ | ア#C | |Aエ#Eカ| |#####| |キB#Dコ| +-----+ [2] U エ +-----+ | ア#C | |ウA#EB| |#####| |キク#Dコ| +-----+ [3] U オ +-----+ | C#イ | |ウE#Aカ| |#####| |キD#ケB| +-----+ [4] L エ +-----+ | ア#B | |ウA#オカ| |#####| |CE#Dコ| +-----+ [5] L ク +-----+ | B#イ | |CE#Dカ| |#####| |キA#ケコ| +-----+ [6] R イ +-----+ | ア#A | |ウB#オカ| |#####| |キD#EC| +-----+ ∴2 Runtime : 6.109 [sec]
※参考URL
●日能研のマインスイーパーに似たボードパズルの問題をPythonで解いてみた。 - rscのブログ
●判断推理のロッカーの位置関係の問題をPythonで解いてみた。 - rscのブログ
●判断推理の家の位置関係の問題をPythonで解いてみた。(2) - rscのブログ