500問を0.4秒で解けるのだから、もうこれ以上速くする必要はないので、問題を解く部分については手をつけない。そのため、NP.pyのMPI対応版からは、問題を解く部分であるsolveNP(filename)は無駄だから消そうと思う。
問題を解くには、NP_ORG.pyを使うことにしよう。
そうしてNP_ORG.pyからsolveNP()を削除してできたNP_GEN.pyは103行になった。
103 NP_GEN.py 173 generator.py 60 solution.py 248 solver.py 127 util.py 711 合計 この問題生成用に書き換えたNP_GEN.pyで問題を生成してみた。
$ python3 NP_GEN.py -g data/Pattern500.txt
中略
No.500 H 23
- - X X - - - - -
- - X - - X - - -
- - - - - X - X X
- X X X - X - - X
- - - - - - - - -
X - - X - X X X -
X X - X - - - - -
- - - X - - - - -
- - - - - X X - -
[0] 0.159188 sec
- - 8 9 - - - - -
- - 6 - - 7 - - -
- - - - - 1 - 2 5
- 1 5 3 - 2 - - 7
- - - - - - - - -
7 - - 1 - 4 8 9 -
8 9 - 2 - - - - -
- - - 4 - - - - -
- - - - - 5 4 - -
total 500 failure 0
Time 881.859514 sec
各問題生成において、リトライ数は[]の中に、その後ろに問題生成に掛かった時間を表記するようにした。
以下に、短くなったNP_GEN.py全体を示すが、機能的な変更はないので、詳しい説明は省略する。
肝心なのは、44行のgenerator.generate(pattern)で、ここで自動生成が行われている。34行からのforは、問題を連続して作成するためである。
# NP(Number Place) Main Module
##
## This class has the main method.
##
##(c) 2019-2023 FUJIWARA Hirofumi, Knowledge Engineering Center, Time Intermedia, Inc.
## This code is licensed under MIT license (see LICENSE.txt for details)
##
import random
import os, sys, time
import numpy as np
import util
import generator
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering=1)
datainput = None
dataoutput = None
random.seed()
def generateNP(filename):
global datainput, dataoutput
problems = util.readPatterns(filename)
# 全問作るループ
start_time = time.perf_counter()
failureCount=0
successCount=0
n=0
for pb in problems:
pattern = pb.pattern
n += 1
str = f"No.{n} H {util.countHint(pattern)}"
print(str)
if dataoutput != None:
print(str,file=dataoutput)
util.printHintBoard(pattern)
st_time = time.perf_counter()
retrycount = generator.generate(pattern) # 自動生成
gen_time = time.perf_counter() - st_time
if retrycount >= 0:
pb.problem = generator.getProblem()
if dataoutput != None:
# print(retrycount,file=dataoutput)
util.printBoard(pb.problem,dataoutput)
print( f"[{retrycount}] {gen_time:06f} sec" )
util.printBoard(pb.problem)
successCount += 1
else:
str = "FAILURE {}".format(-retrycount)
if dataoutput != None:
print(str,file=dataoutput)
print(str)
failureCount += 1
print()
exe_time = time.perf_counter() - start_time
probSize = len(problems)
totalCount = successCount+failureCount
print( f"total {totalCount} failure {failureCount}\n")
print( f"Time\t{exe_time:06f} sec\n" )
def printErrorMessage():
prog = sys.argv[0]
print("===== arguments input error =====")
print(f"python3 {prog} -g pattern_file [problem_file]")
## -------------------- main() ------------------------
def main(args):
global datainput, dataoutput
if len(args) < 2:
printErrorMessage()
return
try:
datainput = open(args[2],'r')
if len(args)>=4:
dataoutput = open(args[3],'w')
except:
print( '*** File open error ***')
return
if args[1] == '-g':
generateNP(args[2])
else:
printErrorMessage()
# -------------------- start from here ------------------------
if __name__ == "__main__":
main(sys.argv)
if datainput != None:
datainput.close()
if dataoutput != None:
dataoutput.close()
ここまでは、MPI版の説明をするのに不要な部分を削除したり、移動・変更しただけに過ぎない。でも、この書き換え、リファクタリングで今後の説明が少しはすっきりしたと思う。
次回以降、これに徐々に手を加えることで、MPI対応版にする方法を示す。