各月の末日(最終日)を判定するアルゴリズム
JavaScript で各月の最終日を取得する場合に、2つのアルゴリズムを考えたので実行速度を比較してみた。
1. 単純なアルゴリズム
まず、誰でも思いつくであろう単純なアルゴリズム。
function eom_a(y, m){ switch (m) { case 2: return isLeapYear(y) ? 29 : 28; case 4: case 6: case 9: case 11: return 30; } return 31; }
isLeapYear() はうるう年判定の関数。2月のみうるう年の判定を行い、29 or 28 を返す。そのほかの月については 30日となる月のみ switch 文で判定し、マッチしない場合は 31 を返す。たぶん、誰でも思いつく簡単なアルゴリズム。
2. ちょっと変なアルゴリズム
単純なアルゴリズムを採用した場合に、条件判定が何度も行われるケースがあるのがちょっと気になり、以下のようなアルゴリズムを考えてみた。
function eom_b(y, m){ if (m == 2) { return isLeapYear(y) ? 29 : 28; } return (m < 8) ? 30 + (m % 2) : 31 - (m % 2); }
2月の場合の処理は一緒。7月までは奇数月が31日、8月以降は偶数月が31日であることに着目してみた。割算をしてるのが速度に影響しそう。
3. 検証
以下、検証用のスクリプト。上記の2関数を各20万回ずつ実行し、処理時間を計測・表示した。
<!DOCTYPE html> <html lang="ja"> <head> <title>Get end of month</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script> $(function(){ function isLeapYear(y){ return ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) ? true : false; } function eom_a(y, m){ switch (m) { case 2: return isLeapYear(y) ? 29 : 28; case 4: case 6: case 9: case 11: return 30; } return 31; } function eom_b(y, m){ if (m == 2) { return isLeapYear(y) ? 29 : 28; } return (m < 8) ? 30 + (m % 2) : 31 - (m % 2); } var i, st, et, b = $('body'); // eom_a st = (new Date()).getTime(); for (i = 200000; i > 0; --i) { eom_a(i % 2010, i % 12 + 1); } et = (new Date()).getTime(); b.append('<p>'+(et-st)+' ms</p>'); // eom_b st = (new Date()).getTime(); for (i = 200000; i > 0; --i) { eom_b(i % 2010, i % 12 + 1); } et = (new Date()).getTime(); b.append('<p>'+(et-st)+' ms</p>'); }); </script> </head> <body></body> </html>
これを各ブラウザで実行してみたら、意外な結果に。ちなみに OS は Windows 7, CPU は Core2 Duo E7500。
ブラウザ | eom_a | eom_b |
---|---|---|
Internet Explorer 8 | 164 ms | 175 ms |
Firefox 3.6.3 | 203 ms | 4 ms |
Chrome 5 | 7 ms | 11 ms |
Safari 5 | 8 ms | 6 ms |
Opera 10 | 68 ms | 59 ms |
思ったより差がない……と思いきや、Firefox のみ圧倒的に後者のアルゴリズムの方が速い。その他のブラウザでは無視できる程度の差しかなかった。
4. 結論
だからどうした……って話だけど、せっかく調べたので投稿してみた。それにしてもいつの間にか Safari がかなり高速化されていてむしろそっちにびっくりした。