一般にr個の変数にn個の数を割り当てる「場合の数」は、perm(n,r)通り。
したがって、高々perm(10,10)=10!通りを検証すればよいことになるが、、、
実際、現状のパソコンでは負荷になるものでもない。
また、一度解答できればプログラムもそれっきりになる。(one write)
論理的要素を一部考慮して、
枝刈りをすること(バックトラック法)で「場合の数」を減らすことができる。
●アルゴリズムの概要
加算は、一の位から順に計算していくので、異なる文字を一の位から順に割り付ける。
一の位(E+O+R=N)を算出するには、
a(i):1234xxxxxx
c$="EORNWUTVFS"
まで数が埋まっていればよい。
そこで MOD(1+2+3,10)=4 ? を確認して、不成立ならxxxxxxはどう組替えてもこの「順列」は成立しない。
したがって、6!(xの数)通りは検証する必要がないことになる。
一つ下の位が成立したなら上の位で同じことを行う。
!覆面算(nPr順列+枝狩り)
DECLARE EXTERNAL SUB F.perm !外部手続き、変数を宣言する
DECLARE EXTERNAL NUMERIC F.ANSWER_COUNT
LET t0=TIME
CALL perm(0, 1) !キャリーは0、一の位から
IF ANSWER_COUNT=0 THEN PRINT "解なし"
PRINT "計算時間=";TIME-t0
END
MODULE F
SHARE STRING c$
! ONE ↓※一の位からa()に順に割り付ける
! TWO ↓
!+ FOUR ↓
!------
! SEVEN ↓
LET c$="EORNWUTVFS" !異なる文字 ←←←←← ※差し替え
IF LEN(c$)>10 THEN
PRINT "指定できる文字は10文字以内です。"
STOP
END IF
SHARE NUMERIC a(10) !文字に設定した数値(0~9)
MAT a=ZER(LEN(c$)) !文字数と同数に調整する
SHARE NUMERIC f(0 TO 9) !文字に割り当てる数(0~9)フラグ用 ※
MAT f=ZER
PUBLIC NUMERIC ANSWER_COUNT !解答数
LET ANSWER_COUNT=0
EXTERNAL FUNCTION fnVAL(w$) !文字表現の数を数値に換える
LET v=0
FOR i=1 TO LEN(w$)
LET p=POS(c$,w$(i:i))
LET v=v*10+a(p)
NEXT i
LET fnVAL=v
END FUNCTION
PUBLIC SUB perm
EXTERNAL SUB perm(cy, L) !nPr順列での数の組を生成する
FOR nm=LBOUND(f) TO UBOUND(f) !重複を避けて、0~9の数字を割り当てる
IF f(nm)=0 THEN
LET f(nm)=1 !使用中フラグをONにする
LET a(L)=nm !L番目を数nmとする
!----- ↓↓↓↓↓ ----- ※差し替え
SELECT CASE c$(L:L)
CASE "N" !一の位を確認する
LET k=fnVAL("E")+fnVAL("O")+fnVAL("R")
IF MOD(k,10)=fnVAL("N") THEN
CALL perm(INT(k/10), L+1)
END IF
CASE "U" !十の位を確認する
LET k=fnVAL("N")+fnVAL("W")+fnVAL("U") + cy
IF MOD(k,10)=fnVAL("E") THEN
CALL perm(INT(k/10), L+1)
END IF
CASE "V" !百の位を確認する
LET k=fnVAL("O")+fnVAL("T")+fnVAL("O") + cy
IF fnVAL("O")>0 AND fnVAL("T")>0 AND MOD(k,10)=fnVAL("V") THEN
CALL perm(INT(k/10), L+1)
END IF
CASE "S" !万と千の位を確認する
LET k=fnVAL("F") + cy
IF fnVAL("F")>0 AND fnVAL("S")>0 AND k=fnVAL("SE") THEN
LET ANSWER_COUNT=ANSWER_COUNT+1 !解答数
PRINT ANSWER_COUNT
PRINT USING " ONE ####":fnVAL("ONE") !結果の表示
PRINT USING " TWO ####":fnVAL("TWO")
PRINT USING "+ FOUR ####":fnVAL("FOUR")
PRINT "-------"
PRINT USING " SEVEN #####":fnVAL("SEVEN")
PRINT
!STOP !1つだけならコメントをはずす
END IF
CASE ELSE
IF L<LEN(c$) THEN CALL perm((cy), L+1) !次の文字へ ※cyは値渡し
END SELECT
!----- ↑↑↑↑↑ -----
LET f(nm)=0 !未使用
END IF
NEXT nm
END SUB
END MODULE