ジャマイカ

http://q.hatena.ne.jp/1256711755


ここ数日ずっと考えてたVBAのマクロがやっとこ形になりました。
回答欄に長々とそのコードの説明とその考え方を書くのも無粋なのでこちらにまとめます。


最初そんなに難しいマクロだとはぜんぜん思いませんでした。
数時間で作成可能かなとか軽い気持ちで挑戦したら、先に行くほど雲を掴むような処理が必要だとわかりました。


まず考えたのは、まったく重複の無い数式だけを出せるアルゴリズムはあるのかどうかということ。
これは経験的に無いだろうと直に見切りをつけました。5つの数字が2つ同じだけで重複ができそうですし。
そこで考えられる方法の手順は力技ですが。

  1. 総当り的に全てのパターンを作る
  2. 重複を取り除く


1 総当り的に全てのパターンを作る

  1. 5つの数字を左から順番に決める
  2. 4つの演算子を決める
  3. 4つの演算順序を決める

曲者の( )とは演算順序そのものなので、これで作ってみたのですが、恐ろしく重く処理が終わらない。
演算順序と数字の順番に被る部分があるので無駄がある為でした。そこで

  1. 5つの数字で配列を作りそこから2つ取り出して計算
  2. その計算した結果で4つの配列を作る
  3. その4つから2つ取り出して計算
  4. それを繰り返し1つにする

この方法を再帰を使って作ってみたところ、劇的に速度アップしそれでいて全てのパターンを作ることが可能でした。


2 重複を取り除く
これは総当りを作ることよりも数倍難しかったです。
まず数式が同じであることを調べる為に、数式を数値で表してみたのですがオーバーフローしました。
そこで比較できるように文字列として変形する方法をとることにします。
数式を前から順番に文字を取り出して行く方法は直に壁に突き当たりました。
( )がどうしても邪魔になるのです。
そこで総当りを作る演算のときに( )が必要か判断して必要なときだけ( )をつける。
その為には前の演算子を格納する配列を作れば可能。
そして、演算と同時に+や×の場合は入れ替え可能なのだから文字列でソートして入れ替える。
それで行けそうだと思いましたが、実際に作ると同じはずの数式でも全てが同じになりませんでした。


そこで四則演算とはどういうことか、中学生の頭に戻って考えてみます。
引き算とはマイナスを足すことであり、割り算とは分数を掛けること。
これはつまり足し算と掛け算だけに変形できるということを意味します。
それで次のように変換してみます。

a → ++a
-b → +-b
*c → **c
/d → */d

ここで引き算も割り算も交換可能ということになります。
変形された式は比較さえできればいいので、実際に正しい式である必要はありません。
そこでそのルールで数式全体を変換すると、別の書き方で順序が違うだけの数式が同じ文字列に変換できることを発見します。
なお実際の変換とソートには再帰を使って深い階層から変換する工夫が必要でした。


たぶんこの方法で限りなく全ての回答を重複無く取り出すことが可能だと思います。
ただ残念なことは他に正答を出すプログラムを知らないので検証する方法が無いことですが。

                                                                                                                                                            • -


この考え方に行き着くまでにはいろいろなアイデアを没にしました。
例えば、演算子の数を増やす方法。
+よりも演算順序の早い++とかをたくさん定義する。
そうすれば( )は必要なくなるのが狙いでしたが逆に複雑になって断念。


このマクロを作っていてつくづく思ったのは私たちが慣れ親しんだ四則演算の数式というのは実に使いかっての悪い物であるということ。
同じ数式を意味する書き方が複数存在し、方向性のある演算記号は算数を複雑にして数学嫌いの子供を作ることに繋がってるんじゃないのかな。


ここ数日は仕事中もこのことを考えたりして、良い意味で熱中していました。
たぶん私たちが知らないだけで先人はもっといい方法を知っているのだろうけど、
近道を調べるのではなくて自分でコツコツ考えることができたのは貴重な体験でした。
前の質問に続き楽しませていただいた質問者様に感謝します。