レシピ |
■ PHP 4.xまたは5.x (http://www.php.net/) 別途モジュールは必要ありません |
最近は,どのプログラム言語でもデータベースと絡むお話が多くなりました。データベースとプログラムの接続そのものは,入門書や雑誌記事が豊富なので困ることはありません。しかし,実際に何万件かのデータを入れて…という作業がやっかいだったりします。
今月のサンプルは,日本人の名前を大量に生成してテキスト・ファイルに書き出すというものです。プログラム言語はPHPを使います(サーバーは必要ありません)。
データベース開発の案件で,動作確認用のテスト・データを作る必要があるとき,すべてのテスト・データが同じ“テスト太郎”だったり,あるいは元のデータが20人ぶんの名前の繰り返しになっているだけだったり,ということは少なくありません。データがデータベースに入るか入らないかを調べるためなら,件数だけそろえればなんとかなります。しかしデータベース・プログラムの検証では,「50万件のデータがあるけれど,実質は20個のデータを繰り返しているだけ」というテスト・データでは何の役にも立ちません。かといって50万人ぶんの人名データを集めるのも現実的には大変です。
そこで,いかにもありそうな名前を乱数と配列で自動生成するプログラムを作ってみました。このプログラムを使えば,およそ3万件以上の名前を自動生成できます。
ロジックは簡単です。名字や名前に使われるであろう文字を配列に入れて,乱数で適当な文字を抽出して組み合わせるだけです。ただしあまりにも突拍子のない名前ができても面白くないので(ここが作り手のこだわりと意地),例えば「田田」のように1文字目と2文字目が同一文字の名字が生成されたら,作り直すようにしてあります。作り直しの際には再度漢字の組み合わせをさせるのではなく,伊藤,田中といったポピュラーな名字から選択するようにしました。
名前のほうは女性名も生成されるように配慮しました。といっても「子」か「美」が最後についてくる程度なので,こちらの現実味はちょっと微妙です。男性用と女性用で名前生成用のデータ配列を変えるといった工夫を入れるといいでしょう。さらに男性名を生成するか,女性名を生成するかの比率を組み込めば,思い通りのテスト・データを作れますね。
プログラム・ファイルは,C:\phpに「name_generator.php」(リスト1[拡大表示])の名前で作成します。コマンドプロンプトで「C:\php\php name_generator.php」とすると実行できます。テスト・データは,プログラムと同じC:\phpフォルダにname_list.txtの名前で生成されます(図1[拡大表示])。
<?php $count_data = 1000; for($a=0;$a<$count_data;$a++){ //名字生成用配列 $FN_1 = Array("","山","川","谷","田","小","石","水","大","橋","野","池","吉","中"); $FN_2 = Array("田","本","川","口","野","村","崎","山","島","上","浦","内","原"); //予備名字 $FN_3 = Array("加藤","鈴木","佐藤","高橋","渡辺","伊藤","斉藤","佐々木","原","田中"); //名前生成用配列 $LN_1 = Array("順","優","恵","浩","裕","正","昭","真","純","清","博","孝","幸"); $LN_2 = Array("","一","二","子","美","一郎","実","義","夫","雄","太郎","彦"); $len_FN1 = count($FN_1) - 1; $len_FN2 = count($FN_2) - 1; $len_FN3 = count($FN_3) - 1; $len_LN1 = count($LN_1) - 1; $len_LN2 = count($LN_2) - 1; $seed_a = mt_rand(0, $len_FN1); $seed_b = mt_rand(0, $len_FN2); $seed_c = mt_rand(0, $len_LN1); $seed_d = mt_rand(0, $len_LN2); $name_a = $FN_1[$seed_a]; $name_b = $FN_2[$seed_b]; if($name_a==$name_b || !$name_a || $name_a.$name_b=="池川"){ $seed_a = mt_rand(0, $len_FN3); $name_a = $FN_3[$seed_a]; $name_b = ""; } $name_c = $LN_1[$seed_c]; $name_d = $LN_2[$seed_d]; //生成された名前を変数に格納 $name .= $name_a.$name_b.$name_c.$name_d."\r\n"; } chdir("C:\php"); $fh = fopen("name_list.txt","w"); fputs($fh,$name); fclose($fh); ?> |
生成用の名前は
1文字でなくてもいい
生成ロジックを考えてみると,名字と名前をそのまま配列に入れて,「Array("田中","伊藤"…)」という作り方でもいいような気がします。しかしその方法では,生成する名字の数は名字データをいくつ用意できるかにかかってきます。リスト1は1文字目,2文字目にそれぞれ13文字を設定しているので,単純計算では13×13で169パターンの名字を生成できますが,名字だけで139個を用意するのは大変ですよね。ちなみに,出現頻度を増やしたい文字は配列内に複数個入れておきます。例えば「Array("田","田"…)」のようにすれば,単純に「田」の出現頻度は2倍になります。
注目してほしいのは名前生成用の配列です。$LN_2配列には,「""」というNull文字を入れてあります。これは1文字しかない名前も生成させるためです。また,「一郎」という2文字データがあるのは,「純一郎」のような3文字の名前を生成するためです。このように配列内のデータを一文字の漢字にこだわる必要はまったくありません。
乱数値の限界を超える方法
このプログラムの欠点は,乱数を使用するプログラムの常で,どうしても結果が偏る点です。理屈ではユニークな3万通りの名前が作れるはずですが,実際にはダブりが発生します。PHPの乱数は乱数生成シード値の生成も自動で行われるため,リスト1では意図的にシードの再作成をしていません。言語仕様まかせです。しかし(おそらく)パソコンの処理速度が速ければ速いほど,乱数の発生結果が前回の発生乱数と近似してくるので,どこかに遅延のためのsleep処理を入れるなどの対策が必要かもしれません。ところがPHPではsleepは秒指定なので,5万件のデータを生成する場合,1件ごとに遅延処理を挿入すると最低5万秒(約14時間!)かかってしまうことになります。ルーチンそのものは単純ですから,マイクロ秒でsleep指定できる言語に移植したほうが現実的でしょう。
この手のプログラムは,一つあると便利だなと思っていても,なんとなく面倒でなかなか作成に着手できません(着手しませんが正解)。しかし想像以上に出番が多いものです。せっかくの機会ですから,いろいろとカスタマイズして,現実的な名前生成プログラムを作ってみてはいかがでしょうか。