ハッシュ法
2分木なども終わって、検索処理のさらなる高速化として、 ハッシュ法を説明する。
オープンアドレス法
電話番号が記録されているのかどうかを探す処理を考えると、 最も単純な方法は、電話番号を配列の添字にする方法となる。
int array[ 1000000 ] ; // 局番2桁+4桁 void entry( int phone ) { array[ phone ] = phone ; } int search( int phone ) { if ( array[ phone ] == 0 ) return 0 ; else return 1 ; }
しかしこの方法では、0778621111 といった番号も考えると、 巨大なメモリを必要として、非現実的となる。 この際の解決法がハッシュ法で、データ件数が少なければ、 例えば電話番号の末尾2桁がおなじデータの発生する確率は低い。 もし、電話番号末尾2桁が同じでも、その番号の次の場所に保存するなどすればよい。
#define SIZE 100 // ハッシュ表のサイズ int hash[ SIZE ] ; // ハッシュ表 int hash_func( int phone ) { // ハッシュ関数 return phone % SIZE ; } void entry( int phone ) { int idx = hash_func( phone ) ; while( hash[ idx ] != 0 ) // データ件数100で無限ループだけど... idx = (idx + 1) % SIZE ; hash[ idx ] = phone ; } int search( int phone ) { int idx = hash_func( phone ) ; while( hash[ idx ] != 0 ) { if ( hash[ idx ] == phone ) return 1 ; idx = (idx + 1) % SIZE ; } return 0 ; }
チェイン法
オープンアドレス法では、次の空き場所を探して保存するが、 表サイズよりもデータ件数を多く保存することはできない。
表が埋まって、同じハッシュ値のデータがでてきたら、同じハッシュ値同士を リスト構造で保存する方法はチェイン法と呼ばれる。
struct List *hash[ SIZE ] ; // ポインタはNULLで全て初期化 void entry( int phone ) { int idx = hash_func( phone ) ; hash[ idx ] = cons( phone , hash[ idx ] ) ; } int search( int phone ) { int idx = hash_func( phone ) ; struct List* p ; for( p = hash[ idx ] ; p != NULL ; p = p->next ) { if ( p->data == phone ) return 1 ; } return 0 ; }
認証評価を前にWebサーバダウン
自宅にてくつろいでいるなか、 11/7,20:18に、学校の www.fukui-nct.ac.jp のサーバが 動いていないとの警告メールが携帯に届く。 nagios3 での監視状況からすると、学内の他のサーバは どれも稼働中で、落ちているのはメインサーバだけっぽい。
ただ、明日の認証評価の実地審査では、学内グループウェアや エビデンス系のデータサーバなどが使われる。 んで、さすがにこれらのサーバは動作監視の対象外だし、 これが落ちていたら朝から学校中、冷や汗仕事になるのは必至。 しかたなく学校に出向いて状況確認。
まずは自室で確認すると、メインWebサーバ以外安定稼働中。 情報処理センターの内藤さんも駆けつけてくれたので、 サーバ室にて状況を確認すると、WebサーバのUPSが、 ピーピー鳴って出力停止でフリーズしてた。 壊れた状況は、また明日確認ということで、電源直結にして 帰宅となった。
はぁ。
ワードアライメントとビットフィールド
先週の構造体の演習が1週2コマでは不足っぽいので、前半座学+後半演習とした。
ワードアライメント
struct FOO { char a[ 6 ] ; int b ; // sizeof( data ) は (6+4)*10=100byte? } data[ 10 ] ; // 実際は、(6+2+4)*10=120byte。
このような構造体を作ったら、構造体1件あたりのメモリの使用量は何バイトであろうか? 6+4の10byteと思うかもしれないけど、32bitコンピュータなどであれば、12byte になるのが一般的。
これは、CPUとメモリのデータの速度を比べると、メモリの速度の方が遅い。 このため、CPUがメモリとデータのやり取りをする時は、1ワード(32bit)など まとめて読み書きをするのが一般的。
pack状態 隙間あり +|0|1|2|3| +|0|1|2|3| 00|a|a|a|a| 00|a|a|a|a| 04|a|a|b|b| 04|a|a|x|x| packされた状態では、 08|b|b|a|a| 08|b|b|b|b| data[0].bは、6,7,8,9番地になる。 12|a|a|a|a| 12|a|a|a|a| するとワード単位の読み出しでは、 16|b|b|b|b| 16|a|a|x|x| 04行と08行の2回に分けて読まれる。
構造体のchar a[6]とint bが隙間なく配置されると、ワード単位の読み書きでは メモリの読み出し回数が増えて、機械語の実行速度が遅くなる。 このため、構造体ではデータがワード単位の区切り(ワードアライメント) をまたがないように、データ間に隙間を入れるのが一般的。
ビットフィールド導入
struct Birthday { int year ; int month ; int day ; } ;
このような構造体を考えると、1件あたり4×3=12byteを要する。 しかし、年は西暦であれば2047までであれば、11bit で十分であり、 monthは0〜11までの4bitで十分。dayであれば1〜31の5bitで十分。 西暦を4095までの12bitとしても、21bitで1ワードに収まるデータであり、 最初の誕生日構造体はメモリ上無駄がある。
また、この誕生日データでは、年齢の比較をする際にyear,month,dayの比較を 要し、処理的にも煩雑となる。この様な時は、10進数の桁を使って、 以下の様なテクニックもよく使われる。
int ymd( int y , int m , int d ) { return y * 10000 + m * 100 + d ; // 1999年7月14日は、19990714 } int month_ymd( int ymd ) { return (ymd / 100) % 100 ; // 19990714から7を取り出す }
しかし、この方法では、合成や分解で100といった2進数的に切りの悪い、 乗除算計算の遅い組込系のCPUでは処理が遅くなる。 それに、月を0〜99までの数値として扱いメモリもちょっと無駄。 そこで2進数を使って、year=12bit,month=4bit,day=5bitで扱う方法であれば、 以下のようになるであろう。
int ymd( int y , int m , int d ) { return (y << 9) | (m << 5) | d ; } int month_ymd( int ymd ) { // YYYYYYYYYYYYMMMMDDDDDから return (ymd >> 5) & 0xF ; // MMMMを取り出す。 }
Facebookの福井高専ページ
Facebookにて、福井高専の名前が変な人に使われないようにと、 "福井工業高等専門学校"のページを作ってある。 さらに"いいね!"をしてもらえるように、高専の速報記事が 自動的に掲載するようにしてある。
この状態で約半年近く運用をしてきて、それなりに認知されている状態。 ということで、管理者パネルでみれる情報の一部をキャプチャで公開。
VMware ESXiのインストール
次期学科サーバは、余力のある多機能な仮想サーバ上で動かそうと、 VMware ESXi(free)を入れてみた。ゲストOSに、DebianとWindows Server2008 を入れてひとまずお試し。
ただ、インストールの際は、自室のMac環境では、Mac用の vSphere client が無い。そこで、Parallels Desktopで Windows 7を動かしてその中で、 Windows用 vSphere client を動かしてインストール。
あまりにも仮想環境のためにCPU無駄遣いとは思いながらも、 vSphere client 越しに、仮想サーバのWindows Serverが普通に立ち上がる。 ここまで来たら、 無駄の極みの処理速度を実感してみようと、 ゲストOSのDebianとWindowsの間を、 リモートデスクトップでつないで動かしてみた。
それでも、遅いと思いながらも使える速度で Firefox が立ち上がった。
(Mac OS X Snow Leopard) - Parallels Desktop for Mac (Windows 7 Ultimated) - vSpere client for Windows (VMware ESXi 5.1) (Windows Server 2008r2) - Remote Desktop for Windows (Debian 6.0/testing) XRDP - gnome / Firefox
2EI工場見学で松浦機械を訪問
松浦機械さんでは、大型のNC機械の設計や製造している所を見学させて頂いた。さすがに世界的にも先端の機械を作っているだけあって、個人的にも興味深く見学となった。
興味深かったキーワードとして、物づくり・ものづくり・モノづくり。 高専では「ものづくり」をよく使うけど、松浦機械さんは 「精度のあるものを作るには、ソフトウェア (プログラムの意味もあるけど、品質工学的な人というソフトも含むのかな…) があって作れるもの…という意味で"モノづくり"と記載しています」 といった説明であった。 見学した大型機械をみても機械企業というイメージが強いけど、 実際にはそれを制御するためのソフトウェア開発が重要であり、 情報分野の採用も多いようでした。 また、品質工学を機械のためのソフトウェアにも積極的に取り入れている。 この際のキーワードが、1:10:100の原則。 設計段階のバグにかかるコストが1ならば、工場内でみつかるバグは10倍のコスト。 お客さんの所でバグが見つかれば、クレーム対応や信用問題も含め100倍のコストが かかる。だから、早い段階でバグを見つけるための体制が重要とのことであった。
あと、会社では英語力は大事。 でも技術的な話をするのは専門用語を並べれは、度胸と積極性でなんとかカバーできる。 しかし5時以降の英語力は、その後の外国のお客さん(あるいは現地スタッフ)との繋がりの為にはさらに大切とのことであった。(^_^;
SQLの説明その2と課題用資料
SQLの命令の説明 part2 と、残り時間は課題のための演習時間とする。
SQL説明2
SQLの説明の残りということで、WHERE節でかける特殊な条件や、 副問い合わせなどについて説明する。
値 IN 集合 例:SELECT S.業者名, S.所在 FROM S WHERE S.業者番号 IN (SELECT SG.業者番号 FROM SG WHERE SG.商品番号 = 'G2' AND SG.在庫量 >= 200) ; 文字列 LIKE パターン 例: SELECT S.業者名 FROM S WHERE S.業者名 LIKE 'AB%' ; ワイルドカード文字: % 任意の文字 , _ 1文字 値 BETWEEN 値 AND 値 例: SELECT G.価格 FROM G G.価格 BETWEEN 100 AND 200 ;
上記の IN の例に示すように、SQLの中の() に、別のSQL命令を記述できる。 この場合は、()の中のSQLを予め実行し、その結果の中に S.業者番号 が含まれれば、 WHERE節が成立する。このような処理が副問い合わせである。
実際には、もう少し複雑な"相関副問い合わせ"というものもあり、
SELECT G.商品名, G.色, G.価格 FROM G WHERE 'S4' IN (SELECT SG.業者名 FROM SG WHERE SG.商品番号 = G.商品番号 ) ;
上記の副問い合わせの中には、全体の問い合わせのテーブルGが含まれており、 このような副問い合わせでは、()の中を先に実行しておくことはできない。 このため、Gのテーブルについて1件づつG.商品番号を代入して()の中を実行し、 その結果で WHERE 'S4' IN (…) が成立するかチェックされる。 このような問い合わせは、相関副問い合わせと呼ばれる。
注意:この授業で使う実験環境は、SQLiteであり、相関副問い合わせは使えない。
課題用資料
2分木の応用/意思決定木
今日は、前回の演習時間も不足だと思われるので、前半に座学・後半演習とした。
意思決定木
雑誌の占いや性格判断みたいなページには、質問に対して"yes","no"の回答をしてもらって、 最終的にあなたの性格は!みたいなのがよく書かれている。こういうようなものは、
| 勉強好き? / \ もっと勉強? 部活好き? / \ / \ 大学 技術者 アスリート 心配!
意思決定木と呼ばれる。このデータ構造では、ノードに質問事項が書かれ、 木の末端のノードには、最終的な答えが記載される。 これをプログラムで表すならば、
struct DTree { char *mess ; struct DTree* yes ; struct DTree* no ; } ; struct DTree* dtcons( char* s , struct DTree* y , struct DTree* n ) { struct DTree* ret ; ret = (struct DTree*)malloc( sizeof( struct DTree ) ) ; if ( ret != NULL ) { ret->mess = s ; ret->yes = y ; ret->no = n ; } return ret ; } struct DTree* top = dtcons( "勉強好き?" , dtcons( "もっと勉強?" , dtcons( "大学" , NULL , NULL ) , dtcons( "技術者" , NULL , NULL ) ) , dtcons( "部活好き?" , dtcons( "アスリート" , NULL , NULL ) , dtcons( "心配..." , NULL , NULL ) ) ) ; void main() { char buff[ 100 ] ; struct DTree* p = top ; while( p->yes != NULL || p->no != NULL ) { printf( "%s" , p->mess ) ; scanf( "%s" , buff ) ; if ( strcmp( buff , "yes" ) == 0 ) p = p->yes ; else p = p->no ; } printf( "%s" , p->mess ) ; }
入試説明会
昨日は、入試説明会ということで福井のAOSSA会場で学科の説明を担当しました。他の学科の説明では慣れないのもあって、ちょいとカミカミでしたが、興味を持って頂けたのであれば、ぜひとも福井高専においでませ〜!!