Raspberry Pi スパコン (12) ナンプレ


2023年 05月 02日

問題生成専用に改編

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対応版にする方法を示す。