レベル3:演算子の制限がある中で計算
レベル3で増えた禁止文字は、「!」「^」「~」「<」「>」「=」「Math」です。比較演算子を中心に、さまざまな計算方法が禁止されています。そのため、これまでとは違った発想で計算を行っていく必要があります。このレベルから、コードは非常に千差万別になります。それでは、実際の解き方を見ていきましょう。
関数やオブジェクトを利用
最初の方法は、JavaScriptのビルトイン関数やオブジェクトを利用して解く方法です。
Boolean
まず、使用するのは「Boolean」です。数値を真偽値にできます。「0」の場合は「false」、「0以外」の場合は「true」になります。こうして得た真偽値は、数値演算で「0」「1」に変換できます。そこから先は、これまでの処理の延長になります。それでは、実際のコードを見ていきましょう。
3-Boolean(i%3)-Boolean(i%5)*2
レベル2で出てきた「!(i%3)+!(i%5)*2」と違い、「3-」と3から引いています。これは、「!(i%3)+!(i%5)*2」のコードでは「!」と真偽の値を反転していましたが、「Boolean」は反転ではないためです。そのため、少しだけ違う計算になっています。
parseInt
次は「parseInt」です。この関数は、小数点数を整数に変換することができます。ここで利用する操作は、以下のような操作です。
-
「parseInt(1-i%3/3)」
-
「
i%3」が割り切れ、0になる場合- 「1-0/3」→「1-0」→「1」→整数化→「1」
-
「
i%3」が割り切れず、1になる場合- 「1-1/3」→「1-0.333」→「0.667」→整数化→「0」
-
「
「1」未満なら、整数化する際に切り捨てられて、「0」になるという仕様を利用します。それでは、実際のコードを見てみましょう。
parseInt(1-i%3/3)+parseInt(1-i%5/5)*2
isNaN
次は「isNaN」です。この関数は、「NaN」(Not a Number、非数)の場合に「true」を、そうでない場合に「false」を戻します。
この関数をどう使うかというと、「0」に関する演算で利用します。JavaScriptでは、例えば「0」に関する除算をした場合には、以下のような値が得られます。以下の「1」は、0以外の他の数値に置き換えてもよいです。
- 「1/0」→「Infinity」
- 「0/1」→「0」
- 「0/0」→「NaN」
この仕様を利用すると、「isNaN」で真偽値を得ることができます。「0/0」になるか否かで分岐するわけです。それではコードを見ていきましょう。
isNaN(0/(i%3))+isNaN(0/(i%5))*2
isNaN(i%3/0)+isNaN(i%5/0)*2
上記の2つの計算は、「0/0」の得方に違いがあります。下のコードの方が若干短くなっています。
isFinite
さて、「0」に関する除算では、「Infinity」が発生するか否かを利用する方法もあります。ここで利用するのは「isFinite」という関数です。この関数は、数値が有限値なら「true」、それ以外なら「false」を戻します。それではコードを見ていきましょう。
3-isFinite(1/(i%3))-isFinite(1/(i%5))*2
「isNaN」の方が、若干短く書けます。それでは、次からは発想を変えて、これまでとはまったく違う方法で解いていきます。
配列を使う
「何も計算しなくてよいよね。答えを羅列しておけば」という発想が、配列を使う方法です。この方法には2つの解があります。1つ目は、15周期で値が変化することを利用して、要素数15の配列を用意する方法です。2つ目は、3の倍数と5の倍数用に、3周期、5周期の配列を用意する方法です。
これらのコードは、説明するよりもコードを見る方が分かりやすいので、以下のコードを見てください。
[0,0,1,0,2,1,0,0,1,2,0,1,0,0,3][(i-1)%15]
[3,0,0,1,0,2,1,0,0,1,2,0,1,0,0][i%15]
[1,0,0][i%3]+[2,0,0,0,0][i%5]
文字列を使う
「配列を使う」の発展形です。取得する値がいずれも1桁であることから、文字列から特定の1文字を取得して、その値を数値として利用すればよいという発想です。JavaScriptでは、文字列を数値演算した場合、そのまま数値として計算されます(ただし、数値として利用可能な文字列でなければならない)。この仕様を利用して計算を行います。
それではコードを掲載します。
"300102100120100".charAt(i%15)
「charAt」を使うことで、文字列の中から特定の1文字を取り出すことができます。
さて、上記のコードはもっと短く書けます。JavaScriptでは「"abcd"[0]」のように、文字列に添え字を与えると、その位置の1文字を取得できます。それでは、この仕様を利用したコードを書いてみます。
"300102100120100"[i%15]
すいぶんすっきりとしました。次は文字列繋がりで、正規表現で判定する方法を紹介します。
正規表現を使う
割り切れる場合、剰余は「0」になります。そのことを利用して、計算結果で「0」が得られれば真という正規表現の式を作り「test」を利用して判定します。これは、実際のコードを見ると一目瞭然なので、見てみましょう。
/0/.test(i%3)+/0/.test(i%5)*2
この先は、計算を使って解いていきます。シンプルですが、奥深い計算になっていきます。
剰余を使う
剰余を連続して取ることで、「0」と「1」の値を得ます。「m%n%(n-1)...%2」の場合、「m」が「nの倍数+1」の時のみ「1」です。この性質を使い、「(i+1)」を使って、「1」と「0」に仕分けます。これは、図を見てもらった方が分かりやすいので、図を示します。
| 元の値 | %5 | %4 | %3 | %2 | |
| 0 | → |
0 |
0 |
0 |
0 |
| 1 | → | 1 | 1 | 1 | 1 |
| 2 | → | 2 | 2 | 2 |
0 |
| 3 | → | 3 | 3 |
0 |
0 |
| 4 | → | 4 |
0 |
0 |
0 |
それでは実際のコードを示しましょう。なかなか美しいコードになっています。
(i+1)%3%2+(i+1)%5%4%3%2*2
掛け算を使う
剰余の次は掛け算です。ここでは「フェルマーの小定理」を利用します。
「p」を素数とし、「a」を「p」の倍数でない整数とする時に以下が成り立つ。
ap-1≡1 (mod p)
→「a」が「p」の倍数でないなら「1」になる。
→「a」が「p」の倍数なら「0」になる。
コードを示します。こちらも、なかなか美しいコードになっています。
3-i*i%3-i*i*i*i%5*2
それでは、さらに難しいレベル4に入っていきましょう。
