
※ サムネイル画像はWikipediaの「メルセンヌ数」のキャプチャです。
皆さんこんにちは! ボドゲ制作サークル「ゆんプロ」の代表のY.T.です!
豪華版に付属する書籍の内容紹介も8回目です。
そしてこれまでの内容は比較的簡単な内容ばかりでした。今振り返ってみても、少し難しい話だったのは「Pythonの名前マングリング」ぐらいかなと思います。
そちらも無料で公開しているので、見落としている方はチェック!
https://camp-fire.jp/projects/881547/view/activities/756976#main
今回の内容は、簡単そうな皮を被っていた本作が、いきなり牙をむき出したような難題を扱っています。
まず、以下の数列を見てください。
1,3,7,15
この数列の次の値、知っていますか?
実はこれ、有名な「メルセンヌ数」と呼ばれる数列で、一般項では「2のn乗-1」という式で表されます。
数列の続きは…
1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191…
となっています。
ITエンジニアとしてメルセンヌ数については、その恩恵を把握しておくべきですが、特徴としては「2進数で表記すると、すべての桁が1になる値」という点が挙げられます。
要は1を左ビットシフトしていき、1減ずることで得られる数値です。このメルセンヌ数やメルセンヌ数の一般項や仕組みを知っていると、ビット演算を利用したコーディングにおいて有利です。例えば、補数計算などが簡単になります。IPアドレス計算などでは、こういう知識が求められるんですね。
話を戻すと、
「要は」などと表現していますが、「1を左ビットシフトしていき、1減ずることで得られる数値」というのは「2のn乗-1」をエンジニア目線で解説しただけのことです。この表現で意味が通じる場合、アナタもワタシもエンジニアなのです…笑。
さて。
メルセンヌ数の定義を実直にプログラミングすると、

※ JavaScriptにて実装
このように、簡単なコードで実現できます。
ITエンジニア向けカードゲームですから、これはこれで良いのですが、もっと式を変形してみましょう。
一旦、各数列の階差数列を考えます。階差数列というのは、各数列の差を求めた数列のことです。
1,3 … 2
3,7 … 4
7,8 … 8
階差数列を見れば、「2…4…8」と、2の倍数である(2の等比数列である)ことが分かります。
そして階差数列が2の等比数列であるため、要するに

という変形ができます。ビットシフトという、エンジニアの領域の知識を用いずとも、数学の知識でプログラミングができていますね。
で、さらにこれを変形しましょう。
メルセンヌ数は結局、前の数(i-1)と今の数(i)の和から1を減じた結果の総和を求めることと同義です。
したがって初期値を1ではなく0から始めることで、1を減ずる必要がなくなりますし、iとi+1を加算していくこととして変形できます。

これを最終的に、加算代入演算子とインクリメント演算子に置き換えたのが取り札のコードになっています。

ところで、
加算代入演算子と、インクリメント演算子ってなんでしょうか?
「横断プログラミングかるた」の豪華版では、付属するコードの解説とともに、用語解説もあります。
用語集から加算代入演算子とインクリメント演算子の解説を引っ張ってきてみましょう。


本作は「プログラミングの初学者も遊べるが、プログラミング中級者~上級者にも参考になるゲーム」として制作しています。
私はプログラマーの立場としては、「forやwhileでの定型的なインデックス更新などの処理を除き、インクリメント演算子は使用すべきではない」という立場を取っています。このスタンスを理解できない方にも参考となるように、コードを作成しています。
事実、インクリメント演算子およびデクリメント演算子は、言語の処理系によって後置と前置の解釈が変わっています。
JavaScriptではループの終了条件で用いる変数に対して、代入演算子と後置インクリメントを同時に使用すると無限ループに陥る
これは代入演算子の解釈順序や前置インクリメントや後置インクリメントの解釈の順序に違いがある点に起因します。
※ ここは理解しなくてもいいです / 開始
C/C++/PHPなど:前置インクリメントの場合、インクリメントを先に実行し、変数の値を参照します。対して後置インクリメントの場合、式の値を直接取得し評価されたあと、式の最後に遅延してインクリメントが実行されます。ここで重要な点が、代入演算子も計算式の一部であるとして処理する点です。要は、インクリメント演算子の加算や参照のタイミングが保証されています。
JavaScript/C#/Java:代入演算を計算する場合、左辺を先に計算したあと、右辺をあとから計算することが規定されています。前置インクリメントの場合、インクリメントを先に実行し、変数の値を参照します。対して後置インクリメントの場合、式の値を直接取得し評価されたあと、式の最後に遅延してインクリメントが実行されます。要は、インクリメント演算子の加算や参照のタイミングは、左辺と右辺で異なることを意味します。
※ ここは理解しなくてもいいです / 終わり
要は、このようなコードは言語依存となるため、昨今のプログラミングにおいては避けたほうが良いのです。
なぜなら各種言語の処理系における解釈の違いによって、代入演算子とインクリメント演算子やデクリメント演算子の同時使用は言語間で異なる挙動を示すからです。
…ここまでの内容は難しかったでしょうか? なんとなく理解できたという方も、まったく理解できなかったという方もいるでしょう。
正直、ここまで深い内容を、初心者の方含めて、上級者まで理解する必要はありません。しかし重要なのは
インクリメント演算子と代入演算子を同時に使用してはならない
という点を理解していただくことです。
本書では、出力結果を提示することで、この主張を初心者の方でも直感的に理解しやすいように配慮しています。
ということで、実際に本書の内容をごらんください。
先程までの説明よりは、分かりやすいと思います(何度も推敲して、わかりにくい説明を省きまくりました…苦笑)。






正直なことを言うと、この主張を広めるために、メルセンヌ数を出力するコードを加算代入演算子とインクリメント演算子で実装したというのが本音です。
結局、エンジニアを説得するには、実例を示すのが一番手っ取り早いので…笑。
本作や、今回の内容があなたのプログラミングスキルの一部になったり、仲間内の話のネタにでもなると嬉しいです。
それでは、また次の内容でお会いしましょう!
次回の内容をチラ見せ…




