2012年2月21日火曜日

テルマエ・ロマエに見る法則

いま話題の「テルマエ・ロマエ」をコンビニで立ち読みしてきた.
Wikipediaにある概要は以下のとおり.

古代ローマ時代の浴場と、現代日本風呂をテーマとしたコメディである。入浴文化という共通のキーワードを軸に、現代日本にタイムスリップした古代ローマ人の浴場設計技師が、日本の風呂文化にカルチャーショックを覚え、大真面目なリアクションを返すことによる笑い[2]を描く。

面白い部分は,まさにここに書かれている通り「古代ローマの大真面目なリアクション」にある.
異なる時代の異なる人種からみた日本の風呂の面白さと,表現の秀逸さ.
 - 書店員の選ぶマンガ大賞2010[4]
 - 第14回手塚治虫文化賞短編賞[2]受賞
 - 「このマンガがすごい!」2011年版オトコ編2位
 - 全国書店員が選んだおすすめコミック2011、3位入選。
これらの実績からも,万人受けする名作であることは間違いない.


テルマエ・ロマエは,古代ローマ時代と現代日本を行き来する古代ローマ人の物語.

互いに重なり合わない2つの時代を主人公が行き来するパターン
これは最近の勝ちパターンのような気がする.

TBSで高視聴率だったドラマ,「JIN - 仁-」.
これは,主人公である南方仁が現代から江戸時代にタイムスリップする話.

売れっ子作家の中には,【いま売れる物語の公式】が暗黙知として宿っているのではないだろうか.

2012年2月9日木曜日

問題解決プロフェッショナル 「思考と技術」

問題解決プロフェッショナル 「思考と技術」 を2章まで読んだ.
2章の技術編では有用な知見が多かったのでここにまとめておく.



【はじめに】


問題の原因や解決策を考える際に,思いついた順に箇条書きにする人はいないだろうか?
はい,私です.
苦労して列挙したリストが増えれば増えるほど混乱して最終的に考えたことが無駄になったり...
次元の異なる要素が同じカテゴリーの中に列挙されていたり...
(例 : アメリカ,中国,オーストラリア,京都...)


箇条書きの問題点は3つ.
1つは漏れ
「これ以外にも要素は無いですか?」
という問いに胸を張ってNOと言い切れるだろうか.

2つ目はダブり
一見異なるもののようでも,実は本質的に同じことを言っていたりする.
本来同じものは極力排除したい.

最後は次元の違い
分析結果をプレゼンテーションする際に,次元の異なるものを一緒くたにすると聴衆に伝わらない.
他人から見ても分かりづらいし,時間が経てばそのうち思い出せなくなる.

こういった問題を無くすために,システマティックに思考の整理を行うツールとしてMECEとロジックツリーがある.



【MECE】

MECE (Mutually Exclusive and Collectively Exhaustive)とは,もれなくダブりのない状態のこと.
簡単な例としてはジャンケンにおける手.
「グー・チョキ・パー」,これでMECE.



フレームワークと呼ばれる決まった切り口を使って,MECEな状態に問題を切り分けることで思考の整理を行う.

・代表的なフレームワーク
  3C+1C : 顧客(Customer),競合(Competitor),自社(Company) + 流通チャネル(Channel)
  ビジネスシステム : 製品が開発されてから市場に出るまでの流れを時間軸で整理したもの
  4P : 製品(Product)、価格(Price)、流通(Place)、プロモーション(Promotion)
  事業ポートフォリオ : 相対マーケットシェア × 市場成長率





【ロジックツリー】


問題の原因を深堀りして,解決策を具体化し,因果関係を可視化する技術のこと.
思いついた順の箇条書き,即ち根拠のないアイディア出しからの脱却のためには必須なツール.
やり方は,課題の問題や解決策をMECEに分解し続けるだけ.
これ以上やっても意味が無いくらいに掘り下げられたら,枝にある要素を優先順位付けする.




ロジックツリーの例として痩せる方法のロジックツリーと箇条書きの差異を説明するサイトがあった.
かなり参考になる.




【最後に】


今回は,問題解決プロフェッショナル 「思考と技術」における2章の思考ツールのみをまとめた.
この本はコンサルティングファーム等で配られる程の名著のようだ.
実際に読んでみても読みやすく,得られるものも多い.
一般的にMECEやロジックツリーが浸透すれば,社内会議等も効率的に行えると思う.
もっと幼少期にこの本に出会って,雑然とした思考を一刻も早く追い出したかった.
これからはこの技術を体得するために日常生活等でも応用していこうと思う.





2012年2月5日日曜日

C++プログラミング入門(5) // 配列とnew,ポインタと参照

配列とnew

【int array[5];】と【int *array = new int[5];】の違い

・配列には「必要なメモリの大きさがコンパイル時にわかっている」「関数が終わるときに必ず解放する」制限がある

・newは上記の制限がない分,メモリの空き場所を探すのに時間がかかる

参照とポインタ

・参照は必ず初期化する必要があり,指すものを変えられない.(安全)
・ポインタは上記の成約がない.(気をつけるべし)

データ量の大きいクラスTを引数とする関数func (例えばこんなの void func(T t){} )
を呼び出した時,コピーが生成される.
このムダを省くために,
「書き換えるときはポインタ渡し,書き換えないなら参照」
を徹底するべし.
(ポインタでも 書き換えたくない場合はconstをつければ参照と同様な効果が得られるが,呼び出し側のコードを見ただけでは判断できなくなるので使い分けること)

C++プログラミング入門(4) // C++文法について

【テンプレート】


#include
using namespace std;

// Type型のメンバ変数をもつクラスA
template  class A{

private:
 const Type data;

public:

 A(){
  cout << "default constructor\n";
 }


 A(Type d):data(d){ // 初期設定なので代入
  cout << "constructor\n";
 }

 /*
 A(Type d){
  data = d; // constなメンバに代入はできない
  cout << "constructor\n";
 }
 */

 ~A(){
  cout << "destructor\n";
 }

 Type getData(){
  return data;
 }
};

int main(){

 A  hoge_float(1);
 A  hoge_int(3.3);
 A  hoge_double(3.3);

 cout << "hoge_float:" << hoge_float.getData() << endl;
 cout << "hoge_int:" << hoge_int.getData() << endl;
 cout << "hoge_double:" << hoge_double.getData() << endl;

 return 0;
}

C++プログラミング入門(3) // File入出力

ステージデータの読み込み機能をつけて任意の名前のファイルを読み込める構成にしたい.
エラー処理等は全く考慮せず,ステージの幅と高さについては固定長のものしか扱えない点は改善すべき.

void readFunc(char *fileImage,const char *filename){

 ifstream inputFile(filename,ifstream::binary);
 inputFile.seekg(0,ifstream::end);

 int fileSize = static_cast<int>(inputFile.tellg());
 inputFile.seekg(0,ifstream::beg);
 inputFile.read(fileImage,fileSize);

}

int main(){

 char filename[100];
 char *gStage;
 gStage = new char[gHeight*gWidth];

    // 読み込み開始--------------

 cout << "Please input a file name:";
 cin >> filename;
 readFunc(gStage,filename);

    // 読み込み完了--------------


 return 0;
}



【備考】
2次元のように見せるクラスの定義では,非公開メンバとしてポインタ・縦・横をもたせ,参照用の公開メンバ関数を用意する.
メンバ変数には先頭にmを付ける.

C++プログラミング入門(2) // 前回のまとめと条件分岐の考察

【前回のまとめ】

ゲームとは入力を受け取って,それを反映させ,結果を表示する処理の繰り返しである.
イメージは, while(1){ 入力; 反映; 表示; if(クリア){ break; } }

(チェック)
・文字列定数の途中での改行
⇒行末に"\"を付ける

・グローバル変数の命名規則
⇒接頭辞としてgをつける+キャメル記法で読みやすく

・constの使い方
⇒変更する予定のないものには極力付ける

・列挙型のメリット
⇒未定義の値を許容しない,デバッグ時列挙型の名前で見られる.
なお,列挙型の名前は大文字で.

・メモリの解放
⇒delete と ポインタに0代入

・1次元配列を2次元的に扱う
⇒ state[y*width + x] : 幅widthの2次元配列における(x,y)成分にアクセス.

・移動は差分で
⇒ 現在地 + 移動幅 で記述するとプログラムが簡潔に書ける.

・計算で求まる情報は極力保存しない.矛盾が出ると厄介だから

・キャラの移動時にはステージ内かどうかを先に確認

・三項演算子
⇒ a = b ? c : d (bがtrueならa=c; bがfalseならa=d;)


【条件分岐における思考過程】
キャラクターが動作するときのupdate関数では,定義されない動作が無いように,MECEになるように網羅的に条件分岐を考える.

今回は,列挙型のObjで定義された
SPACE,WALL,GOAL,MAN,MAN_ON_GOAL,BLOCK,BLOCK_ON_GOAL,UNKNOWN,
という8パターンの状態と,現在地,移動先,移動先の奥における組み合わせを網羅的に考える.
単純に考えると8×8×8通りあるように見えるが,あり得ない組み合わせ(動作が許容されない,そもそもありえない)を省いて考えるとさほど考える状況は少ない.
動作が許容されないケースの例としては移動先が壁である場合などがある.
そもそもあり得ない場合としては,現在地にMAN,MAN_ON_GOAL以外が入るケース.

現在地は2通り,移動先は4通りに絞られる.
コードとして定義が必要な場合は,6通りのケース(【参照:概念図】)のみ.
簡単のため,今回は愚直にswitch構文でこれらを実装した.
同じようなコードが多々あり冗長性があるためまたコードは簡素化出来る.
今回は漏れを無くすための思考訓練として安定的に動くプログラムを実装したつもりである.


【概念図】


Blogger上にプログラムソースをきれいに表示する方法

Blogger上にプログラムソースをきれいに表示する方法として,ここ( http://toldalie.blogspot.com/2012/01/blogger_31.html )
が参考になりました.


まとめると,
(i) HTMLコードをここ( http://www.way2blogging.org/widget-generators/syntax-highlighter-scripts-generator )で生成してコピー.

(ii) Bloggerの ”デザイン>HTMLの編集>” でテンプレートのソースを表示し(i)でコピーしたコードを</head>の直前にペーストする.

(iii) 投稿記事をHTML編集モードで開いて以下のタグを挿入.
<pre class="brush: c++; tab-size: 2">
// コードを記述</pre>
上記ソースはC++プログラムを貼り付ける場合の例.



実際に使ってみたら,
作成された HTML ファイルは承認できません: タグが破損しています:
とか怒られる.
無視して記事を投稿すると,ソースが変わってたりする.
いろいろ調べたら,”<”とか”>”はHTMLにおけるタグと勘違いしちゃうよ!ってのに気付いて.
"<" ⇒ "&lt;"
">" ⇒"&gt;"
に置き換えたら怒られなくなった.
SQLインジェクションとかサニタイジング周りを昔浅く勉強してたのですぐトラブルシュート出来た.
参考サイト: http://www.imymode.com/lab/keiji03.htm

2012年2月4日土曜日

C++プログラミング入門(1) // 倉庫番プログラムの実装

うまく修論が通れば大学院を卒業して,晴れて社会人になる.
入社試験でC++のプログラミングを課されるくらいなので,きっと仕事でC++を使うんだろう.
そこで,軽く文法等を覚えるために入社前に勉強することにした.

【現状】
研究ではもっぱら数値計算用ソフト(MATLAB)のみを使っており,メモリ等を意識してプログラムを組むことは無い.
変数の宣言やメモリの確保のような行儀の良いプログラムを書くのは3年前くらいにCでかじった程度.

純粋に文法を勉強するのもあほらしいので,


ゲームプログラマになる前に覚えておきたい技術



っていう分厚い本を買って読むことにした.

第一章ではいわゆる倉庫番のプログラムのお勉強.
読みすすめていくと,プログラムから醸しだされる著者の優秀さを感じ取った.
優秀なプログラマの書くプログラムは読んでて感動する.


本を読んでみて重要だと思った点をコメントアウトで追記しつつプログラムを組んだ.
以下はコンソール上で倉庫番を楽しむプログラム.
コピペしてコンパイルすれば動くはず.


#include<iostream>
using namespace std;


// ステージのオブジェクトの定義
// 列挙型に出来るときはなるべく使う.
// 利点は2つ.変な値が入らない+デバッグの際に列挙型の名前で確認できる.
// 区別のために要素は大文字.
enum Obj{
// 列挙型は単なるint型
SPACE, // 0
WALL, // 1
GOAL,
MAN,
MAN_ON_GOAL,
BLOCK,
BLOCK_ON_GOAL,

UNKNOWN,
};


// ステージ
// グローバル変数はどこからも見えるので接頭辞をつけて区別する.
// いじらない変数にはconstをつける.
// キャメル記法推奨.
// 文字列定数の途中で改行する場合は文末に\が必要.
const char gStage[] = "\
##########\n\
#    p   #\n\
#  .o  0 #\n\
#        #\n\
##########";

// 幅と高さ
const int gHeight = 5,gWidth = 10;

void initialize(Obj *state,const int height,const int width,const char *stage){
const char *p = stage;
int x=0,y=0;

while(*p){
 Obj t;
 switch(*p){
  case ' ': t = SPACE; break;
  case '#': t = WALL; break;
  case '.': t = GOAL; break;
  case 'p': t = MAN; break;
  case 'P': t = MAN_ON_GOAL; break;
  case 'o': t = BLOCK; break;
  case '0': t = BLOCK_ON_GOAL; break;
  case '\n': t = UNKNOWN; y++; x=0; break;
  default : t = UNKNOWN; break;
 }

 if(t!=UNKNOWN){
  state[y*width+x] = t; // 縦height,横widthをもつ二次元配列における(x,y)にアクセス.
  x++;
 }

 p++;
}
}

void draw(const Obj *state,const int height,const int width){
const char c[] = {' ','#','.','p','P','o','0'}; // 列挙型は単なるint型
for(int h=0;h<height;h++){
 for(int w=0;w<width;w++){
  cout << c[state[h*width+w]];
 }
 cout << "\n";
}
}

void update(Obj *state,const char input,const int height,const int width){

int x,y,tx,ty,dx=0,dy=0;
int p,tp,tp2;

// 移動方向の定義
// 位置 + 差分
switch(input){
 case 'a': dx = -1; break;
 case 's': dx = 1; break;
 case 'w': dy = -1; break;
 case 'z': dy = 1; break;
 case 'q': cout << "Bye"; break;
 default : cout << "undefined input\n please input \"a,s,w,z\""; break;
}

// プレイヤー位置の探索
for(int i=0;i<height*width;i++){
 if(state[i]==MAN || state[i]==MAN_ON_GOAL){
  x = i%width; // 現在地のx座標
  y = i/width; // 現在地のy座標
  // 計算で求まる情報はなるべく保存しないほうがいい...
  p = i; // 現在地
 }
}

// 移動できるか判定
tx = x + dx; ty = y + dy;
if(tx < 0 || width <= tx || ty < 0 || height <= ty){
 cout << "can't move";
 return;
}

// 移動先の計算
tp = ty*width + tx;
tp2 = (ty+dy)*width + (tx+dx);

// 移動
switch(state[tp]){
 case SPACE:
  state[p] = (state[p]==MAN) ? SPACE : GOAL;
  state[tp] = MAN;
  break;

 case GOAL:
  state[p] = (state[p]==MAN) ? SPACE : GOAL;
  state[tp] = MAN_ON_GOAL;
  break;

 case BLOCK:

  switch(state[tp2]){
   case SPACE:
    state[tp2] = BLOCK;
    state[tp] = MAN;
    state[p] = (state[p]==MAN) ? SPACE : GOAL;
    break;

   case GOAL:
    state[tp2] = BLOCK_ON_GOAL;
    state[tp] = MAN;
    state[p] = (state[p]==MAN) ? SPACE : GOAL;
    break;

   default:
    break;
  }
  break;

 case BLOCK_ON_GOAL:

  switch(state[tp2]){
   case SPACE:
    state[tp2] = BLOCK;
    state[tp] = MAN_ON_GOAL;
    state[p] = (state[p]==MAN) ? SPACE : GOAL;
    break;

   case GOAL:
    state[tp2] = BLOCK_ON_GOAL;
    state[tp] = MAN_ON_GOAL;
    state[p] = (state[p]==MAN) ? SPACE : GOAL;
    break;

   default:
    break;
  }
  break;

 default:
  break;
}


}

bool isClear(const Obj *state,const int height,int width){

for(int i=0;i<height*width;i++){
 if(state[i]==BLOCK){
  return false;
 }
}

cout << "Congratulations!!";
return true;
}

int main(){
char input;
Obj *state = new Obj[gHeight*gWidth];

initialize(state,gHeight,gWidth,gStage);
draw(state,gHeight,gWidth);

do{
 cout << "Input:";
 cin >> input;

 update(state,input,gHeight,gWidth);
 draw(state,gHeight,gWidth);
}while(input!='q' && !isClear(state,gHeight,gWidth));


delete[] state; // メモリの解放
state = 0; // 使い終わったポインタには0を代入


return 0;
}







【実行画面】

2012年2月3日金曜日

集合知プログラミング

【協調フィルタリング】
多くのユーザの嗜好情報を蓄積し、あるユーザと嗜好の類似した他のユーザの情報を用いて自動的に推論を行う方法論

類似の評価
・ユークリッド距離
多次元空間中での2点間の距離,近いほど類似度が高い.
正規化されていないデータに大して脆弱

・ピアソン係数
正規化されていないデータではこちらのほうが有効


(例)映画の評者といくつかの映画に対する評価データ
◇アイテムの推薦方法
 似ているユーザーの探索 : 共に評価している映画に対して類似度計算
 アイテムの推薦 : ユーザーが見ていない映画に対する評点に類似度で重み付けして合計を計算し,類似度の総和で除算

◇アイテムベースのフィルタリング
 - 製品同士の類似度 - 
既に見た映画と見てない映画の類似度を計算.
既に見た映画の評点を導出した類似度で重み付けして総和をとり,類似度の総和で除算