コイン投げの正規分布

「完全独習 統計学入門」小島寛之 著をお勉強中で、下記のような命題を実際に検証してみた。

コインをN枚同時に投げて(あるいはN回続けて投げて),そのうち何枚が表になったかをデータとして記録する。この作業を膨大に実行して、表の枚数Xの出た相対度数のヒストグラムを作ると、それは近似的に、平均値がN/2、S.D.が√N/2になる

perlスクリプトをかいてそれをグラフにしてみました。スクリプトで出力したのは同時に100枚投げて表になった枚数をkeyにして10万回繰り返した分布をvalueにいれたハッシュです。結果はエクセルで分析しました。

use strict;
use warnings;
use GD::Graph::bars;
use Data::Dumper;

my $N = 100;
my %distribution;
for ( 1 .. 100000 ) {
    my $num_of_omote = throw($N);
    $distribution{$num_of_omote}++;
}

for ( sort keys %distribution ) {
    printf "%s\t%s\n", $_, $distribution{$_};
}
graph( \%distribution );

sub graph {
    my $dist  = shift;
    my @label = sort { $a <=> $b } keys %$dist;
    my @data  = map { $dist->{$_}, } @label;
    my $graph = GD::Graph::bars->new( 600, 450 );

    my $img = $graph->plot( [ \@label, \@data ] );
    open my $fh, ">", "./graph.gif" or die;
    print $fh $img->gif;
    close $fh;
}

sub throw {
    my $n     = shift;
    my $total = 0;
    for ( 1 .. $n ) {
        $total++ if is_omote();
    }
    return $total;
}

sub is_omote {
    my $rand = int( rand(10) );
    ## 1はオモテ 0はウラと考える                                                       
    return ( $rand % 2 );
}


結果のグラフはきれいに正規分布の形になりました。(グラフの数値が見えなくなってるけどまぁいいや)

下記はごちゃごちゃしてますがCSVデータです。perlで出力したハッシュのkeyが「階級値」でvalueが「度数」となります。

  • 「相対度数」は「度数/100000」で導出
  • 「階級値X相対度数」の和は平均となります。これは50.0088となり命題を満たします。
  • 「偏差」は「階級値-平均」で導出
  • 「偏差の2乗X相対度数」の和が「分散」です。
  • 標準偏差(S.D.)」は分散の√を取ることで導出。これも約5となり命題を満たします。

階級値,度数,相対度数,階級値X相対度数,偏差,偏差の2乗,偏差の2乗X相対度数
28,1,0.00001,0.00028,-22.0088,484.3872774,0.004843873
30,3,0.00003,0.0009,-20.0088,400.3520774,0.012010562
31,7,0.00007,0.00217,-19.0088,361.3344774,0.025293413
32,11,0.00011,0.00352,-18.0088,324.3168774,0.035674857
33,18,0.00018,0.00594,-17.0088,289.2992774,0.05207387
34,35,0.00035,0.0119,-16.0088,256.2816774,0.089698587
35,82,0.00082,0.0287,-15.0088,225.2640774,0.184716544
36,163,0.00163,0.05868,-14.0088,196.2464774,0.319881758
37,281,0.00281,0.10397,-13.0088,169.2288774,0.475533146
38,448,0.00448,0.17024,-12.0088,144.2112774,0.646066523
39,720,0.0072,0.2808,-11.0088,121.1936774,0.872594478
40,1072,0.01072,0.4288,-10.0088,100.1760774,1.07388755
41,1524,0.01524,0.62484,-9.0088,81.15847744,1.236855196
42,2267,0.02267,0.95214,-8.0088,64.14087744,1.454073692
43,3063,0.03063,1.31709,-7.0088,49.12327744,1.504645988
44,3799,0.03799,1.67156,-6.0088,36.10567744,1.371654686
45,4890,0.0489,2.2005,-5.0088,25.08807744,1.226806987
46,5805,0.05805,2.6703,-4.0088,16.07047744,0.932891215
47,6649,0.06649,3.12503,-3.0088,9.05287744,0.601925821
48,7305,0.07305,3.5064,-2.0088,4.03527744,0.294777017
49,7791,0.07791,3.81759,-1.0088,1.01767744,0.079287249
50,8104,0.08104,4.052,-0.0088,7.744E-05,6.27574E-06
51,7743,0.07743,3.94893,0.9912,0.98247744,0.076073228
52,7408,0.07408,3.85216,1.9912,3.96487744,0.293718121
53,6610,0.0661,3.5033,2.9912,8.94727744,0.591415039
54,5794,0.05794,3.12876,3.9912,15.92967744,0.922965511
55,4767,0.04767,2.62185,4.9912,24.91207744,1.187558732
56,3788,0.03788,2.12128,5.9912,35.89447744,1.359682805
57,3079,0.03079,1.75503,6.9912,48.87687744,1.504919056
58,2282,0.02282,1.32356,7.9912,63.85927744,1.457268711
59,1617,0.01617,0.95403,8.9912,80.84167744,1.307209924
60,1075,0.01075,0.645,9.9912,99.82407744,1.073108832
61,718,0.00718,0.43798,10.9912,120.8064774,0.867390508
62,471,0.00471,0.29202,11.9912,143.7888774,0.677245613
63,260,0.0026,0.1638,12.9912,168.7712774,0.438805321
64,164,0.00164,0.10496,13.9912,195.7536774,0.321036031
65,92,0.00092,0.0598,14.9912,224.7360774,0.206757191
66,53,0.00053,0.03498,15.9912,255.7184774,0.135530793
67,13,0.00013,0.00871,16.9912,288.7008774,0.037531114
68,12,0.00012,0.00816,17.9912,323.6832774,0.038841993
69,10,0.0001,0.0069,18.9912,360.6656774,0.036066568
70,3,0.00003,0.0021,19.9912,399.6480774,0.011989442
71,2,0.00002,0.00142,20.9912,440.6304774,0.00881261
72,1,0.00001,0.00072,21.9912,483.6128774,0.004836129

Time::Piece(Time::Seconds)のONE_MONTHでうっかりやってしまった

「2011-10-01 00:00:00」の一ヶ月前のTime::Pieceオブジェクトを取得したいというような場合に「$t -= ONE_MONTH」みたいなコードを書いたら意図しない結果になってしまった。詳しくはhttp://blog.clouder.jp/archives/000409.htmlに書いてある通り。以下はよろしくないサンプルとそれの代替となるコード。

use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
use Perl6::Say;

for my $month ( 1 .. 12 ) {
    my $date = sprintf "2011-%02d-01 00:00:00", $month;
    my $t = Time::Piece->strptime( $date, '%Y-%m-%d %H:%M:%S' );
    my $t2 = $t - ONE_MONTH;

    my $format = '%Y-%m-%d %H:%M:%S';
    printf "%s\t%s\n", $t->strftime($format), $t2->strftime($format);
}
## 2011-01-01 00:00:00     2010-12-01 13:30:56
## 2011-02-01 00:00:00     2011-01-01 13:30:56
## 2011-03-01 00:00:00     2011-01-29 13:30:56
## 2011-04-01 00:00:00     2011-03-01 13:30:56
## 2011-05-01 00:00:00     2011-03-31 13:30:56
## 2011-06-01 00:00:00     2011-05-01 13:30:56
## 2011-07-01 00:00:00     2011-05-31 13:30:56
## 2011-08-01 00:00:00     2011-07-01 13:30:56
## 2011-09-01 00:00:00     2011-08-01 13:30:56
## 2011-10-01 00:00:00     2011-08-31 13:30:56
## 2011-11-01 00:00:00     2011-10-01 13:30:56
## 2011-12-01 00:00:00     2011-10-31 13:30:56

これをTime::Piece::Monthを使って正しくすっきりと書く。

use strict;
use warnings;
use Time::Piece::Month;
use Perl6::Say;

for my $month ( 1 .. 12 ) {
    my $date = sprintf "2011-%02d-01 00:00:00", $month;
    my $t = Time::Piece->strptime( $date, '%Y-%m-%d %H:%M:%S' );
    my $month = Time::Piece::Month->new($t);

    my $format = '%Y-%m-%d %H:%M:%S';
    printf "%s\t%s\t%s\n", $t->strftime($format),
      $month->next_month->end->strftime($format),
      $month->prev_month->start->strftime($format);
}
## 2011-01-01 00:00:00     2011-02-28 00:00:00     2010-12-01 00:00:00
## 2011-02-01 00:00:00     2011-03-31 00:00:00     2011-01-01 00:00:00
## 2011-03-01 00:00:00     2011-04-30 00:00:00     2011-02-01 00:00:00
## 2011-04-01 00:00:00     2011-05-31 00:00:00     2011-03-01 00:00:00
## 2011-05-01 00:00:00     2011-06-30 00:00:00     2011-04-01 00:00:00
## 2011-06-01 00:00:00     2011-07-31 00:00:00     2011-05-01 00:00:00
## 2011-07-01 00:00:00     2011-08-31 00:00:00     2011-06-01 00:00:00
## 2011-08-01 00:00:00     2011-09-30 00:00:00     2011-07-01 00:00:00
## 2011-09-01 00:00:00     2011-10-31 00:00:00     2011-08-01 00:00:00
## 2011-10-01 00:00:00     2011-11-30 00:00:00     2011-09-01 00:00:00
## 2011-11-01 00:00:00     2011-12-31 00:00:00     2011-10-01 00:00:00
## 2011-12-01 00:00:00     2012-01-31 00:00:00     2011-11-01 00:00:00

ほんのちょっとテストする時間をけちった結果は高くつきました。。コードを書けば書くほど自分の粗がぼろぼろと出てきてちょっと鬱。

公開鍵認証でSSHアクセス

備忘録に記す。リモートのサーバにローカルのPCからputtyでアクセスしたい、という状況。

  • サーバでssh-keygenコマンドを実行
    • 生成の際のパスフレーズはあってもなくてもよい(?)
    • ~/.ssh/id_rsaと~/.ssh/id_rsa.pubが生成される。
    • mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keysを実行して公開鍵をサーバに設定
  • サーバから~/.ssh/id_rsaをアクセスしたいPCにダウンロード
    • puttygen.exeを使いダウンロードした秘密鍵putty用に保存しなおす。この際パスフレーズを取り払うもよし。盗まれると危ないけれど。
    • puttyに設定する(SSH - 認証のプライベートキーファイル)
  • サーバのsshを再設定(/etc/ssh/sshd_config)
    • 公開鍵認証でしかログインできないという設定にするには「PermitRootLogin without-password」の一文を追加する必要がある。これがないと公開鍵認証でもログインできるという設定なので注意が必要。
    • /etc/init.d/sshd restartで再起動
    • rootユーザ以外でも公開鍵認証のみにしたい場合は「PasswordAuthentication no」を設定する

mysqlサーバを複数インスタンス立ち上げる

レプリケーションの練習をしたいとか、spiderエンジンを試してみたいとかの動機があったので複数のmysqlサーバが必要となった。使わなくなって久しいPentiumのPCをサーバにするとかも考えたけれど面倒なのでMacvmwareでなんとかしようと思ったが2Gのメモリだと複数の仮想サーバはやはりつらい。何とかならないかと思っていたところに1つのホストで複数のインスタンスを立ち上げる手法があったのでちょっと調べてみた。

2013-05-07追記
最近のmysqlの環境構築は以下の2つがよいかなと思ってます。

<<MySQL Sandbox>>
簡単に複数の検証用環境を立ち上げることができます。
CPANからインストールできる。(http://search.cpan.org/~gmax/MySQL-Sandbox-3.0.17/lib/MySQL/Sandbox.pm)
Perlerでない場合は
wget http://search.cpan.org/CPAN/authors/id/G/GM/GMAX/MySQL-Sandbox-3.0.19.tar.gz
tar zxvf MySQL-Sandbox-3.0.19.tar.gz
cd MySQL-Sandbox-3.0.19
perl Makefile.PL
make
make test
sudo make install


<<mysqlenv>>
http://blog.livedoor.jp/xaicron/archives/54459954.html

mysqld_multiコマンド

読んだとおりのコマンドが/usr/local/mysql/binに存在している。
mysqlサーバを複数立ち上げるときに気をつけなければならないのはリソースの競合で、下記に挙げたものをそれぞれのインスタンスごとに用意する必要がある

datadir
tmpdir
port
socket
pid_file

手順その1 「mysqld_multi --example」でmy.cnfの設定例を確認

実際に/etc/my.cnfに設置したのは下記

[mysqld_multi]
mysqld = /usr/local/mysql/bin/mysqld_safe
mysqladmin = /usr/local/mysql/bin/mysqladmin
user = root
password = root


[mysqld]
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
log-bin=mysql-bin
binlog_format=mixed

[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
no-auto-rehash

[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M

[mysqlhotcopy]
interactive-timeout

[mysqld1]
datadir = /var/mysql-1
port = 3307
socket = /tmp/mysql-1.sock
tmpdir = /var/mysql-1/tmp
server_id = 1

[mysqld2]
datadir = /var/mysql-2
port = 3308
socket = /tmp/mysql-2.sock
tmpdir = /var/mysql-2/tmp
server_id = 2

手順その2 各インスタンスのデータ等を初期化

細かい権限設定等は端折った。。mysqld_multiコマンドは[mysqld_multi]セクションの情報が使われるのでパスワード等は設定しておく必要がある。

mkdir /var/mysql-1
mkdir /var/mysql-1/tmp
chmod 777 /var/mysql-1/tmp
/usr/local/mysql/scripts/mysql_install_db --user=mysql --basedir=/usr/local/mysql --datadir=/var/mysql-1


mkdir /var/mysql-2
mkdir /var/mysql-2/tmp
chmod 777 /var/mysql-2/tmp
/usr/local/mysql/scripts/mysql_install_db --user=mysql --basedir=/usr/local/mysql --datadir=/var/mysql-2

手順その3 起動・終了

mysqld_multi {start|stop|report}

ログがdatadir以下に記録されているので、意図通りにうごかない場合はそれをチェックすること。また、mysqld_multiはperlスクリプトなので必要とあらばどういうコマンドに変換しているのかをwarnで出力してみるなどすればよいかも。

手順その4 クライアントの接続

今回設定したmy.cnfに則れば、各サーバインスタンスへの接続は下記コマンドとなる

mysql -u root -p -S /tmp/mysql-1.sock -P 3307
mysql -u root -p -S /tmp/mysql-2.sock -P 3308

ぶっちゃけこの本読みました。

まいど〜

CentOS5.6でMySQL5.5.15をソースからインストール

文字コード関連での追記あり

cmakeが必要になるのでインスコ

http://www.cmake.org/cmake/resources/software.htmlからソースをダウンロード

./bootstrap
make
make install

mysqlのインストール

WEB上のマニュアルは下記リンク

ソースを解凍した中のINSTALL-SOURCEというドキュメントの2.9章にも同様の内容が書いてある。

groupadd mysql
useradd -r -g mysql mysql


tar zxvf mysql-5.5.15.tar.gz
cd mysql-5.5.15
cmake .
(もしくは「cmake . -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci」
また、-DCMAKE_INSTALL_PREFIX=/home/taro/mysql_test などとしてインストール先を指定もできるので、検証用としてインスコできる。)

make
make install


cd /usr/local/mysql
chown -R mysql.mysql
./script/mysql_install_db --user=mysql
chown -R root .
chown -R mysql data


cp support-files/my-medium.cnf /etc/my.cnf
cp support-files/mysql.server /etc/init.d/mysql


/etc/init.d/mysql start

/usr/local/mysql/binにパスを通す必要があるので~/.bash_profileのPATH変数を適宜修正した。
source ~/.bash_profileをお忘れなく。

/etc/my.cnfはpid-fileの部分だけ1行加えたのが下記

Example MySQL config file for medium systems.

[client]
port = 3306
socket = /tmp/mysql.sock

[mysqld]
port = 3306
socket = /tmp/mysql.sock
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
log-bin=mysql-bin
binlog_format=mixed
server-id = 1
pid-file=/usr/local/mysql/data/mysqld.pid

[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
no-auto-rehash

[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M

[mysqlhotcopy]
interactive-timeou

rootユーザのパスワード管理等は「./script/mysql_install_db --user=mysql」を実行した後に下記のようなコメントが出ているのでそれに沿って実施すればよい。

[root@localhost mysql]# ./scripts/mysql_install_db --user=mysql
Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

./bin/mysqladmin -u root password 'new-password'
./bin/mysqladmin -u root -h localhost.localdomain password 'new-password'

Alternatively you can run:
./bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default. This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd . ; ./bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd ./mysql-test ; perl mysql-test-run.pl

Please report any problems with the ./bin/mysqlbug script!

追記

コンパイルオプションで文字コードを指定しない場合はlatin1になるが下記の設定を駆使することで文字化けは回避できるが、面倒といえば面倒

  • character_set_server = utf8  (my.cnfの設定)
  • 「set names utf8」 (アプリケーションからDB接続する際に一番最初に発行する)


コンパイルオプションを設定することで始めからutf8に統一しておくこともできる

  • cmake . -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci

AnyEventサンプルその6

AnyEvent::JSONRPC::Liteから要点を抜き出す。サーバ内部のcondvarをrecvしている箇所。

サーバ側

use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;
use Data::Dumper;

tcp_server '127.0.0.1', 8888, sub {
    my ($fh) = @_ or die;
    my $handle = AnyEvent::Handle->new(
        fh       => $fh,
        on_error => sub {
            shift->destroy;
            warn "ON ERROR $!";
        },
        on_eof => sub {
            shift->destroy;
            warn "ON EOR $!";
        },
    );

    $handle->on_read(
        sub {
            undef $handle;
            shift->unshift_read(
                line => sub {
                    warn Dumper \@_;
                    my ( $handle, $request ) = @_;
                    my $inner_cv = AE::cv;
                    $inner_cv->cb(
                        sub {
                            my $result = $_[0]->recv;
                            $handle->push_write( $result . "\n" );
                        }
                    );
                    $inner_cv->send( $request . " echo !!" );
                }
            );
        }
    );
};
AE::cv->recv;

クライアント側

use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;
use Data::Dumper;

my $cv = AE::cv;
tcp_connect '127.0.0.1', 8888, sub {
    my ($fh) = @_ or die;
    my $handle = AnyEvent::Handle->new(
        fh       => $fh,
        on_error => sub {
            shift->destroy;
            warn "ON ERROR $!";
        },
        on_eof => sub {

        },
    );
    $handle->push_write("this is testdayo\n");
    $handle->push_read(
        line => sub {
            undef $handle;
            my ( $handle, $line ) = @_;
            $cv->send($line);
        }
    );
};
print $cv->recv;
print "\n"

AnyEventサンプルその5

AnyEvent::Handleの扱いに苦労した。

ioやtimerのヲチャー変数のようにundefでも行けた。参照カウントが残っているとどうもよくないのかしらね。AnyEvent::JSONRPC::Liteでもweaken $handleとやっている部分がどうも今ひとつ理解できずにいたのだが、大事だよということはよくわかった。

サーバ側
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;
use Data::Dumper;

tcp_server '127.0.0.1', 8888, sub {
    my ($fh) = @_ or die;
    my $handle = AnyEvent::Handle->new(
        fh       => $fh,
        on_error => sub { shift->destroy; warn "ON_ERROR"; },
        on_eof   => sub { shift->destroy; warn "ON_EOF"; },
    );

    $handle->on_read(
        sub {
            undef $handle;
            warn "ON_READ";
            shift->unshift_read(
                json => sub {
                    my ( $handle, $request ) = @_;
                    $handle->push_write(
                        json => { foo => $request->{foo} . " echo!!" } );
                }
            );

        }
    );
};

sub _time {
    warn AnyEvent->now;
}
AE::cv->recv;
サーバ側(デッドロック?してしまうだめな例)
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;
use Data::Dumper;

tcp_server '127.0.0.1', 8888, sub {
    my ($fh) = @_ or die;
    my $handle = AnyEvent::Handle->new(
        fh       => $fh,
        on_error => sub { shift->destroy; warn "ON_ERROR"; },
        on_eof   => sub { shift->destroy; warn "ON_EOF"; },
    );
    $handle->on_read(
        sub {
            warn "ON_READ";
            ## ここのundef $handleがないとダメ。
            shift->unshift_read(
                json => sub {
                    my ( $handle, $request ) = @_;
                    $handle->push_write(
                        json => { foo => $request->{foo} . " echo!!" } );
                    warn "WRITE";
                    $handle->destroy;
                }
            );
        }
    );   
};

sub _time {
    warn AnyEvent->now;
}
AE::cv->recv;
クライアント側
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;
use Data::Dumper;

my $cv = AE::cv;
tcp_connect '127.0.0.1', 8888, sub {    my ($fh) = @_ or die;
    my $handle = AnyEvent::Handle->new(
        fh       => $fh,
        on_error => sub { shift->destroy; warn "ON_ERROR"; },
        on_eof   => sub { shift->destroy; warn "ON_EOF"; },
    );

    $handle->push_write( json => { foo => 'foo' } );

    $handle->push_read(
        json => sub {
            my ( $handle, $result ) = @_;
            $cv->send( $result->{foo} );
        }
    );
};
print $cv->recv;
print "\n";