kei0425tan’s blog

技術的なことを主に

paiza B032:デジタル計算機

ヒントにならないように感想のみで。


まずは基本情報


paiza B032:デジタル計算機
受験者数: 291人 正解率: 70.48% 平均回答時間: 53分25秒 平均スコア: 58.45点

いいスコアをとりたかったので、受験者数が少なめ。
面倒な問題はやなので、平均回答時間短め。
平均スコアが低めなものは罠がある場合が多いけど、慎重にやれば大丈夫かな??

で、特定言語マスターを埋めようと思い、現在はPython2のみなので、それ以外の言語縛りでーと思い、最近勉強中のScalaで解こうと思い問題をみましたが、ちょっと難しそうだぞということで、JavaScriptで解きました。

で、結果。
paiza.jp

受験結果 受験言語: JavaScript 回答時間: 38分36秒 バイト数: 1581 Byte スコア: 50点

半分間違えてしまいました。
回答時間はかなり早めだったんですが。。。。

何を間違えたのかなーって思ったのですが、全然無視してた条件がありました。

そう思って、いろいろ調べた結果、どうやらJavaScriptを選択した時点でこの結果は決まっていたようです。
Pythonにしておけば、問題なかったのになぁ・・・・

やはり、例のみではなく、自分でエッジのテストはしないと100点はとれないですね。

ちなみに、Rubyでも同様の制限があるようです。

JavaScriptでもbigintを利用すればまだなんとかなるようです。
※ そもそも「デジタル計算機」であって「○○リーダー、○○ライター」ではないってことに気が付かなきゃいけなかったんですね。

JavaScriptで末尾再帰で素数

今度は、JavaScriptで末尾再帰を利用して素数を取得してみました。

paiza.ioでは処理時間が2秒が上限のため、700000まで取得できました。
スタックオーバーフローはおきませんでした。

末尾再帰とは

関数の末尾が自分自身の呼び出しのみになっている再帰のこと。
ここでいう末尾とは、関数の最後の行ということではなく、処理の末尾のこと。(普通はreturn文)


意味のないコードですが、以下のようなものが末尾再帰になります。

function f(x) {
   if (x == 0) {
      return 1;
   }
   return f(x - 1);
}

以下は階乗を取得する再帰関数ですが、最後が自分自身の呼び出しのみになっていないため、末尾再帰ではありません。

function fact(x) {
   if (x == 1) {
      return 1;
   }
   return x * fact(x - 1);
}

なんで、わざわざ再帰でも末尾再帰と特別な名前がついているのかといいますと、簡単に末尾再帰最適化ができるからです。
これは、コンパイラが自動的に対応する場合もありますし、手でも簡単にできます。

JavaScriptには末尾再帰最適化の機能がES6で導入されたため、まだ実装されている処理系はあまりありません。
paiza.ioのNode.jsも未対応です。

なので、手で末尾再帰最適化を行ってみましょう。

問題のコードはこちら

function prime(n) {
    var limit = Math.sqrt(n);
    
    function primemain(primelist, remain) {
        if (remain.length === 0) {
            return primelist;
        }
        else if (limit < remain[0]) {
            return primelist.concat(remain);
        }
        else {
            primelist.push(remain[0]);
            return primemain(primelist, remain.filter(function (x) {return x % remain[0] !== 0}));
        }
    }
    
    var remain = [];
    for (var i = 2; i < n + 1; i++) {
        remain.push(i);
    }

    return primemain([], remain);
}

末尾再帰最適化したコードはこちら。

function prime(n) {
    var limit = Math.sqrt(n);
    var primelist = [];
    var remain = [];
    for (var i = 2; i < n + 1; i++) {
        remain.push(i);
    }
    
    while (true) {
        if (remain.length === 0) {
            return primelist;
        }
        else if (limit < remain[0]) {
            return primelist.concat(remain);
        }
        else {
            primelist.push(remain[0]);
            remain = remain.filter(function (x) {return x % remain[0] !== 0});
        }
    }
}

以下の手順で変更します。

  1. 関数定義前に、パラメータだった変数を初期化
  2. 関数定義を無限ループに変更。
  3. 再帰呼び出し箇所で、パラメータを修正するように変更。

実際に動作するものは以下になります。

せっかく直したのにほとんど速度は変わりませんでした。

※ paiza.ioで、「don't make functions within a loop」のワーニングがでていますが、このケースではクロージャでないため、問題ありません。(クロージャで遅延評価をする場合は危険)
※ 通常の再帰を末尾再帰に変換するほうが大変です。

paizaの結果

最近ちょっとはまってたpaizaの受験結果を適当に紹介。

paizaとは

結果

とりあえず、ランクDから解いてみました。

D040:連休の天気 Python2
paiza.jp


その後、勧められたのがこちら

C010:安息の地を求めて JavaScript
paiza.jp

まだまだ簡単

調子にのって、次に勧められた問題がこちら

B012:チェックディジット JavaScript
paiza.jp
結構面倒で時間が掛かってしまいました。。。。。

なので、まとまった時間がないとできないなーと思いつつ、しばらく時間を空けてからやったのがこちら

A003:盤面ゲーム JavaScript
paiza.jp
超有名なゲームの判定でした。

とここまでは、一発合格でランクAまでクリアしたのですが、ここからがちょっと大変でした。

S010:ひとりすごろく Python2
paiza.jp
問題読み間違えて、なかなか正しくコーディングできなかったのと、静的テーブルを用意したのですがそのテーブルの初期値をタイプミスしたりなどでさんざんな出来でした。

気を取り直して連続で挑戦

S002:最短距離を測る Python2
paiza.jp
アルゴリズムをある程度勉強するとでてくる課題。解法はいろいろ知っているのですが、コーディング速度が一番速い系でやってみましたが。。。。。
大規模データでタイムアウトが発生して、痛恨の90点。ジャッジが1回のみなのは結構きついです。

ムキになって再挑戦

S009:辞書順最小 Python2
paiza.jp
問題の読み間違えで時間が結構かかってしまいましたが、終わってみればほんの30行ほどでできました。
念願のランクSにやっとなれました。

ランクSまで解いた感想

paizaにおけるランキングは以下の通り。

  • ランクS 上位2% 上級100点

非常に高いスキルを持っています。
ほぼすべての企業で書類選考なしにカジュアル面談、面接へ進めます。

  • ランクA 上位8% 上級70点

高いスキルを持っています。
非常に多くの企業で書類選考なしにカジュアル面談、面接へ進めます。

  • ランクB 上位30% 中級100点/上級50点

一定基準以上のスキルを持っています。
多くの企業で書類選考なしにカジュアル面談、面接へ進めます。

  • ランクC 上位60% 初級100点/中級60点/上級30点

基本的なスキルは十分、効率的なコードを意識しましょう。
一部の企業で書類選考なしにカジュアル面談、面接へ進めます。

  • ランクD 初級50点/中級30点/上級10点

要努力!基礎的な部分をもう一度見直しましょう。
ポテンシャル求人案件への応募が可能です。

ランクSは50人に一人の逸材のようです。

とはいうものの、母集団によるかと思います。
職業プログラマであれば、ランクSとれる人はもっと多いかと。。。。
当然のことながら、職業プログラマでもランクCどまりの人もたくさんいると思いますが。

感覚的には、ランクS:ランクA:ランクB:ランクC = 1:2:3:2 くらいなのかなあ??

herokuでnodejsでチャット

herokuでリアルタイムチャット作ってみたいなということで、こちらを参考にして作ってみました。(参考というかコアな部分は丸コピになります。)
Node.js + Socket.IO + jQuery で最小構成チャット - Qiita

herokuでアプリの設定

橋本商会 » Node.jsに入門して画像チャットを作ってHerokuで動かした
まあ、最初から最後までこっちでもいいんだけど、念のため。

ファイルの用意

ファイルは以下

  • index.html
  • server.js
  • package.json
  • Procfile

index.html

<!doctype html>
<meta charset="utf-8">
<title>chat</title>
<script src="/socket.io/socket.io.js"></script>
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<form><input></form><div></div>
<script>
 var socket = io();
 $('form').submit(function () {
   socket.emit('msg', $('input').val());
   $('input').val('');
   return false;
 });
 socket.on('msg', function (data) {
   data = $('<div/>').text(data).html();
   $('div').prepend(data + '<br>');
 });
</script>

server.js

var html = require('fs').readFileSync('index.html');
var http = require('http').createServer(
    function (req, res) {
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.end(html);
});
var io = require('socket.io')(http);
var webPort = process.env.PORT || 3000;
http.listen(webPort);
io.on(
    'connection',
    function (socket) {
        socket.on(
            'msg',
            function (data) {
                io.emit('msg', data);
            }
        );
    }
);

herokuの場合はポートがprocess.env.PORTになります。

package.json

{
  "name": "chat04",
  "version": "0.0.1",
  "description": "test",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "socket.io": "^1.3.6"
  }
}

npm initで質問に答えるだけで自動生成できる。

Procfil

web: node server.js


以上の組み合わせで無事にチャットが動きましたー

IE8のArray.prototype.sort()の第2引数

普段、chromeで作成しています。

しかし、IE8での動作保証をしなければいけなかったりするので、リリース前にIE8で動作確認するのですが、先日原因不明のエラーが発生しました。

JScript オブジェクトを指定してください。

調査したところ、とあるライブラリで配列のソート時に、第2引数を指定しており、そこで上記エラーが発生していました。

 

参考

Array.prototype.sort() - JavaScript | MDN

 

追試験

[1,2,3].sort(function(a,b){return a-b;},1);

 

chromeでもIE9でもエラーにはなりません。

IE8でのみ、上記エラーが発生します。

 

いったい、この第二引数は何を意味しているのでしょう?