Processing math: 0%

2012-12-30

Box-Muller法と正規乱数生成器

PRML上巻の序章を読んだ頃に、ガウス分布に肄う乱数発生器を作りたいと思っていたので作ってみた。つまり、平均値と標準偏差を引数に取る乱数生成関数である。Web検索するとBox-Muller法を使うのと、中心極限定理を使う方法があるのでそれぞれ調べてみた。

Box-Muller法を使う

Box-Muller法は次の式によって作られる2つの独立変数を使うらしい。
\sqrt{-2 * log(α)} * sin(2πβ)
\sqrt{-2 * log(α)} * cos(2πβ)
式だけ見てもイメージが湧かないので、Octaveを使ってサンプル数2000でテストしてみた。
check_box_muller.matlab
clear;
alpha = rand(1, 1000);
beta = rand(1, 1000);
val1 = sqrt(-2 * log(alpha)) .* sin(2 * pi * beta);
val2 = sqrt(-2 * log(alpha)) .* cos(2 * pi * beta);
hist([val1,val2], 20);


ちゃんと正規分布っぽい。Box-Muller法で生成した標準正規分布に標準偏差を掛けて、平均値を足せば完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"use strict";
 
/**
 * @param {Number} mean 平均値
 * @param {Number} std 標準偏差
 */
function gaussianRandom(mean, std) {
  return randByBoxMullerTransform() * std + mean;
}
 
// Box Muller法を使って標準正規分布N(0,1)を得る
var randByBoxMullerTransform = (function() {
  var vals = [];
 
  function calc() {
    var alpha = Math.random(),
        beta = Math.random();
    return [
      Math.sqrt(-2 * Math.log(alpha)) * Math.sin(2 * Math.PI * beta),
      Math.sqrt(-2 * Math.log(alpha)) * Math.cos(2 * Math.PI * beta)
    ];
  }
 
  return function() {
    vals = vals.length == 0 ? calc() : vals;
    return vals.pop();
  }
})();
 
// Module export
module.exports = gaussianRandom;

中心極限定理を使う

中心極限定理を利用すると、複数の一様乱数の和を使うだけで標準正規分布N(0,1)が得られる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"use strict";
 
/**
 * @param {Number} mean 平均
 * @param {Number} std 標準偏差
 */
function gaussianRandom(mean, std) {
  return randByCentralLimitTheorem() * std + mean;
}
  
// 中心極限定理を使って標準正規分布N(0,1)を得る
function randByCentralLimitTheorem() {
  var v = 0;
  for (var i = 0; i < 12; i++) {
    v += Math.random();
  }
  return v - 6;
}
  
// Module export
module.exports = gaussianRandom;
こっちの方が圧倒的に簡単でびびる。

2012-12-27

autojumpをMacとUbuntuに導入した

サーバーとMacで共通のzsh用dotfilesを使っているのだが割とうまくいった。zshとautojumpの連携は、zshのプラグインマネージャのantigenを使った。

autojumpのインストール

Mac

brew install autojump

Ubuntu

sudo apt-get install autojump

dotfilesの設定

自分のdotfilesリポジトリにantigenをsubmoduleとして追加

cd dev/dotfiles
git submodule add https://github.com/zsh-users/antigen.git antigen
git submodule init
git submodule update

.zshrcからantigenを呼び出し

~/.zshrc
source ~/dev/dotfiles/antigen/antigen.zsh
antigen-lib
antigen-bundle autojump
antigen-apply

j がaliasとして使われるので、jのaliasを変更しておく。


2012-12-21

MacOS X Mountain Lionの通知センターを便利に使う

この記事はVOYAGE GROUP エンジニアブログ : Advent Calendar 2012の20日目である。特にテーマは何でも良いそうなので、Mac OS X Mountain Lionで追加された通知センターについて。

VOYAGE GROUPではグループウェアにサイボウズガルーン3を使っている。このサイボウズガルーン3、不便な事にMac用のリマインダークライアントが提供されていない。そこで、ミーティングをすっぽかさないためにもMacユーザーはリマインダーを各自実装する必要がある。
私はAPIを叩いて結果をGrowl通知させていたが、OSアップデートでこれが動かなくなった。単にGrowlをインストールするのを忘れていただけだが、通知センターがOSに追加されたのでGrowlはもう不要な気がする。

というわけで、通知センターでガルーンのスケジュール通知を受けられるようにしてみよう。私はObjective-Cが全く書けないので、ガルーンのAPIを叩く所はとりあえず現状のを使いまわして、JSON形式のファイルを配置するようにした。

通知センターに通知を送る

通知センターを使うのに一番手っ取り速いのは多分Cocoaなので、Xcodeでそれっぽいコードを書く。NSUserNotificationNSUserNotificationCenterを使えば良い。

// AppDelegate.h
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
// Windowはいらないのでxibファイルから消しておく
@property (assign) IBOutlet NSWindow *window;
@end
view raw AppDelegate.h hosted with ❤ by GitHub
// AppDelegate.m
#define DATA_FILE_PATH @"/Users/hagino3000/tmp/data.json"
#define CHECK_INTERVAL 20 * 60
#import "AppDelegate.h"
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
// 初回起動
[self checkSchedule];
// 次回以降はタイマーで起動
[NSTimer scheduledTimerWithTimeInterval:CHECK_INTERVAL
target:self
selector:@selector(checkSchedule)
userInfo:nil
repeats:YES];
}
- (void)checkSchedule {
// ファイルでスケジュールデータを受けとる
NSInputStream* stream = [NSInputStream inputStreamWithFileAtPath:DATA_FILE_PATH];
[stream open];
NSError *error;
NSArray *arr = [NSJSONSerialization JSONObjectWithStream:stream
options:NSJSONReadingMutableContainers
error:&error];
[stream close];
for (int i = 0; i < arr.count; i++) {
NSDictionary *dict = [arr objectAtIndex:i];
[self sendNotification:[dict objectForKey:@"title"]
subtitle:[dict objectForKey:@"time"]
body:[dict objectForKey:@"body"]
url:[dict objectForKey:@"url"]];
}
}
- (void)sendNotification:(NSString *)title subtitle:(NSString *)subtitle body:(NSString *)body url:(NSString *)url {
// UserNotificationの生成
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
userNotification.actionButtonTitle = @"開く";
userNotification.title = title;
userNotification.subtitle = subtitle;
userNotification.informativeText = body;
userNotification.userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:url, @"url", nil];
// 通知センターに送る
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
NSLog(@"done!!");
}
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
// アクションボタンがクリックされたら、スケジュールのページを開く
NSURL *url = [NSURL URLWithString:[notification.userInfo valueForKey:@"url"]];
[[NSWorkspace sharedWorkspace] openURL:url];
}
@end
view raw AppDelegate.m hosted with ❤ by GitHub
通知を表示するだけなら通知を送った後にアプリケーションを終了さても良いが、通知をクリックした時に任意の操作をしたい場合はアプリケーションを生かしておく必要がある。ガルーンAPIを叩く部分のCocoa移植は後にして、とりあえず動くようになった。めでたしめでたし。



という訳で、せっかくエンジニアをやっているので、日々ハックできる所はハックしていきたいですね。


次は@pank7さんです。

このエントリーをはてなブックマークに追加

2012-12-12

はてなダイアリーから引越しました。

おそらくはてなダイアリーの方はもう更新しません(^-^)

旧ブログ
http://d.hatena.ne.jp/hagino_3000


今の所、2012年分の記事まで移行完了。

このエントリーをはてなブックマークに追加