ホーム » 2024 (ページ 2)
年別アーカイブ: 2024
ERモデル
データベースの設計
リレーショナル・データベースでは、データは表形式であればなんでも良い訳ではない。
例えば、学生の成績データが以下のような1つのテーブルで構成されるデータだった場合、
|
- 修正不整合: 授業担当が 斉藤 → 高久 のように変更になったら、複数のテーブルを修正しなければならない。大量のレコード修正は、時間がかかるし、その途中にシステムダウンしたらデータの整合性に問題が発生するかも。
- 挿入不整合: 新しい科目 情報ネットワーク を追加したいけど、受講学生が決まらないとデータを挿入できない。
- 削除不整合: 山田 が受講を取りやめたら、科目 情報メディア も消えてしまう。
これらを考慮すると、以下のような3つの表で設計するべきである。
■学生(実体)
|
■受講(関係)
30203を消しても情報メディア |
■科目(実体)
斉藤→高久に変更したら 情報ネットワークを追加しても問題なし |
データベースの設計では、(1)概念設計、(2)論理設計、(3)物理設計が行われる。
- 概念設計:概念スキーマの決定(実体・関係モデルを使う)。上記の受講データベースの設計例
- 論理設計:論理スキーマの決定。関係データベースで実装?ほかのデータベース?
- 物理設計:物理スキーマの決定。データの格納方法や管理方法を決める。
実体関連モデル(ERモデル)
データベース設計では、実体関連モデル(ERモデル:Entity-Relation model)が使われる。 実体とは、モデル化しようとする対象で独立した存在となれるもの。 実体が持つ色々な特性は属性と呼ばれる。 属性の取りうる値の集合を定義域、同一種類の実体の集まりを実体集合と呼ぶ。 関連とは、実体同士の相互関係をモデル化したもの。
実体関連図(ER図)では、実体を長方形、関連をひし形、属性を楕円で表現する。 属性で、キーとなるものには下線をつけて表す。
ER図で調べると、実際にはもっと細かい規定で表現が行われている。 参考:IDEF1X表記とIE表記
専攻科実験・コンパイラと関数電卓プログラム作成
- コンパイラの技術と関数電卓プログラム(1)
- 再帰下降パーサによる構文解析(LL法)による電卓プログラム作成
- 補助資料:コンパイラの技術と関数電卓プログラム(1-2)
- 課題
- 複数桁の数字が使えること。
- 式中に空白が使えること。
- 何らかの演算子を追加すること。
- (例) %,単項演算子のマイナスなど
- 演算子が左結合か右結合か確認すること。
- オプション課題
- 変数が使えること。
(変数名は1文字のA-Zといったもので良い)
- 変数が使えること。
- レポート内容
- コンパイラ技術の概要、課題(1)の説明・最終的なBNF記法・ソース・動作検証、考察
- 前半レポート提出先
- コンパイラの技術と関数電卓プログラム(2)
- コンパイラツールを使ったLR構文解析による電卓プログラムの作成
- 補助資料:専攻科実験: unix系 開発環境のインストール
- 課題
- 基本的に、lex+yaccで(1)と同様の課題で参考資料を元に改良を行う。
- レポート内容
- lex,yaccの概要、課題(2)の説明・ソース・動作検証、考察
- 後半レポート提出先
2分木の応用(意思決定木と演算子)
データをO(log N)で検索するための2分探索木以外の2分木のデータ構造について解説を行う。
意思決定木
意思決定木の説明ということで、yes/noクイズの例を示しながら、2分木になっていることを 説明しプログラムを紹介。
((意思決定木の例:小さい子供が発熱した時)) 38.5℃以上の発熱がある? no/ \yes 元気がある? むねがひいひい? yes/ \no no/ \yes 様子をみる 氷枕で病院 解熱剤で病院 速攻で病院
このような判断を行うための情報は、yesの木 と noの木の2つの枝を持つデータである。これは2分木と同じである。左右に枝のあるものは質問であり、yesの枝もnoの枝もない末端は最終決断を表す。このようなデータ構造は意思決定木と呼ばれ、質問と決断の処理は以下のように記述ができる。
import java.util.* ; import java.util.Scanner ; class TreeNode { String qa ; TreeNode yes ; TreeNode no ; TreeNode( String s , TreeNode y , TreeNode n ) { this.qa = s ; this.yes = y ; this.no = n ; } } ; public class Main { public static void main(String[] args) throws Exception { Scanner sc = new Scanner( System.in ) ; TreeNode top = new TreeNode( "38.5℃以上の発熱がある?" , new TreeNode( "胸がひぃひぃ?" , new TreeNode( "速攻で病院" , null , null ) , new TreeNode( "解熱剤で病院" , null , null ) ) , new TreeNode( "元気がある?" , new TreeNode( "様子をみる", null , null ) , new TreeNode( "氷枕で病院", null , null ) ) ) ; TreeNode p = top ; while( p.yes != null || p.no != null ) { System.out.println( "Question:" + p.qa ) ; if ( sc.nextInt() == 1 ) { System.out.println( "yes" ) ; p = p.yes ; } else { System.out.println( "no" ) ; p = p.no ; } } System.out.println( "Answer:" + p.qa ) ; } }
- 意思決定木(Paiza.io)
struct Tree { char *qa ; struct Tree* yes ; struct Tree* no ; } ; struct Tree* dtree( char *s , struct Tree* l , struct Tree* r ) { struct Tree* n ; n = (struct Tree*)malloc( sizeof( struct Tree ) ) ; if ( n != NULL ) { n->qa = s ; n->yes = l ; n->no = r ; } return n ; } void main() { struct Tree* p = dtree( "38.5℃以上の発熱がある?" , dtree( "胸がひぃひぃ?" , dtree( "速攻で病院", NULL,NULL ) , dtree( "解熱剤で病院",NULL,NULL ) ) , dtree( "元気がある?" , dtree( "様子をみる", NULL,NULL ) , dtree( "氷枕で病院", NULL,NULL ) ) ) ; // 決定木をたどる struct Tree* d = p ; while( d->yes != NULL || d->no != NULL ) { printf( "%s¥n" , d->qa ) ; scanf( "%d" , &ans ) ; // 回答に応じてyes/noの枝に進む。 if ( ans == 1 ) // yesを選択 d = d->yes ; else if ( ans == 0 ) // noを選択 d = d->no ; } // 最終決定を表示 printf( "%s¥n" , d->qa ) ; }
2分木の応用として次週以降に式の表現の説明を行うけど、その前に計算式の一般論の説明を行う。
逆ポーランド記法
一般的に 1*2 + 3*4 と記載すると、数学的には演算子の優先順位を考慮して、(1*2)+(3*4) のように乗算を先に行う。このような優先順位を表現する時に、()を使わない方法として、逆ポーランド記法がある。
演算子の書き方には、前置記法、中置記法、後置記法があり、後置記法は、「2と3を掛ける、それに1を加える」と捉えると、日本語の処理と似ている。
中置記法 1+2*3 前置記法 +,1,*,2,3 後置記法 1,2,3,*,+ # 1と「2と3をかけた値」をたす。
後置記法は、一般的に逆ポーランド記法(Reverse Polish Notation)とも呼ばれ、式をコンピュータで実行する際の処理と似ている。
演算子の右結合・左結合
例えば、”1/2*3″という式が与えられたとする。この結果は、1/6だろうか?3/2だろうか?
一般的な数学では、優先順位が同じ演算子が並んだ場合、左側から計算を行う。つまり”1/2*3″は、”(1/2)*3″を意味する。こういった左側の優先順位が高い演算子は左結合の演算子という。
ただしC言語やJavaでは、”a = b = c = 0″ と書くと、”a = (b = (c = 0))” として扱われる。こういった代入演算子は、 右結合の演算子である。
理解度確認
以下の式を指定された書き方で表現せよ。
逆ポーランド記法 1,2,*,3,4,*,+ を中置記法で表現せよ。 中置記法 (1+2)*3-4*5 を逆ポーランド記法で表現せよ。
以前の情報処理技術者試験では、スタックの概念の理解の例題として、逆ポーランド記法への変換アルゴリズムのプログラム作成が出題されることが多かったが、最近は出題されることはなくなってきた。
ネットワーク層とIPアドレス
ネットワーク層とIPアドレス(IPv4)
(前回の復習)
サブネットに分割し、隣接するサブネット、さらには上流のインターネットと通信をするためには、IPアドレスを用いた通信が行われる。
ネットワークに接続する機器には、それぞれユニークな32bitの番号(IPv4アドレス)を割り振る。
コンピュータへのIPアドレスの設定には、(a)IPアドレス,(b)サブネットマスク,(c)ゲートウェイの情報が必要となる。
- IPアドレス: 192.156.145.100 といった、0~255の8bitの値をピリオド区切りで4つ並べて表記するのが一般的。
- サブネットマスク: 255.255.255.0 といった値で、IPアドレスを2進数で書き並べた32bitと、サブネットマスクの32bitで、2進数の論理積をとった値は、ネットワーク番号と呼ばれ、機器が存在する場所を表す。
また、IPアドレスとサブネットマスクの否定と論理積をとった値は、ホスト番号と呼ばれる。
サブネットマスクは、先行する1のbit数で書き表すことも多い。255.255.255.0は、”/24″のように書く。 - ゲートウェイ: 自分自身のネットワーク番号と通信相手のネットワーク番号が異なる場合は、異なるサブネットにいるので、パケットを中継してもらう機器(ルータ,ゲートウェイ)にパケットを送る。
- IPアドレスとクラス: IPアドレスは、先頭8bit をネットワーク番号とするクラスA,16bitのクラスB,24bitのクラスCに分類されている。以前は、IPアドレスを割り当てる企業規模に応じて、大規模な大学だからクラスA、中規模ならクラスB(福井大学は133.7.0.0/16 ←このような書き方はCIDR記法という)、小規模ならクラスCを割り当てていた。(福井高専はCクラスを5本192.156.145~149.0/24 : 福井高専のIPアドレスでは3つのCIDR記法で表現できる。 192.156.145.0/24, 192.156.146.0/23, 192.156.148.0/23)
しかし、最近では IPv4 アドレスの不足から、大きな組織に割り振られた クラスA を再分配しているため、先頭が0~126で始まるアドレスでも大きなネットワーク組織とは限らない。
ユニキャスト、マルチキャスト、ブロードキャスト通信
通信では、機器が1対1でデータを送る(ユニキャスト通信)だけでなく、複数の機器に同時にデータを送りたいこともある。
同じサブネット内の全てにデータを送る時には、ブロードキャスト通信が用いられる。この際には、IPアドレスのサブネットにおけるホスト番号部分が全て1のアドレスを使う。Cクラスの 192.168.11.x/24 のネットワークであれば、192.168.11.255 がブロードキャスト通信用のアドレスとなる。一般的にホスト番号部分が全て0のアドレスは、サブネット全体を表すために使われることから、192.168.11.0 のIPアドレスも通常の通信には使えない。このため、Cクラス(サブネットマスク24bit)であれば、256台-2台(全て0と全て1のホスト番号)を引いた254台の機器が設置できる。(実際には、他のネットワークに接続するためのゲートウェイアドレスも必要なので 253台)
動画などの配信では、ユニキャスト通信が使われると、サーバから通信相手までのルータまでに同じデータが送られてくる。こういった方式では大量の視聴者がいる場合、ネットワークの帯域が埋まってしまい、効率の良い配信ができない。同じデータを同じタイミングで複数の機器に配信したい場合はマルチキャスト通信を使う。IPv4では 224.0.0.0/4 を使う。
ARP(IPアドレスとMACアドレスの橋渡し)
同じサブネットの中では、データリンク層でMACアドレスを用いて通信相手を指定するが、ネットワーク層ではIPアドレスを用いて通信相手を指定する。この違いを埋めるためのプロトコルがARPである。
サブネット内に相手先IPアドレスの指定されたパケット(10.10.22.102)が届くと、通信機器はサブネット内の全ての機器相手に ARPリクエストを送信する。(10.10.22.102はいますか?ブロードキャスト通信を使う)
この時、10.10.22.102 のコンピュータは、自分宛てのパケットがあることを知るので、送信元のコンピュータに、自分のMACアドレスを付けたARPリプライを送り返す。(10.10.22.102は、私 “FE:DC:BA:98:76:54” です!)。送信元は、ARP通信をへらすために、その情報を記憶して、2度目以降は覚えたMACアドレスですぐに通信を始める。
ルータとRIP
ルータは、隣接するサブネットの間に入る機器で、各サブネットにゲートウェイとなるインタフェースを持つ。ルータの内部では受け取ったパケットをどこに流すか…という経路情報が重要となる。
- 静的ルーティング – 末端のルータの設定で、管理者が経路を設定
- 動的ルーティング – 上流ルータでRIPにより設定
経路設定には2種類あり、(a)末端のルータではこのネットワーク番号はどのルータに送るという情報をルータに直接設定する静的ルーティングがとられる。(b)上流のルータでは、末端のルータの設定が変更されることがあるので、ルータ同士がルーティング情報を送りあう動的ルーティングがとられる。動的ルーティングでは、RIPというプロトコルにより、各サブネットのつながっている経路情報が送られてくる。この経路情報を見て、パケットのIPアドレスを見て、パケットの送り先を判断する。(ネットワークの上流では、複数のネットワーク経路を操る BGP などが使われる)
((Windows の場合))
C:> ipconfig インタフェース名: IPv4アドレス............192.168.xx.xx サブネットマスク.........255.255.255.0 デフォルトゲートウェイ....192.168.xx.1 C:> arp -a インタフェース: 192.168.xx.xx 74-03-xx-xx-xx-xx 動的 192.168.xx.yy B0-05-xx-xx-xx-xx 動的 C:> netstat -r ネットワーク宛先 ネットマスク ゲートウェイ インタフェース メトリック 0.0.0.0 0.0.0.0 192.168.xx.1 192.168.xx.xx 45 192.168.xx.0 255.255.255.0 ....
((Unix の場合))
$ ifconfig -a # もしくは ip addr en1: .... inet 192.168.xx.xx netmask 0xffffff00 ... $ arp -an # もしくは ip neigh .... (192.168.xx.xx) at 74:03:xx:xx:xx:xx ... .... (192.168.xx.yy) at b0:05:xx:xx:xx:xx ... $ netstat -rn # もしくは ip route Destination Gateway ... default 192.168.xx.1 ... 192.168.xx ...
プライベートアドレス
IPv4 では、32bit でコンピュータを識別することから、最大でも 232台≒40億台(4×103×3台)しか識別できない。実際、IPアドレスの管理団体では、2017年度には IPv4 アドレスは使い切った状態となっている。この対応として、その組織やその家庭内だけで使われる IPアドレス である、プライベートアドレスが用いられる。
- 10.0.0.0~10.255.255.255 / 8 – 大きな機関向け
- 172.16.0.0~172.31.255.255 / 12
- 192.168.0.0~192.168.255.255 /16 – 個人向け
プライベートアドレスを利用する組織では、インターネットに接続するルータでは NAT(もしくはNAPT) という機能を内蔵し、プライベートアドレスとグローバルアドレスの変換を行う。
ローカルループバックアドレス
ネットワーク通信では、1つのコンピュータの中でもプロセスとプロセスの間でデータ通信を行うことも多い。この時に使われるIPアドレスは、ローカルループバックアドレスと呼ばれ、IPv4では 127.0.0.1 のIPアドレスを使う。このアドレスには localhost というドメイン名が割り振られる。
IPv6アドレス
IPv4の32bit の IP アドレスでは、40億台のコンピュータを区別することしかできない。そこで、最近では 128bit の IPv6 アドレスが用いられる(256×102412台≒340×1036台)。 IPv6 では、2004:6800:4004:0826:0000:0000:0000:2003 (www.google.co.jp) といった16進数4桁(16bit)を8個を”:”区切りで書き連ねる書き方をする。ただし16進4桁の先行する0や、全部”0000″ は省略して、”2404:6800:4004:826::2003″ と書く。IPv6 は最近では普及がかなり進んでいるが、途中のルータなどがすべて IPv6 に対応する必要があり IPv4 しか使えない組織も多い。
IPv6でも、同じサブネット内だけで使えるIPv6リンクローカルアドレス(fe80::/10)や、同じ組織内だけで使えるIPv6サイトローカルアドレス(fec0::/10)、ループバックアドレス(::1)などが規定されている。
DNS と nslookup
IPアドレスみたいな数字の羅列は覚えることが難しい。このためコンピュータの名前からIPアドレスを調べるサービスがあり、Domain Name Service(DNS) と呼ばれる。詳しい仕組みは ドメイン名の説明の際に行うが、IP アドレスの調べ方を説明する。
ドメイン名から、IP アドレスを調べるには、nslookup (もしくは dig) を使用する。
$ nslookup www.fukui-nct.ac.jp : Non-authoritative answer: Name: www.fukui-nct.ac.jp Address: 104.215.53.205 $ nslookup -query=A www.fukui-nct.ac.jp # IPv4 アドレスの取得 (同上) # もしくは dig www.fukui-nct.ac.jp A +short $ nslookup -query=AAAA www.google.co.jp # IPv6 アドレスの取得 : # もしくは dig www.google.co.jp AAAA +short Non-authoritative answer: Name: www.google.co.jp Address: 2404:6800:4004:826::2003
理解確認
- Formsによる理解度確認問題
- 192.168.11.2/24 のコンピュータから、192.168.1.50にデータを送る場合、どのような処理が行われるか、IPアドレス、サブネットマスク、ゲートウェイ、ネットワーク番号を使って説明せよ。
- 同じサブネット内で相手のIPアドレスが与えられた時、どのようにパケットが送られるか、MACアドレスとARPを交えて説明せよ。
GROUP BY HAVINGとビューテーブル
GROUP BY HAVING
GROUP BY-HAVING では、指定されたカラムについて同じ値を持つレコードがグループ化される。SELECT 文に指定される集約関数は、グループごとに適用される。HAVING は、ある条件を満たす特定のグループを選択するための条件で、WHERE と違い、集約関数が使える。
SELECT SG.商品番号, SUM(SG.在庫量) FROM SG GROUP BY SG.商品番号 HAVING SUM(SG.在庫量) >= 500 ;
このSQLを実行すると、SG のテーブルから、商品番号が同じものだけをあつめてグループ化される。そのグループごとに在庫量のデータの合計SUMを集約し、合計が500以上のデータが出力される。
|
⇒ |
|
⇒ |
|
CREATE VIEW
今までで述べてきたSQLでは、実際のテーブルを対象に、結合・選択・射影を行う命令であり、これは概念スキーマと呼ばれる、対象となるデータベース全体を理解したプログラマによって扱われる。
しかし、プログラムの分業化を行い、例えば結果の表示だけを行うプログラマにしてみれば、全てのデータベースの表を考えながらプログラムを作るのは面倒である。そこで、結合・選択・射影の演算の結果で、わかりやすい単純な表となったものであれば、初心者のデータベースプログラマでも簡単に結果を扱うことができる。このような外部スキーマを構成するための機能が、ビューテーブルである。
-- 優良業者テーブルを作る -- CREATE VIEW 優良業者 ( 業者番号 , 優良度 , 所在 ) AS SELECT S.業者番号, S.優良度, S.所在 FROM S WHERE S.優良度 >= 15 ; -- 優良業者テーブルから情報を探す -- SELECT * FROM 優良業者 WHERE 優良業者.所在 = '福井' ; -- 文房具データベース -- CREATE VIEW 文房具データベース ( 業者名 , 商品名 , 在庫量 ) AS SELECT S.業者名, G.商品名, SG.在庫量 FROM S, SG, G WHERE S.業者番号 = SG.業者番号 and SG.商品番号 = G.商品番号 ; -- VIEWの確認 -- SELECT * FROM 文房具データベース ; -- 串刺ししたデータベースに見える ----- 実行結果 ----- 業者名 商品名 在庫量 ABC社 赤鉛筆 300 ABC社 ノート 200 ABC社 消しゴム 400 ABC社 消しゴム 200 ABC社 筆箱 100 ABC社 バインダ 100 LMN社 赤鉛筆 300 LMN社 ノート 400 PQR社 ノート 200 STU社 ノート 200 STU社 消しゴム 300 STU社 筆箱 400
ビューテーブルに対する SQL を実行すると、システムによっては予め実行しておいた CREATE VIEW の AS 以下の SQL の実行結果をキャッシュしておいて処理を行うかもしれない。システムによっては SQL の命令を 副クエリを組合せた SQL に変換し、処理を行うかもしれない。しかし、応用プログラマであれば、その SQL がどのように実行されるかは意識する必要はほとんど無いであろう。
ただし、ビューテーブルに対する 挿入・更新・削除といった演算を行うと、データによっては不整合が発生することもあるので注意が必要である。
SQL言語
教科書の流れに沿ってSQLの言語について、再掲
- スキーマ定義
- CREATE – 実テーブル、ビューテーブルの定義
- GRANT – 権限の定義
- スキーマ操作
- DROP – 実テーブル、ビューテーブルの削除
- REVOKE – 権限の削除
- ALTER – テーブルの変更
- ADD – カラムの追加
- データ操作
- SELECT, INSERT, DELETE, UPDATE – レコードの検索、追加・削除・更新
- トランザクション処理
- データベースでは、原子性などを満たすためにデータベースへの更新履歴を保持している。これらの更新履歴をデータベースに反映させ確定する処理がトランザクション処理。
- COMMIT – データベースの更新処理を確定
- ROLLBACK – データベースの更新処理を取り消す
ホスト言語とのインタフェースとSQLインジェクション
プログラミング言語によっては、その言語の中でSQLを使うために「組み込み型のSQL」が使えるものがある。
(COBOL,PL/Iなど)
動的メモリ管理が得意な最近のPythonやPHPなどの言語であれば、データベース参照の関数が利用できる。
SQLインジェクション
例えば、PHPでは、SQLからデータを取り出す処理は、以下のようになる。
// 検索するユーザID $id = "t-saitoh" ; $pdo = new PDO( '...' ) ; // データベースに接続する関数 $sql = "select name from usertable where id='$id'" ; $query = $pdo->prepare( $sql ) ; // 取り出せたデータに関する処理 id がプライマリキーならforeachは1回ループのはず foreach( $query->fetcAll() as $name ) { // $name に取り出した名前が入っている }
しかし、$id の部分を、Web の入力フォームなどの値であれば、名前以外の情報が入力される場合もある。
この際に、「 $id = ” ‘ or 1==1 — ‘ ” 」といった値が入っていた場合、SQLで実行される命令は、
$id = "' or 1==1 --'" の場合 $sql = "select name from usertable where id='' or 1==1 -- ''" ;
となってしまい、本来なら1人のデータを抽出する select 命令が、全テーブルに対して該当してしまい、情報漏洩が発生するかもしれない。
「 $id = “‘; drop usertable ; — ‘” 」であれば、usertable が消されてしまい、システムが動かなくなる(サービスを提供できなくする攻撃 = DoS攻撃 – Denial-of-service attack)ことも考えられる。
他にもSQLインジェクションを使う技がある。こちらの CTF の WriteUp などが参考になる。
こういった攻撃手法は、SQLに本来の意図ではないSQL命令を紛れ込ませる攻撃ということで、SQLインジェクションという。
SQLインジェクションで発生した有名な事件では、以下のようなものがある。
- Yahoo! BB 顧客情報漏洩事件 – 100億円を超える被害
- PlayStation Network個人情報流出事件
対策としては、ユーザが入力したデータを用いて SQL 命令を実行する場合は、ユーザ入力をSQLとして悪用されないように、シングルクオートなどをエスケープするなどの処理が必要となる。さまざまな手法があるので、SQL無効化の専用関数を用いるべき。
また、データベースシステムは、ネットワーク経由でSQLによる処理を行うが、データベースサーバ自体がインターネットに接続されていて、パスワード攻撃によりデータベース本体に不正アクセスが行われる場合もある。一般的なデータベースを用いたシステムは、フロントエンドのWebサーバ、スレーブDBサーバ、マスタDBサーバの三層構成をとることが多いが、バックエンドのデータベースは、インターネットから隔離しフロントエンドのWebサーバのみ接続できるようにするのが一般的である。
データベースに接続する場合はパスワードにより利用者を限定することができるが、データベースシステム自体がインターネットに接続されていると、パスワード総当たり攻撃(ブルートフォース攻撃)や、パスワードスプレー攻撃(総当たり攻撃は、短時間でパスワード失敗が多発するのでシステムで接続拒否するのが一般的。これを回避するために時間をかけて総当たり攻撃をする手法)により、情報漏洩が発生する。
2分探索木への追加とAVLと2分ヒープ
2分探索木にデータを追加
前回の2分探索木では、基本的な説明ということで、木の生成では直接木構造を生成していた。しかし、本来であれば、扱うデータに応じて木を生成することが求められる。
Javaの場合
以下の Javaのプログラムの関数 entry( … ) では、再帰を使いながら、value を追加した木を返す関数で記述した。
import java.util.*; class BTreeNode { int data ; BTreeNode left ; BTreeNode right ; BTreeNode( int x , BTreeNode l , BTreeNode r ) { this.data = x ; this.left = l ; this.right = r ; } } public class Main { public static void print_tree( BTreeNode p ) { // 木構造をわかりやすくするために()をつけて表示 if ( p != null ) { System.out.print( "(" ) ; print_tree( p.left ) ; System.out.print( p.data ) ; print_tree( p.right ) ; System.out.print( ")" ) ; } } public static BTreeNode entry( BTreeNode p , int value ) { if ( p == null ) { return new BTreeNode( value , null , null ) ; } else { if ( p.data == value ) ; // 何もしない else if ( p.data > value ) p.left = entry( p.left , value ) ; else p.right = entry( p.right , value ) ; return p ; } } public static void main(String[] args) throws Exception { BTreeNode top = null ; top = entry( top , 42 ) ; top = entry( top , 27 ) ; top = entry( top , 72 ) ; top = entry( top , 13 ) ; top = entry( top , 38 ) ; top = entry( top , 64 ) ; top = entry( top , 81 ) ; print_tree( top ) ; System.out.println() ; } } // 動作結果 // (((13)27(38))42((64)72(81)))
- 2分探索木へのデータの追加(Paiza.io)
しかしながらこのプログラムでは、以下のように小さい順序でデータを与えると、右に伸びる木が作られてしまう。
// 不均一な木ができる場合 public static void main(String[] args) throws Exception { BTreeNode top = null ; top = entry( top , 13 ) ; top = entry( top , 27 ) ; top = entry( top , 38 ) ; top = entry( top , 42 ) ; top = entry( top , 64 ) ; top = entry( top , 72 ) ; top = entry( top , 81 ) ; print_tree( top ) ; System.out.println() ; } // 動作結果 // (13(27(38(42(64(72(81)))))))
このような不均一な木が生成されてしまうと、本来であればデータ探索の処理時間は O( log N ) であるが、最悪の状態では O( N ) となってしまう。
AVL木
こういった偏った木が生成されてしまった時には、以下のような処理を加えると、不均一な状態を改善できる。
depth() 関数は与えられた木の深さを求める関数であり、左枝と右枝が大きく違う場所で 木の右回転を行う rotate_right() を実行する。
public class Main { public static int depth( BTreeNode p ) { // 木の深さを求める if ( p == null ) { return 0 ; } else { int l = depth( p.left ) ; int r = depth( p.right ) ; if ( l > r ) return 1 + l ; else return 1 + r ; } } public static BTreeNode rotate_right( BTreeNode p ) { BTreeNode q = p.left ; // p BTreeNode r = q.right ; // / \ q.right = p ; // q p.left = r ; // / \ return q ; // r } public static void main(String[] args) throws Exception { BTreeNode top = null ; top = entry( top , 86 ) ; top = entry( top , 53 ) ; top = entry( top , 92 ) ; top = entry( top , 11 ) ; top = entry( top , 65 ) ; top = entry( top , 22 ) ; // 不均一な状態を確認 print_tree( top ) ; System.out.println() ; System.out.println( "p.left=" + depth( top.left ) ) ; System.out.println( "p.right=" + depth( top.right ) ) ; // top の右回転を実行 top = rotate_right( top ) ; // 改善された状態を確認 print_tree( top ) ; System.out.println() ; System.out.println( "p.left=" + depth( top.left ) ) ; System.out.println( "p.right=" + depth( top.right ) ) ; } }
- AVL木と右回転(Paiza.io)
上記のプログラムでは、左枝が3段で右枝が1段であり不均一な状態となっている。
そこで、最上段の86の左枝を上に移動させるように枝の繋ぎ方を rotate_right() により変更している。実際には、繋ぎ変えを左回転させる処理も作る必要がある。
このように、木の枝の深さが不均一な場所で、このような処理を加えて均一化を図った木構造は AVL 木と呼ぶ。
AVL木は、考案した Velski と Landis の名前が由来(Adelson-Velskii and Landis’ tree)
2分ヒープ(binary heap)
2分探索木では、1つのノードにつき2つのポインタを持ち、データ1件あたりのメモリの使用量が多い。通常の「配列の先頭から昇順にデータを並べる2分探索法」では、途中にデータを挿入する場合、データを後ろにずらす必要があるため、O(N)の処理時間を要する。
これらの問題の解決法の1つとして、2分ヒープがある。左右に均一に成長している2分探索木で、上から番号を以下の様に振ると、i番目のデータの左の枝は 2×i+1 番目、右の枝は 2×i+2 番目であることが判る。
このような順序で配列にデータを保存する方法が2分ヒープである。この方式ならアルゴリズムの説明は省略するが、入れるべきノードの場所でのデータの入れ替えで実現できるため O( log( N ) )で挿入が可能となる。
import java.util.*; public class Main { public static int find_heap( int[] array , int idx , int key ) { while( idx < array.length ) { if ( array[ idx ] == key ) return idx ; // 見つかった else if ( array[ idx ] > key ) idx = idx*2 + 1 ; else idx = idx*2 + 2 ; } return -1 ; // 見つからなかった } public static void main(String[] args) throws Exception { int[] a = { 53 , 11 , 86 , 10 , 22 , 65 , 92 } ; System.out.println( find_heap( a , 0 , 22 ) ) ; } }
- 2分ヒープ(Paiza.io)
レポート課題
以下のようなデータを扱う2分探索木のプログラムを作成せよ。以下の箇条書き番号の中から、(出席番号 % 3+1)のデータについてプログラムを作ること。
- 名前(name)と電話番号(phone)
- 名前(name)と誕生日(year,mon,day)
- 名前(name)とメールアドレス(mail)
プログラムは以下の機能を持つこと。
- 1行1件でデータを入力し、2分木に追加できること。
- 全データを昇順(or降順)で表示できること。
- 検索条件を入力し、目的のデータを探せること。
レポートでは、(a)プログラムリスト,(b)その説明,(c)動作検証結果,(d)考察 を記載すること。
(2年後期実験) 簡単ゲーム作成
1.目的
簡単なゲーム作成を通して、遊びながらプログラミングすることで、スキルアップを目指す。
2.概要
簡単なゲームとして、以下の3つのゲームの中から1つを選び、リンク先の JavaScript による基本機能だけのゲームに、ルール判定・勝敗判定などの機能を組み込む。以下のリンク先の五目並べ、オセロゲームのプログラムは、2人で交互にコマをうつX,Y座標を入力しながら、コマの結果(や勝敗の結果)が表示されるものを作成する。
- 五目並べ(連珠)
- 5つ並べば勝ち…が五目並べ。これだけのルールだと、先手が有利なので先手の黒には、長連、四四、三三など禁じ手などが決められているのが連珠
- オセロゲーム
- 自分のコマで挟んだ相手の駒を裏返しで自分のコマにできる。ベースとなったリバーシを、日本のゲーム研究家の長谷川さんが改良したものがオセロゲーム。ルールが単純だけど、最後に大逆転となることも多い。
- ポーカー
- ポーカーはプレーヤーにトランプのカードを5枚づつ配り、その後にカードを入れ替えた後に、カードの役で勝敗が決まる。カジノゲームとして遊ぶ際には、役の完成度に応じて賭け(ベッド)たり、降りたりしながら遊ぶ。ゲームのやり方により、細かくルールが違ったりローカルルールを設ける場合もある。
プログラム作成の腕試しとしての難易度であれば、ポーカー(すべての役判定をするなら) > オセロ ≒ 連珠 > 五目並べ だと思われる。
もし、この3つのテーマの難易度が面白くないのであれば、三目並べでもよい。ただし人間対コンピュータであること。
3.実験方法
- 概要で示した五目並べ,オセロ,ポーカーのルールについて調べ、自分で2週の実験時間の中で、改良できそうなテーマを最初に1つ選んで下さい。
- 選んだテーマについて、概要で示したリンク先の html ファイルを自分のパソコンに保存し、エディタにてプログラム中の JavaScript の内容を理解してください。
- 以下のチェックシートのファイルをダウンロードし、シートのタブの中から自分が取り組むテーマを選んで、2週の実験の中で実装できそうな機能について、「作成の予定」の列に”〇”を入力してください。
- 実験1週目でプログラムを作成したら、実験時間の最後に、実験1週目で完成した機能について、チェックシートの「1週目」の列に”〇”を入力してください。
- 実験2周目では、前回の続きでプログラムを完成にむけて作成してください。2周目の実験時間の最後に、チェックシートの「2週目・自己判定」の欄に”〇”(一部未完成なら”△”,未完成なら”×”)を入力してください。
- 自己判定の記入が終わったら、同じ実験グループの1人と交代して、相手のプログラムの完成度をチェックし、相手のチェックシートの「2周目・他者判定」の欄に”〇”,”△”,”×”を入力してください。また、他の人のチェックシートに記入する際には、チェックシート表の下の「チェック担当」の欄に名前を記入しておいてください。
4.結果
- 出来上がったプログラムを動かして、作成した機能がちゃんと動作しているということが分かる実行例をいくつか画面のキャプチャ機能を使って「実験結果」として記録してください。
- 実験方法での2週目・他者判定において、(自分では動くと思っていたのに)”△”や”×”の判定のあったものについては、その実行例も「実験結果」として記録してください。
- また、他の人に他者判定をしてもらった自分のチェックシートも「実験結果」として保存してください。
5.レポート
実験レポートでは、以下の内容について記載してください。
- 目的
- 概要
- 自分が作成したテーマのルールなどを簡単にまとめる。
- プログラムの説明
- 自分のプログラムのフローチャートなどを作成し(全体の詳細なフローチャートでなくてもよい)、簡単に全体像を説明してください。
- 自分で工夫した所については、詳しく説明してください。
(例えばオセロなら裏返し処理の詳細なフローチャートなどを記載するなど。)
- 実験結果
- 実験結果として、プログラムリスト、チェックシート、動作の解る実例などをつけてください。
- 実験結果として、プログラムリストや動作例などの図をレポート内に入れる場合は、「プログラムリスト1」「図2動作例」といったように、リスト番号、図番号などをつけてレポート内で引用すること。
- 考察
- 自分で作ったプログラムでうまく動いていない所があれば、その原因やどのようにすれば直せるかを考え、その内容を説明してください。(必ずしもプログラムを直す必要はない)
- 動作で問題が見つからなかった場合は、さらにゲームを完全なものとするにはどういう改良が期待できるか、説明してください。
WiFi接続、ネットワーク層
無線LANと暗号化
無線LAN(通称 WiFi)は、IEEE 802.11 にて規格が定められている。無線LANは、使う通信周波数で、2.4GHz帯を使うものと、最近増えてきた5GHz帯のものに分けられる。
-
- IEEE802.11a 5GHz帯を使う、最大54Mbps
- IEEE802.11b 2.4GHz帯を使う、最大11Mbps
- IEEE802.11g 2.4GHz帯を使う、最大54Mbps
- IEEE802.11n 2.4GHz/5GHzを使う、最大600Mbps
- IEEE802.11ac 5GHz帯を使う、最大6.9GBps – 11a/g/n/ac を組み合わせで現時点の主流
- IEEE802.11ad 60GHz 最大6.8Gbps
- IEEE802.11ax 2.4GHz/5GHz 最大 9.6Gbps (通称 WiFi 6)
- IEEE802.11be 2.5GHz/5GHz/6GHz 最大 46Gbps (WiFi 7)
2.4GHz帯は、電子レンジで使う電波の周波数と重なるため、電波干渉を受けやすい。また、2.4GHzは様々な家電製品・電子機器で利用されているため、他の機器との干渉を受けやすく速度低下を起しやすいが、遠くまで電波が届きやすい。 5GHzは、この周波数帯を利用している機器が少ない為、干渉を受けにくく安定して通信できるが、あまり遠くには電波が届かず、通信が極端に不安定になる場合がある。
無線LANに接続する場合には、接続先(アクセスポイント)に付けられた名前(SSID)と、SSIDに割り振られたパスワードが必要となる。ただし無線は、電波で信号を飛ばすため、近くに行くだけで通信を傍受できる。このため、データの暗号化が必須となる。この暗号化は、そのアルゴリズムにより解読の困難さが変わる。
- WEP 64bit / 128bit – すでに古い暗号化で専用ソフトを使うとすぐに解読される可能性が高い。使うべきではない。
- WPA/WPA2 –
現時点の主流?- 暗号化方式 TKIP や 暗号化アルゴリズム AES
- WPA3 – そろそろこちらが主流かな…
- 192bit暗号化, 辞書攻撃に強い SAE を使用
無線LANでは、車でセキュリティの甘いアクセスポイント(暗号化無しやWEPを使うAP)を探し、その無線LANを使ってクラッキングなどをおこなう場合も多い。(ウォードライビング)
勝手に無線LANを使われないようにするために一般的には、(1)アクセスポイントに接続できる機器をMACアドレス(機器に割り当てられた48bitの固有値)で制限したり、(2)SSIDのステルス化(APが出す電波にSSIDを入れない方式)を行う場合も多い。ただし、これらの制限をかけても専用の機器を使えば通信は傍受可能。
移動通信システム
WiFiは屋内にアクセスポイントを設置し十数メートル範囲の通信を行うが、屋外の基地局と端末の間での数百メートルから数キロといった距離の通信を行う場合には「移動通信システム」が用いられる。
- 第1世代移動通信システム – アナログ方式 1980年代
- 第2世代移動通信システム – デジタル方式 1990年代 (GSM,CDMA,PDC)
- 第3世代移動通信システム – モバイルインターネットの時代 2000年代 (数Mbps) W-CDMA, CDMA2000
- 2024年現在 Docomo のみサービスを提供しているが、FOMA/iモードは2026/3/31に終了予定
- 第4世代移動通信システム – 高速大容量 2010年代 (数十Mbps-数百Mbps) LTE
- 現時点では山間部は4G、主要市町では5Gが利用可能 (参考:auのカバーマップ)
- 第5世代移動通信システム – 超高速大容量 2020年代 (最大20Gbps) 通称 5G
- ローカル5G – 通信事業者ではなく、企業や自治体などが自社の敷地や特定のエリア内に構築する専用の5Gネットワーク(国指定の無線局免許取得が必要)
移動体通信での 5G の話をすると、無線LANの話から、5G = 5GHz と勘違いするかもしれないが、5G = 第5世代移動通信システム(5th Generation) なので注意。
ネットワーク層
前回説明したMACアドレスによるデータリンク層では、1つのサブネットの中で指定した相手にデータを送ることはできる。しかし、データリンク層だけでは、他のサブネットにいる相手にデータを送ることができない。(相手の名前を知っていても、住所を知らなければ郵便は送れない。)
ネットワーク層とIPアドレス(IPv4)
サブネットに分割し、隣接するサブネット、さらには上流のインターネットと通信をするためには、IPアドレスを用いた通信が行われる。
ネットワークに接続する機器には、それぞれユニークな32bitの番号(IPv4アドレス)を割り振る。
コンピュータへのIPアドレスの設定には、(a)IPアドレス,(b)サブネットマスク,(c)ゲートウェイの情報が必要となる。
- IPアドレス: 192.156.145.100 といった、0~255の8bitの値をピリオド区切りで4つ並べて表記するのが一般的。
- サブネットマスク: 255.255.255.0 といった値で、IPアドレスを2進数で書き並べた32bitと、サブネットマスクの32bitで、2進数の論理積をとった値は、ネットワーク番号と呼ばれ、機器が存在する場所を表す。
また、IPアドレスとサブネットマスクの否定と論理積をとった値は、ホスト番号と呼ばれる。
サブネットマスクは、先行する1のbit数で書き表すことも多い。255.255.255.0は、”/24″のように書く。 - ゲートウェイ: 自分自身のネットワーク番号と通信相手のネットワーク番号が異なる場合は、異なるサブネットにいるので、パケットを中継してもらう機器(ルータ,ゲートウェイ)にパケットを送る。
- IPアドレスとクラス: IPアドレスは、先頭8bit をネットワーク番号とするクラスA,16bitのクラスB,24bitのクラスCに分類されている。以前は、IPアドレスを割り当てる企業規模に応じて、大規模な大学だからクラスA、中規模ならクラスB(福井大学は133.7.0.0/16 ←このような書き方はCIDR記法という)、小規模ならクラスCを割り当てていた。(福井高専はCクラスを5本192.156.145~149.0/24 : 福井高専のIPアドレスでは3つのCIDR記法で表現できる。 192.156.145.0/24, 192.156.146.0/23, 192.156.148.0/23)
しかし、最近では IPv4 アドレスの不足から、大きな組織に割り振られた クラスA を再分配しているため、先頭が0~126で始まるアドレスでも大きなネットワーク組織とは限らない。
CTF問題とセキュリティの実験でWebサーバを使う場合
CTF問題とセキュリティ実験で、Webサーバを必要とする自作問題を作る時は、nitfcei.mydns.jp のサーバを使えます。
- nitfcei.mydns.jp に guestXX のIDでログイン(情報メディア工学(前期)資料を参考)
- public_html 配下のフォルダに xxxx.php などのファイルを作成
- http://nitfcei.mydns.jp/~guestXX/xxxx.php にブラウザでアクセス
- 最初に、Webの認証画面が出たら、別途アナウンスの ID, PW でログインしてください。
- https は使えないので要注意
- https://tsaitoh.net/~t-saitoh/ctf/ に設置してあるファイルと同じものが、
nitfcei.mydns.jp の /home0/t-saitoh/public_html/ctf/ のフォルダに置いてあります。
必要に応じてコピーや改造をしながら自作問題を作ってみてください。
かにロボコン車体の試走
ジュニアドクターの実験でつくった「かにロボコン」の車体。
最初の実験では、光センサーが低すぎて、ライン用の黒のガムテープに引っかかって動きが遅かった。
センサーを床に擦れない高さにあげ、左右の光センサーの間隔を狭くしてみた。
最初の配置だと、黒テープ幅とほぼ同じ幅に配置したため、白黒・黒白の状態が多発し、片側のモータが止まるタイミングが多いため、動きも遅かった。左右センサーの幅を、テープ幅より狭くしたら黒黒状態で直進の頻度がふえ、走る速度が上がってる。
ゴールに辿り着くまでは動くようになったので、ボール回収ゾーンにたどり着いた際の動きをさせるために、サーボモータの腕を追加。ボール回収ゾーンでの動きを取り込んで試走。
10 A = IN() & 6 : PRINT A 左右センサーの一括入力 20 IF A = 6 THEN OUT `100001 黒黒で直進 30 IF A = 4 THEN OUT `100000 黒白で左折 40 IF A = 2 THEN OUT `000001 白黒で右折 50 IF A = 0 THEN GOTO 100 白白でゴール到着 60 GOTO 10 繰り返し 100 END ボール回収ゾーンでの処理
しかしながら、走っている途中でコースギリギリだと、光センサーがゴールにたどり着いた(白白状態)と途中で判断することが発生する。この辺は、白白状態が連続で発生するかなどを判断させるような改良が必要。