テンパズル(4つの数字で10を作るパズル)
このパズルの全問解答集をExcel VBAを使って作成してみました。
是非下記からダウンロードして、眺めてみてください。↓↓
テンパズル 完成版
ここからはどのようなプログラムを使って、この解答集を作ったのかを紹介します。
手順としては次の通り。
①4つの数字を全パターン用意する。
②式の形を一般化して作る。
③重複している式を削る。
④①で作ったパターンを③の式にあてはめ、10になる式を探す。
⑤同じ式を削除する。
⑥÷0を削除する。
⑦痛恨の手作業。
完成!!
簡単に書くと、こんな感じですが試行錯誤を繰り返して1か月以上考えましたね・・・。
論理的に薄かったり、無駄も多かったり・・・。能書きはこの辺にしておきましょう。
では、詳しく説明していきます。長いので、流し読みしてください。
スポンサーリンク
①4つの数字を全パターン用意する。
まずは、テンパズルの問題となる4つの数字のパターンを全て作ります。
(1)まずはエクセル上のA列に、4桁の数字を1000~9999まで用意します。
これはエクセルの数値コピーを使えば一瞬ですね。
(2)MID関数を使って、CF列に4桁の数字を4つの数字に分解します。
MID関数とは、文字列の指定した位置から指定された文字数を表す関数です。
(3)C~F列を数値コピーして、HK列に貼り付けます。
これは、(4)のLARGE関数でエラーを防ぐためです。
このとき、文字列として、貼り付けられるため、警告マークから全て数値に
変換する必要があります。(ドラッグしてやれば、一発)
(4)LARGE関数を使って、MP列に4つの数字を大きい順に並び替えます。
LARGE関数とは、データの中から指定した番目に大きい数値を表す関数です。
(5)並び替え機能を使って、各行をMP列の大きさにより並び替えます。
すると、例えば1001から出てきた1100と、1100から出てきた1100、1011から
出てきた1100、3つが重複してしまいます。
(6)Excel VBAを使って、4つの数字が重複している行を削除します。
これらを削除するために、次のようなマクロを組みます。
このマクロを何回か繰り返すことで、重複をなくすことができます。
(7)最後にL列に整理番号を付けて、4つの数字全パターンが重複なしでできあがります。
最初は1000~9999の9000の数字だったのが、重複をなくすと、
714通りになってしまいました。
一応、これに0000を付け加えて全715通りの数のパターンの完成です。
スポンサーリンク
②式の形を一般化して作る。
問題となる4つの数字は作り終えたので、今度は解答を出すための式作りに入ります。
(1)理論から考える。
4つの数字を\( a , b , c ,d \)とする。
四則の演算子(\( + , – , \times , \div ~\))の入れ方は、
\(~a\)と\(b\)の間で4通り、\(b\)と\(c\)の間で4通り、\(c\)と\(d\)の間で4通り、つまり\( 4^3=64 \)通り。
さらに、計算の順序を考える。左から順々に計算していくのを次のように表す。
a 1 b 2 c 3 d → 例:\( a + b + c + d \)
すると、今のも含めて全部で5通りの計算方法が得られる。
a 2 b 1 c 3 d → 例:\( (a+(b+c))+d \)
a 3 b 1 c 2 d → 例:\( a+((b+c)+d) \)
a 3 b 2 c 1 d → 例:\( a+(b+(c+d)) \)
a 1 b 3 c 2 d (a 2 b 3 c 1 d も同様) → 例:\( (a+b)+(c+d) \)
さらにさらに、4つの数字を並び替えることも考える必要があるため、4つの数字\( (a,b,c,d) \)の並べ方は
\(~ 4!=4\times3\times2\times1=24\)通り
以上より、1組の数字につき、\(64\times5\times24= \)7680通りの計算方法が考えられる。
(2)エクセル上に7680通りの計算方法を数字の代わりに\( a , b , c , d \)を使って作っていく。
まず、A列に0~7679までの通し番号、B列にN(0)~N(7679)までの配列を連続データの作成により、作る。
そして、C列、E列、I列、M列、Q列にそれぞれ\( = , a , b , c , d \)を入力する。
(3)最初に演算子を全て+で固定、さらに文字の順番も\( a,b,c,d \)で固定をして、カッコの位置を考える。
すると、カッコの位置は全文で5通りだったので、通し番号0~4は次のように式が書ける。
(4)この5行をまとめて選択して、通し番号319までコピーをします。
そして、5行を1ブロックにして、演算子を変えていきます。(ここは地道に手作業・・)
(5)さらに、(4)で作った320行をまとめて選択して、通し番号7679までコピーをします。
そして、320行を1ブロックにして、Excelの置換機能で、文字の順番を入れ替えます。
(ここも地道に置換の設定をします。)
一応これで、全7680通りの計算式ができあがりました。
③重複している式を削る
あとは、①で作った全パターンを②で作った式に代入するだけなのですが、さすがに重すぎて(715題×7680通り=5491200回の計算)コンピュータが処理してくれません。そこで、\(a+b+c+d\)と\((a+b)+(c+d)\)のような、式的に等価なものを削って1つにまとめてしまいます。
(1)まず、②で作った式は、このままでは使い物にならないため、VBAのコピーします。
②でできるだけ簡単に式を作るために、セルを細かく分割してしまったので、単純にコピーはできません。
そこで、通し番号0~7679のBS列まで全てコピーをして、Wordに「テキストのみ保持」で貼り付けます。(それぞれの式を1行で表したいので、ページレイアウトでA3横に設定しておきましょう。)
そして、今ワードに貼り付けた式をまたコピーをし、Excel VBA内に貼り付けます。
すると、きれいにセルの境目が削除され、計算式としてふさわしい記述ができました。
(2)今貼り付けた式を960個ずつに改行を使ってスペース的に分けます。(全部で8つに分かれる)
これは、1つのマクロ内で7680の式計算をこなそうとすると、オーバーフローして、処理が中断してしまうからです。(もしかしたら、最新のPCならできるのかもしれませんが・・・)
(3)8つに分けたそれぞれの前後に次のように記述する。
<以下VBAでの記述>
Sub テンパズル解答1() ’最初のブロックなので、「1」
Dim a, b, c, d, e As Integer ’変数a,b,c,d,eは整数型であるという宣言
a = Range(“C2").Value 'C2のセルの値をaに代入する。
b = Range(“C3").Value 'C3のセルの値をbに代入する。
c = Range(“C4").Value 'C4のセルの値をcに代入する。
d = Range(“C5").Value 'C5のセルの値をd代入する。
On Error Resume Next '÷0がでてきたら無視をする
Dim N(7679) As Single 'N(0)からN(7680)までの配列を宣言
————————————————-
N(0) = a + b + c + d '上の記述により、adが式に代入されて計算される。
N(1) = (a + (b + c)) + d
N(2) = a + ((b + c) + d)
N(3) = a + (b + (c + d))
N(4) = (a + b) + (c + d)
・・・・・・
N(959)= (a / c) / (b / d)
————————————————-
e = Range(“A1").Value 'A1のセルの値をeに代入する。
Dim x As Integer '変数xは整数型であるという宣言
For x = 0 To 959 ’次の処理を960回繰り返しなさい。(x=0のとき↓)
Cells(9+x , e).Value = N(x) '9行目e列に入る値はN(0)の値
Next x
End Sub
Sub テンパズル解答2() ’以下同様に
Dim a, b, c, d As Integer
a = Range(“C2").Value
b = Range(“C3").Value
c = Range(“C4").Value
d = Range(“C5").Value
Dim N(7679) As Single
On Error Resume Next
<以上VBAでの記述>
(4)さらに、次のようなマクロも記述する。
<以下VBAでの記述>
Sub テンパズル解答()
テンパズル解答1 '(3)で分割した1~8ブロックのプログラムを
テンパズル解答2 連続して行う。
テンパズル解答3
テンパズル解答4
テンパズル解答5
テンパズル解答6
テンパズル解答7
テンパズル解答8
End Sub
<以上VBAでの記述>
(5)下のようなA列、B列を記述したエクセルシートに、C2=1、C3=2、C4=3、C5=4をセルに入力して、
マクロ「テンパズル解答」を実行すると、次のような計算結果が表れる。
例えば、C9からC13の10は、a=1 , b=2 , c=3 , d=4 を代入したときの、
N(0) = a + b + c + d
N(1) = (a + (b + c)) + d
N(2) = a + ((b + c) + d)
N(3) = a + (b + (c + d))
N(4) = (a + b) + (c + d)
の結果が表れている。
(6)ここで、重複している式を減らすための工夫を考える。
上に挙げたN(0)~N(4)の式のように明らかに等価なものから、
N(20) = a + b – c + d
N(29) = (a + b) – (c – d)
のように式変形をすると、等価になるものもある。
そこで、コンピュータに理論的に等価な式を見つけさせるのは難しいため、
いくつかの4つの数字の組を考え、答えが全て等しくなる式を重複しているものとみなし、
削除するという方法をとる。(100%排除できるわけではないが、ある程度は絞れる)
そこで、このシートのA1列の値を1増やして、さらにC2~C5の数字も違う数字にして、
マクロ「テンパズル解答」を実行した。
いくつかの4つの数字の組として用いたのは、全て同じ数や偶数だけ、奇数だけ、0が含まれる、無作為に抽出した数字など、次のような組である。↓
(1,2,3,4)、(4,4,4,4),(7,7,7,7),(2,4,6,8),(1,3,5,9),(3,3,4,7),(2,5,6,9),(6,6,7,9),(7,7,7,4),(4,4,4,5),(0,2,5,9),(1,4,6,8),(0,1,2,7)
その結果、次のようになった。
(7)これらの中から計算結果が全て等しいものを探るため、各式の13個の計算結果の合計(sum関数)、最大値(max関数)、最小値(min関数)、中央値(median関数)、さらにそれら4つの値の合計をとり、確実に同じ式かどうかを見やすくした。
(8)U列の値をもとに各行を並び替えて、合計、最大値、最小値、中央値が等しい式を削除した。
(ここでは、重複した式のある行を削除するマクロは①(6)と同じような仕組みです。)
(9)これで重複している式をある程度削除できたため、もとの式番号通りに並び替えて、新たな番号をつけ、計算結果等の不要な列を削除することで、新たな式リストができた。
なんと、これで7680もあった式が、N(1102)までの1103個の式まで減らすことができた。
これならば、単純計算7倍のスピードで計算ができるだろう。
④①で作ったパターンを③の式にあてはめ、10になる式を探す。
③で作られた1103個の式に、①で作った715組の4つの数字を代入し、それぞれ10となる式を探します。
(1)A4,A104、A204と100間隔で1~715の番号を入力します。
さらに、B4~E4のセルにはvlookup関数を使って、
VLOOKUP($A4,問題!$L$4:$P$718,2) (最後の2をセルによって変更する。)
と入力します。これにより、問題シートから、問題番号が1番なら、1番の問題(0、0、0、0)をB4~E4に4つの数字を並べることができます。
これまた、問題番号715(A71404)の行まで作ります。
(2)次のようなマクロ「解答つくり」を記述します。
<以下VBAでの記述>
Sub 解答つくり()
Dim 問題番号 As Long
For 問題番号 = 1 To 715
Dim a, b, c, d, e As Long
a = Cells(問題番号 * 100 – 96, 2).Value
b = Cells(問題番号 * 100 – 96, 3).Value
c = Cells(問題番号 * 100 – 96, 4).Value
d = Cells(問題番号 * 100 – 96, 5).Value
Cells(問題番号 * 100 – 96, 6).Select
On Error Resume Next
Dim N(1102) As Single
N(0) = a + b + c + d
N(1) = a + b + c – d
N(2) = a + b + c * d
N(3) = (a + b + c) * d
N(4) = a + ((b + c) * d)
N(5) = a + b + c / d
・・・・・・・・
N(1101) = d / ((c / a) – b)
N(1102) = (d / c) / (a – b)
For e = 0 To 1102
If N(e) = 10 Then
ActiveCell.Value = e
ActiveCell.Offset(1, 0).Select
End If
Next e
Next 問題番号
End Sub
<以上 VBAでの記述>
これにより、F列に計算結果が10となる式のナンバーが全て挙がります。
そして、空白の行を削除し、式ナンバーがある行にも問題番号と問題を入力しておくと、次のようになります。
(3)F列の式ナンバーによる式がどのような式であったかを可視化したいので、G~V列に式ナンバーに対応する式を出力します。③(9)で作ったシートをvlookup関数で参照しています。一応、使った関数はコレ↓
IF($F4="","",VLOOKUP($F4,式参照!$C$8:$U$1110,4))
(式ナンバーが空欄なら、空欄のままにしなさい。そうでなければ、式ナンバーに対して対応する式を「式参照」のシートから読み込みなさい。)
(4)さすがに文字式のままだと見づらいので、XAM列に文字式を対応する数字に置き換えた式を作ります。
Y列、AC列、AG列、AK列は、次のように記述し、文字を数字にします。(例Y17)
=IF(F17="","",IF(H17="a",$B17,IF(H17="b",$C17,IF(H17="c",$D17,$E17))))
(式ナンバーが空欄であれば、空欄にしなさい。そうでなければ、H列がaのときはB列のaの値を、H列がbのときはC列のbの値を、H列がcのときはD列のcの値を、H列がdのときはE列のdの値を代入する)
それ以外のところはカッコや演算子をそのまま写せばよいので、(例X17)
=G17
で結構です。すると、次のようになります。
これで一応、各4つの数字の組で10になる式が出てきました。全部で約6000もの式が並びました。
⑤同じ式を削除する。
一応、④で答えは出そろいました。が、ちょっと無駄が多い気がします。下をご覧ください。(Excel2016になりました!)
なんと、同じ式が4つもあります!!
G~V列の式を見てみましょう。すると、上の4つの式の正体は以下のようになっています。
・式ナンバー4:a + ( ( b + c ) * d )
・式ナンバー151:a + ( ( b + d ) * c )
・式ナンバー434:b + ( ( a + c ) * d )
・式ナンバー733:c + ( ( a + b ) * d )
これらの式は明らかに等価ではありませんが、(a,b,c,d)=(2,2,2,2)のときは同じ意味になってしまうため、こんなにも無駄が出てしまうのです・・・。
ということで、⑤では、この現象を解消します。
(1)並び替えをして、同じような式が並ぶようにします。そのため、XAM列は関数での記述になっているので、全て値としてコピーをします。(AO列~BD列にコピーしました。)
そこで、「並び替え」機能を使用して、以下のように並び替えてもらいます。(A列の番号はそのままで、4つの数字の順番が同じものをくっつけるという戦法です)
これでOKを押すと、こうなります↓
あ、式ナンバー521も同じような式でしたね(汗)
とりあえず、これで並べ替えが完了しました。
(2)次にまたまたif関数を使って、同じ式を抽出します。
以下の用にBF列に関数を入力します。なが~く書いていますが、要は「式ナンバー欄が空欄でない式のうち、上の式と比較して、同じなら"0″を、一部分でも違っていれば"1″を記入する」という指示です。
あとは、①(6)で使った「重複式削除」のマクロを応用して、0となった行を削除します。
これで、全く同じ式は削除されました。
⑥÷0の削除
一見、完成したかのように思われましたが、おかしい行を発見!
4・0・0・0で10をつくる・・・。できませんよね。
それなのに、10ができている!!(10ができる場合、式ナンバーがF列にある)
式を見てみると、
式ナンバー87:4*0+0/0(つまり、\( 4\times0+0\div0~\))
0でわるという数学上の禁じ手を行っています。ここにきて、今までのマクロで書いてきた
On Error Resume Next '÷0がでてきたら無視をする
の影響に気づきました。
そこで、まずは÷0を見つけてその式を削除するということをしていきます。
(1)⑤と違って、今度は一行一行見ていきます。
BH列、BJ列、BL列を使って、AT列、AX列、BB列の数字が0の場合、÷0をしていないかを確認する関数をif関数で作ります。
ここで注意しなければならないのは、/(0+3)のようなパターンです。この場合であれば、式として問題でありません。よって、次のような関数をBH列に入力します。BH列では、4つの数字のうち、2番目の数字において、÷0をしていたら”0”を入力するようにしています。。(BJ列、BL列も同様に、3番目、4番目の数字を判定)
(2)3つ並んだので、それらの数字を合計するSUM関数をBN列に作り、BP列には、IF関数で、BN列の値が3なら"1″、3以外なら"0″を入力するような関数とします。すると、上のような結果となります。
つまり、一つでも÷0があると、BN列は0になってしまいます。
(3)ここまで来れば、①(6)や⑤(2)で使用した「重複式削除」のマクロを応用して、0となった行を削除します。これで、÷0がなくなりました!!
⑦痛恨の手作業
ここまでエクセル関数とVBAに頼って、機械的にテンパズル解答集を作ってきました。最後の確認をしていたところ、思わぬところに伏兵がいました。それは次の行↓
\( 5+(0\div(6-6)) \)
\(~ (5+0)\div(6-6) \)
\(~ (5\div(6-6))+0 \)
・・・・そのパターンがあったか!! というこれまでの検査をかいくぐってきたエラー式の称賛と驚きの意を表すると共に、どうすればよいのか・・・と思い悩みました。
(1)そこで、とった方法は・・・手作業。
÷(○-△)の形で÷が入りうる列はAQ列とAV列(BA列にあったとしても、÷0はすでに⑥で削除しているため)。
そのため、その2列の"/"記号を"/“に置換して、見やすくすると、なんと該当した"/“記号は約1500個!
これならいける!!ということで、赤く染まった"/“の式を見ていきました。
そして、手作業による検閲をくぐり抜けた式たちがこちら↓
このファイルが テンパズル 完成版 からダウンロードできます!!どうぞご自由にお使いください。
⑧反省点
なんとなく気に入らないことが2点あります。
・⑦でやったことをプログラム化できなかったこと。複雑すぎて、能力が足りませんでした。
・「\(2\times 5+0+0 ~\)と\( 2\times 5-0-0 \)など式の本質は同じものを削除できなかったこと。
以上の点を解決できればもっと正確なものを、無駄なく作れると思います。
今後の課題ということにしておきましょう。
ディスカッション
コメント一覧
まだ、コメントがありません