ホーム » 2024

年別アーカイブ: 2024

2024年10月
 12345
6789101112
13141516171819
20212223242526
2728293031  

検索・リンク

2分探索木の導入

配列やリスト構造のデータの中から、目的となるデータを探す場合、配列であれば2分探索法が用いられる。これにより、配列の中からデータを探す処理は、O(log N)となる。(ただし事前にデータが昇順に並んでいる必要あり)

// 2分探索法
import java.util.*;

public class Main {
    public static int bin_search( int[] a , int key , int L , int R ) {
        // Lは探す範囲の左端
        // Rは探す範囲の右端+1(注意!!)
        while( R > L ) {
            int m = (L + R) / 2 ;
            if ( a[m] == key )
                return m ;
            else if ( a[m] > key )
                R = m ;
            else
                L = m + 1 ;
        }
        return -1 ;
    }
    public static void main(String[] args) throws Exception {
        int[] array = {
            11, 13, 27, 38, 42, 64, 72, 81
        } ;
        System.out.println( bin_search( array , 64 , 0 , array.length ) ) ;
        System.out.println( bin_search( array , 14 , 0 , array.length ) ) ;
    }
}
// 2分探索法
int array[ 8 ] = { 11, 13 , 27, 38, 42, 64, 72 , 81 } ;

int bin_search( int a[] , int key , int L , int R ) {
   // Lは、範囲の左端
   // Rは、範囲の右端+1 (注意!!)
   while( R > L ) {
      int m = (L + R) / 2 ;
      if ( a[m] == key )
         return m ;
      else if ( a[m] > key )
         R = m ;
      else
         L = m + 1 ;
   }
   return -1 ; // 見つからなかった
}

void main() {
   printf( "%d¥n" , bin_search( array , 0 , 8 ) ) ;
}

一方、リスト構造ではデータ列の真ん中のデータを取り出すには、先頭からアクセスするしかないのでO(N)の処理時間がかかり、極めて効率が悪い。リスト構造のようにデータの追加が簡単な特徴をもったまま、もっとデータを高速に探すことはできないものか?

2分探索木

ここで、データを探すための効率の良い方法として、2分探索木(2分木)がある。以下の木のデータでは、分離する部分に1つのデータと、左の枝(下図赤)と右の枝(下図青)がある。

この枝の特徴は何だろうか?この枝では、中央のデータ例えば42の左の枝には、42未満の数字の枝葉が繋がっている。同じように、右の枝には、42より大きな数字の枝葉が繋がっている。この構造であれば、64を探したいなら、42より大きい→右の枝、72より小さい→左の枝、64が見つかった…と、いう風にデータを探すことができる。

特徴としては、1回の比較毎にデータ件数は、(N-1)/2件に減っていく。よって、この方法であれば、O(log N)での検索が可能となる。これを2分探索木とよぶ。

第35回・高専プロコン奈良大会

10/19,20とプロコン奈良大会に参加です。 自由部門、競技部門に2チームで本選に挑みます。
{CAPTION}

EthernetとCSMA/CD方式

CSMA/CD方式

10BASE/5,2のような Ethernet では、1本の線を共有するバス型であり、複数の機器が同時に信号を出力すると、電圧の高低がおかしい状態となる(衝突,コリジョン)ため、同時に信号を出さない工夫が必要となる。ただし、他の人が信号線を使っていないことを確認してから、信号を出せばいいけど、確認から信号を出すまでの遅延により、衝突を避けるのは難しい。

また、1本の線を共有する機器の数が増えてくると、衝突の発生の可能性が高まってくる。

これらの問題を解決するためのルールが CSMA/CD(Carrier Sense Multiple Access with Collision Detection)方式である。

  • 機器は、信号を出す場合、信号線が空いている状態を待ち、出力を行う。
  • もし、複数の機器が同時に信号を出した場合、電圧異常を検知したら衝突なので再送を試みる。
  • 再送を行う場合には、乱数時間待つ。(機器が多い場合は、これでも衝突が起こるかもしれない)
  • 乱数時間待っても信号線が空かない場合は、乱数時間の単位時間を倍にする。

どちらにしろ、バス共有する機器の台数が増えてくると、衝突の可能性は高まり、100台を越えるような状態は通信効率も悪くなる。

ただし、最近はスイッチングHUBで通信制御を行うことが一般的になり、CSMA/CD方式ではなくフロー制御が行われる。フロー制御では、通信相手に一時的にデータを送らないように要求を出すことでパケット送受信を制御する方式。HUBのフロー制御では、基本的に衝突は起らないが、複数のネットワークケーブルから同時にHUBにデータが送られてくると、処理ができない場合がある。この際には CSMA/CD 方式での JAM 信号を使うことで衝突が発生しているように扱う。

スイッチングHUB

*BASE-T のような、HUB による接続では、複数の機器が異なる機器どうしで通信をする場合、その通信路を時分割多重するのではなく、通信相手に応じて内部回路を直接つながるように接続するスイッチングHUB(以下SW-HUB)が普及している。

ストア&フォワード方式では、フレーム(パケット)を全部読み込んで、宛先情報を確認してからパケットを送り出す方式。しかし、パケットを全部読み込んだ後の転送による遅延が問題となる。カットスルー方式は、フレーム先頭にあるMACアドレス(6byte)を読み込んで宛先が分かったらすぐにフレームを流す方式で遅延が少ない。しかしフレームにエラーが発生していてもそのままフレームを流してしまう欠点がある。この両者の利点を合わせた方式であるフラグメントフリー方式では、衝突を検出するために必要なフレームの先頭64byteを読み込んだ時点でフレームを流す方式。

バス型通信では、1本の線を共有するため、同じネットワーク内の別機器間の通信は、傍受することができる(タッピング)。しかし、SW-HUB の場合、機器同士が直接つながるので、傍受するのが困難であり、セキュリティ的にも望ましい。

SW-HUBでは、ネットワークケーブルがループ状になると、データ転送が無限に繰り返されるようになる(ブロードキャストストーム)ため、ネットワーク全体がダウンする。ブロードキャストフレームとは、新しくネットワークに入った機器が自分のMACアドレスを周囲の機器に伝えるためのパケット。ループが発生するとブロードキャストフレームが無限に繰り返し流れてしまう。

データリンク層とMACアドレス

前述のように、1つのバス型接続のネットワーク内部には、同時に設置できる機器の数には限界がある。このため、小さなネットワークに分割したもの(サブネット)を、ブリッジやルータで接続し、隣接するサブネットにサブネット内の通信情報が出ないように分割することを行う。

Ethernetに接続する機器は、機器ごとにユニークな番号(MACアドレス)を持っている。このMACアドレスは、8bit✕6個の48bitの値で、メーカー毎に割振られた範囲の値を、機器ごとに異なる値がついている。

Windows であれば、コマンドプロンプト(cmd.exe)を開き、ipconfig /allを実行するとMACアドレスを確認できる。

しかし、公衆WiFiサービスを提供する企業が、個人のネットワーク機器のMACアドレス情報を使ってユーザに合わせた広告を出すなどの使い方を始めている。しかしこれが悪用されると、企業が個人の居場所を特定できるなどの問題が出てきた。このため最近はこういった問題を防ぐためにプライベートMACアドレス/ローカルアドレスが使われるようになっており、機器ごとに異なるプライベートMACアドレスを設定したり、接続する度にデタラメなプライベートMACアドレスを設定する方法がある。

通信は、一般的に1500byte程のパケットを単位として送受信が行われる。サブネット内の通信では、自分宛のパケットかどうかをMACアドレスを見て受け取る。これらのレイヤーは、データリンク層と呼ばれる。

旧式のHUB(Dumb HUB)は、電気的に信号を増幅するだけなので、物理層(レイヤー1)だけで通信を行う。
スイッチングHUBは、MACアドレスを見て通信相手を判断(データリンク層/レイヤー2)する。このため SW-HUB の内部には「土のポートの先にどんな MAC アドレスの機器が接続されているのか」を保存するアドレステーブルを持っている。最近の家庭用の SW-HUB であれば 2000 個ぐらいの情報を覚えることができる。また最近では、SW-HUBのコネクタ毎に、パケットにタグを付加することで、1本のネットワーク経路に仮想的な複数のネットワークを構築するタグV-LANといった方式を使う場合もある。このような機能を持つSW-HUBは特にレイヤ2スイッチとも呼ばれる。

L2スイッチとL3スイッチ

サブネットに分割し、それぞれに異なるネットワーク番号を割り振り、中継するルータで FireWall を機能させることで、セキュリティを高めることが行われる。しかし、性能の高いスイッチングHUBは高価でもあり、1つのHUBに異なるネットワークを接続する必要がでてくることもある。この場合、IPアドレスを異なるネットワークの番号を偽装されると、データが盗み見られるかもしれない。

こういった相互に分離すべきネットワークであっても、柔軟なネットワーク構成とするためには、VLAN機能を持った L2スイッチ(レイヤ2スイッチングHUB) が使われる。タグVLAN機能付きのL2スイッチでは、特定のポートにVLANのタグ番号を割り当て、ポートに入る時にパケットに VLAN のタグ情報を付加し、そのパケットは同じ VLAN のタグをもつポートからしかデータを取り出せない。

L2スイッチ(レイヤ2スイッチ)は、機器のMACアドレスやパケットに付けられたVLANのタグ番号の情報(レイヤ2=データリンク層)でパケットの流れを制御している(下記OSI参照モデルの表を参照)。最近では、許可されていない機器がネットワークに侵入する不正侵入を防ぐために、登録されていないMACアドレスのパケットを通さないといった機能がある。

OSI参照モデルとレイヤ
第7層 アプリケーション層 アプリケーションの種類の規定
第6層 プレゼンテーション層 データフォーマットの交換
第5層 セッション層 コネクションの確立や切断などの管理
第4層 トランスポート層 パケットの分割合成や再送といった管理(TCP)
第3層 ネットワーク層 隣接するネットワーク間の通信(IPアドレス)
第2層 データリンク層 直接接続された機器間の通信(MACアドレス)
第1層 物理層 物理的な接続方法(コネクタや電圧など)

スイッチングHUBの中には、レイヤ3(IPアドレス)の情報でパケットの流れを制御するものもある。こういったスイッチは、L3スイッチ(レイヤ3スイッチ)と呼ばれるが、機能的にはルータと同じである。

一般的には、LANとWANを接続するための機器はルータ、LAN内部のネットワークを分離するためのものはL3スイッチと呼ぶ。

無線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
  • IEEE802.11ad 60GHz 最大6.8Gbps
  • IEEE802.11ax 2.4GHz/5GHz 最大 9.6Gbps

2.4GHz帯は、電子レンジで使う電波の周波数と重なるため、電波干渉を受けやすい。5GHz帯は、障害物の影響を受けやすい。
2.4GHzは様々な家電製品・電子機器で利用されているため、他の機器との干渉を受けやすく速度低下を起しやすいが、遠くまで電波が届きやすい。 5GHzは、この周波数帯を利用している機器が少ない為、干渉を受けにくく安定して通信できるが、あまり遠くには電波が届かず、通信が極端に不安定になる場合がある。

無線LANに接続する場合には、接続先(アクセスポイント)に付けられた名前(SSID)と、SSIDに割り振られたパスワードが必要となる。ただし無線は、電波で信号を飛ばすため、近くに行くだけで通信を傍受できる。このため、データの暗号化が必須となる。この暗号化は、そのアルゴリズムにより解読の困難さが変わる。

  • WEP 64bit / 128bit – すでに古い暗号化で専用ソフトを使うとすぐに解読される可能性が高い。使うべきではない。
  • WPA/WPA2 – 現時点の主流。
    • 暗号化方式 TKIP や 暗号化アルゴリズム AES

無線LANでは、車でセキュリティの甘いアクセスポイント(暗号化無しやWEPを使うAP)を探し、その無線LANを使ってクラッキングなどをおこなう場合も多い。(ウォードライビング)

勝手に無線LANを使われないようにするために一般的には、(1)アクセスポイントに接続できる機器をMACアドレス(機器に割り当てられた48bitの固有値)で制限したり、(2)SSIDのステルス化(APが出す電波にSSIDを入れない方式)を行う場合も多い。ただし、これらの制限をかけても専用の機器を使えば通信は傍受可能。

携帯電話を使っていると、最近では高速大容量通信、多数同時接続、低遅延を特徴とする 5G 回線という言葉が出てくる。ここまでの話から、5G = 5GHz と勘違いするかもしれない。また、5G = 5G bps といった勘違いもあるかもしれないけど、5G = 第5世代移動通信システム(5th Generation) なので注意。

SQLの基本

先週の、関係データベースの導入説明を終えて、実際のSQLの説明。

SQLの命令

SQL で使われる命令は、以下のものに分類される。((参考資料))

  • データ定義言語 – CREATE, DROP, ALTER 等
  • データ操作言語 – INSERT, UPDATE, DELETE, SELECT 等
  • データ制御言語 – GRANT, REVOKE 等 (その他トランザクション制御命令など)

データベースは元々商用データの処理に使われることが多かったため、商用計算向けプログラム言語 COBOL と似ている点が多い。COBOL では、計算命令を 英語の文章の様に記述するのが特徴。

一般的な言語  // COBOL
A_100         // A-100     変数名にハイフン(マイナス記号)が使える。Aから100を引くという意味ではない。
A = 100 ;     // MOVE 100 TO A .
A = A + B ;   // ADD A B TO A .

create user

データベースを扱う際の create user 文は、DDL(Data Definition Language)で行う。

CREATE USER ユーザ名
    IDENTIFIED BY "パスワード"

grant

テーブルに対する権限を与える命令。

GRANT システム権限 TO ユーザ名
   データベースシステム全体に関わる権限をユーザに与える。
   (例) GRANT execute ON admin.my_package TO saitoh
GRANT オブジェクト権限 ON オブジェクト名 TO ユーザ名
   作られたテーブルなどのオブジェクトに関する権限を与える。
   (例) GRANT select,update,delete,insert ON admin.my_table TO saitoh
REVOKE オブジェクト権限 ON オブジェクト名 TO ユーザ名
   オブジェクトへの権限を剥奪する。

ただし、後に示す実験環境では、データベースのシステムにSQLiteを用いている。SQLite はネットワーク対応型のデータベースではないため、データベースをアクセスするユーザや権限の概念が存在せず、これらの命令は実行できても無視される。

create table

実際にテーブルを宣言する命令。構造体の宣言みたいなものと捉えると分かりやすい。ただし、作られたテーブルはRDBシステムが永続的に保存しているので、最初に一度実行するだけでよい。逆に、運用が始まったら大量のデータが実際に保存される。この段階でデータベースの設計が悪くてテーブルの内容を変更するのであれば、既存の全データを一旦掃き出し、テーブルを定義しなおし吐き出しておいたデータを再読み込みといった面倒な作業が必要となる。

CREATE TABLE テーブル名
   ( 要素名1  型 , 要素名2 型 ... ) ;
   PRIMARY KEY 制約
   1つの属性でのキーの場合、型の後ろに"PRIMARY KEY"をつける、
   複数属性でキーとなる場合は、要素列の最後に PRIMARY KEY(要素名,...) をつける。
   これによりKEYに指定した物は、重複した値を格納できない。

型には、以下の様なものがある。(Oracle)
   CHAR( size)  : 固定長文字列 / NCHAR国際文字
   VARCHAR2( size ) : 可変長文字列 / NVARCHAR2...
   NUMBER(桁) :指定 桁数を扱える数
   BINARY_FLOAT / BINARY_DOUBLE : 浮動小数点(float / double)
   DATE : 日付(年月日時分秒)
   SQLiteでの型
   INTEGER : int型
   REAL : float/double型
   TEXT : 可変長文字列型
   BLOB : 大きいバイナリデータ

DROP TABLE テーブル名
   テーブルを削除する命令

insert,update,delete

指定したテーブルに新しいデータを登録,更新,削除する命令

INSERT INTO テーブル名 ( 要素名,... ) VALUES ( 値,... ) ;
   要素に対応する値をそれぞれ代入する。
UPDATE テーブル名 SET 要素名=値 WHERE 条件
   指定した条件の列の値を更新する。
DELETE FROM テーブル名 WHERE 条件
   指定した条件の列を削除する。

select

データ問い合わせは、select文を用いる、 select文は、(1)必要なカラムを指定する射影、(2)指定条件にあうレコードを指定する選択、 (3)複数のテーブルの直積を処理する結合から構成される。

SELECT 射影 FROM 結合 WHERE 選択
   (例) SELECT S.業者番号 FROM S WHERE S.優良度 > 30 ;

理解確認

  • キー・プライマリキー・外部キーについて説明せよ。
  • 上記説明中の、科目テーブルにふさわしい create table 文を示せ。
  • select文における、射影,結合,選択について説明せよ。

SQLの演習環境の使い方

SQL の演習は、Paiza.IO で動作確認をしてください。

SQLの基礎/select文と射影・結合・選択

ここまで述べたようにデータベースでは記録されているデータの読み書きは、SQL で行われ、射影・結合・選択を表す処理で構成されることを示した。SQL の機能を理解するために、同じ処理を C 言語で書いたらどうなるのかを示す。

((( 元のSQL )))
SELECT S.業者番号         -- 必要とされるデータを抽出する射影 --
  FROM S                 -- 複数のテーブルを組合せる結合 --
  WHERE S.優良度 >= 20 ;  -- 対象となるデータを選び出す選択 --

((( SQLをC言語で書いたら )))
// 配列の個数を求める #define 文
#define sizeofarray(ARY) (sizeof(ARY) / sizeof(ARY[0]))
// C言語なら... S のデータを構造体宣言で書いてみる。
struct Table_S {
   char 業者番号[ 6 ] ; // 当然、C言語では要素名を
   char 業者名[ 22 ] ;  // 漢字で宣言はできない。
   int  優良度 ;
   char 所在[ 16 ] ;
} S[] = {
   { "S1" , "ABC社" , 20 , "福井" } ,
   :
} ;

// SELECT...をC言語で書いた場合の命令のイメージ
// 結合
for( int i = 0 ; i < sizeofarray( S ) ; i++ ) {
   // 選択
   if ( S[i].優良度 >= 20 )
      // 射影
      printf( "%d¥n" , S[i].業者番号 ) ;
}

Sは、テーブル名であり、文脈上対象テーブルが明らかな場合、フィールド名の前の テーブルは省略可能である。

SELECT 業者番号 FROM S WHERE 優良度 >= 20 ;

WHERE 節で記述できる条件式では、= , <>(not equal) , < , > , <= , >= の比較演算子が使える。
# これ以外の演算機能は、次週にて紹介予定。

直積と結合処理

ここで、SQLの最も便利な機能は、直積による結合処理。複数の表を組み合わせる処理。単純な表形式の関係データベースで、複雑なデータを表現できる基本機能となっている。

SELECT SG.商品番号 , S.所在
  FROM S , SG
  WHERE SG.業者番号 = S.業者番号

上記の様に FROM 節に複数のテーブルを書くと、それぞれのテーブルの直積(要素の全ての組み合わせ)を生成する処理が行われる。この機能が結合となる。しかし、これだけでは意味がないので、通常は外部キーが一致するレコードでのみ処理を行うように、WHERE SG.業者番号 = S.業者番号 のような選択を記載する。最後に、結果として欲しいデータを抽出する射影を記載する。

SELECTの結合処理と処理内容

selectでの選択,結合,射影の処理(select 射影 from 結合 where 選択)を理解するために、同じ処理をC言語で書いたらどうなるかを示す。

// C言語なら
struct Table_S {
   char 業者番号[ 6 ] ;
   char 業者名[ 22 ] ;
   int  優良度 ;
   char 所在[ 16 ] ;
} S[] = {
   { "S1" , "ABC社" , 20 , "福井" } ,
   :
} ;
struct Table_SG {
   char 業者番号[ 6 ] ;
   char 商品番号[ 6 ] ;
   int  在庫量 ;
} = SG[] {
   { "S1" , "G1" , 300 } ,
   :
} ;

// FROM S
for( int i = 0 ; i < sizeofarray( S ) ; i++ ) {
   // FROM SG
   for( int j = 0 ; j < sizeofarray( SG ) ; j++ ) {
      // WHERE S.業者番号 = SG.業者番号
      if ( strcmp( S[i].業者番号 , SG[j].業者番号 ) == 0 ) {
         // SELECT SG.商品番号 , S.所在
         printf( "%s %s¥n" , SG[j].商品番号 , S[i].所在 ) ;
      }
   }
}

(1) i,jの2重forループが、FROM節の結合に相当し、(2) ループ内のif文がWHERE節の選択に相当し、(3) printfの表示内容が射影に相当している。

射影の処理では、データの一部分を抽出することから、1件の抽出レコードが同じになることもある。この際の重複したデータを1つにまとめる場合には、DISTINCT を指定する。

SELECT DISTINCT SG.商品番号, S.所在
   FROM S, SG
   WHERE SG.業者番号 = S.業者番号 ;

上記のプログラムでは、データの検索は単純 for ループで記載しているが、内部で HASH などが使われていると、昇順に処理が行われない場合も多い。出力されるデータの順序を指定したい場合には、ORDER BY … ASC (or DESC) を用いる

SELECT SG.商品番号, S.所在
   FROM S, SG
   WHERE SG.業者番号 = S.業者番号
   ORDER BY S.所在 ASC ;        -- ASC:昇順 , DESC:降順 --

表型のデータと串刺し

FROM に記載する直積のための結合では、2つ以上のテーブルを指定しても良い。

SELECT S.業者名, G.商品名, SG.在庫量
   FROM S, G, SG
   WHERE  S.業者番号 = SG.業者番号  -- 外部キー業者番号の対応付け --
     AND  SG.商品番号 = G.商品番号  -- 外部キー商品番号の対応付け --

// 上記の処理をC言語で書いたら

struct Table_G {
   char 商品番号[ 6 ] ;
   char 商品名[ 22 ] ;
   char 色[ 4 ] ;
   int  価格 ;
   char 所在[ 12 ] ;
} = G[] = {
   { "G1" , "赤鉛筆" , "青" , 120 , "福井" } ,
   :
} ;

// [結合] S,G,SGのすべての組み合わせ
// FROM S -- 結合
for( int i = 0 ; i < sizeofarray( S ) ; i++ ) {
   // FROM G -- 結合
   for( int j = 0 ; j < sizeofarray( G ) ; j++ ) {
      // FROM SG -- 結合
      for( int k = 0 ; k < sizeofarray( SG ) ; k++ ) {

         // [選択] 条件でレコードを選び出す
         //   WHERE S.業者番号 = SG.業者番号
         //     AND SG.商品番号 = G.商品番号
         if ( strcmp( S[i].業者番号 , SG[k].業者番号 ) == 0
           && strcmp( SG[k].商品番号 , G[j].商品番号 ) == 0 ) {
            // [射影] 使用するフィールドを出力
            printf( "%s %s %d\n" ,
                    S[i].業者名 , G[j].商品名 , SG[k].在庫量 ) ;
         }
      }
   }
}

ここで結合と選択で実行している内容は、外部キーである業者番号を S から探す、商品番号を G から探している。この、外部キー対応しているものを探すという視点で、上記 C 言語のプログラムを書き換えると、以下のように表せる。

// FROM SG
for( int k = 0 ; k < sizeofarray( SG ) ; k++ ) {
   // 外部キー SG.業者番号に対応するものを S から探す
   for( int i = 0 ; i < sizeofarray( S ) ; i++ ) {
      if ( strcmp( S[i].業者番号 , SG[k].業者番号 ) == 0 ) {
         // 外部キー SG.商品番号に対応するものを G から探す
         for( int j = 0 ; j < sizeofarray( G ) ; j++ ) {
            if ( strcmp(SG[k].商品番号,G[j].商品番号) == 0 ) {
               printf( "%s %s %d\n" ,
                       S[i].業者名,G[j].商品名,SG[k].在庫量 ) ;
            }
         }
      }
   }
}

このような、複数の表の実体と関係を対応付けた検索を、データベースの専門の人は「データを串刺しにする」という言い方をすることも多い。

また、SQL では、このようなイメージの繰り返し処理を、数行で分かりやすく記述できている。このC言語のプログラム例では、キーに対応するものを単純 for ループで説明しているが、SQL ではプライマリキーなら、B木やハッシュなどを用いた効率の良い検索が自動的に行われる。このため、SQLを扱う応用プログラマは、SQLの結合の処理方法は通常はあまり考えなくて良い。

CTF問題とセキュリティ(4年実験)

この実験では、セキュリティコンテストのCTF問題(Capture The Flag競技)について、インターネットの仕組みを理解し、その問題の解き方を考え、新しく自分自身でCTF問題を作ってもらいます。

日程

実験は、4週にわたり、以下の日程で行います。

内容
1 (前半)暗号・ファイル・Web
2
3 (後半) プログラム作成・インターネット・OS
4

前半・後半でそれぞれ、問題例(もしくは自分で見つけたCTF問題)の1つの説明と自作のCTF問題1つを新しく作り説明をしてください。最終的に4つの問題の説明のレポートを提出となります。

提出物

  • 実験の目的
  • 問題例(or 自分で見つけたCTF問題)を1つ選び
    • 前半・後半それぞれについて
    • その問題が情報セキュリティにどう関係しているのか
    • 問題の解き方のしくみと解説
  • 自作問題について
    • 前半・後半それぞれ
    • その問題が情報セキュリティにどう関係しているのか
    • 問題の解き方、問題の作り方
    • しくみと解説
  • 提出先はこちらのTeams共有フォルダに。
  • 自作問題の例
    • サブリミナル効果で隠されたGIF
    • 電子透かし
    • 学校からアクセスする時だけ、答えが見える。(REMOTE_HOST)
    • スマホでアクセスした時だけ、答えが見える。(HTTP_USER_AGENT)
    • Google 検索した後だけ、答えが見える。(HTTP_REFERER)

Ethernet LANとWAN接続

前回の物理層のLANの話に引き続き、WANの話を説明。

前回の復習

10BASE5, 10BASE2 では、同軸ケーブルにPCが接続。

ITメディアより引用

10BASE5 トランシーバ

10BASE2 とT型分岐コネクタ

10BASE-T

Ethernet と通信速度

10BASE 5/2/-T といった 10BASE は、通信速度の上限が 10Mbps (bit per second) を意味する。100BASE-T といった 100BASE は、100Mbps を意味する。最近では、1000BASE-T は、1000 Mbps = 1Gbps の通信速度となる。最近では、10G BASE-T といった記載であれば、10Gbps を意味する。

バス接続(LAN)と転送速度

基本的な Ethernet の接続では、1本の通信路を共有するバス型接続のため、1本の信号線をパケット単位の通信の短い時間に区切って、送信を交代しながら行う時分割多重方式で行い通信を行う。パケット(イーサネットフレーム)とは、通信データを送る単位で最大1500byteとなっている。(MTU値:Maximum Transmission Unit)

例えば、10BASE のネットワークでつながった4台のパソコンで、A-B間、C-D間で同時に通信を行おうとすると、A-Bの通信中は、通信路が使用中のため、C-D間の通信はできない。このため、A-B間、C-D間の通信をパケットを送る毎に交代しながら通信路を利用する。これにより、見かけ上は A-B, C-D 間が同時に通信しているようにみえる。

    • 10BASE/5の PC-AとPC-Bの間で、音楽CD1枚のデータ(700MB)をを送る場合、通信時間はどの位かかるか?
      • →答え:

        700M[byte] = 5.6G[bit] なので、10M[bit/sec]で送ると、560[sec]

    • 同じく、A-B間、C-D間で同時に送る場合は、通信時間はどのくらいかかるか?
      • →答え:

        短時間的には同時に通信ができないので、通信路を切り替えながら送るため、倍の時間がかかる。よって、1120[sec]

10BASE/T, 100BASE-T, *BASE-T では、HUBの内部構造に注意が必要。基本的には、見かけ上は木構造のように分配しているように見えるけど、内部はバス型の通信路に変わりはない。10BASE/5/2と異なり、データの送信用,受信用の2つの信号のペアとなっている。10BASE/T を利用している頃は、HUBは高価であり単純なバス型接続のHUB(Dumb HUB)であれば、短時間的にみればC-D間通信中はE-F間通信ができない。(前述のようにパケットに小分けして通信するので見かけ上は並行して通信しているように見える)

しかしこれでは、通信速度が無駄になるので、最近はスイッチングHUBが利用される。このHUBは、通信相手に応じてHUB内部の通信路を切り分けるので、A-B間通信中でも、C-D間通信が可能となる。送り先を区別するためには通信機器ごとに固有値が割り振られているMACアドレスを使う。

理解確認

  • 前述の2つのカスケード接続されたDumb HUBで、A,B,C,D,E,Fのコンピュータが繋がっている時、A-C間、B-D間で音楽CD700MBのデータを送る場合、通信時間はどうなる?

電話線接続

同じ敷地内のネットワーク接続のLANどうしを、ネットワークで相互接続するWAN(Wide Area Network)では、昔は電話線を用いていた。電話は、本来音声を伝えるためのものであるため、0/1のデジタル信号を、音の信号に変換(変調)し、受信側は音をデジタル信号に(復調)する。これらを行う機器は、変復調装置(モデム)と呼ばれる。

変調の際には、0/1信号を、音の強弱(振幅変調/AM),音程の高低(周波数変調/FM),位相の前後(位相変調/PM)の組み合わせによって、送受信を行う。参考:ダイヤルアップ接続音(YouTube)

当初は、300bps程度であったが、最終的には64Kbps 程度の通信速度が得られた。(電話線は元々10kHzの音を送るために開発されていたため、64Kbpsが限界だった)

これらの通信速度の改善のため、電話線にデジタル信号で送る ISDN , 電話線の音の信号の高帯域を使った通信 ADSLなどが用いられた。(ADSLでは高周波帯を使うので、自宅から交換機までの距離が長い時は通信速度が速くできなかったりした)

最近では、光ファイバによる FTTH(Fiber To The Home) が広く普及し、1Gbps を越える通信が可能となっている。このほかには、ケーブルテレビ事業者による回線を利用した HFC(Hybrid Fiber-Coaxial) も普及している。HFCでは幹線部分を光ケーブルで接続し、地域の拠点から家庭までは同軸ケーブルで配線することで、テレビ放送とインターネット接続を実現している。

光ファイバ

光ファイバでは、内側(コア)に屈折率の高い透過材料と、外側に屈折率の低い透過材料でケーブルを使い、屈折率の違う断面で全反射することを利用して光を遠くまで運ぶ。中身がガラス繊維なので、中の繊維が折れない工夫や、コネクタで光が減衰しないような工夫が重要。

光ファイバで自宅までネットワーク回線が引かれている場合、光ファイバと自宅内インターネットの間には、ONU(Optical Network Unit/光回線終端装置)が設置されている。一般的にモデムが音の信号を電気的なデジタル信号に変換するのに対し、ONU は光信号をデジタル信号に変換する。音声電話を使う場合には、音声信号をデジタル化して、これをインターネットで通信する VoIP(Voice over IP network) 技術が使われる。

(引用元)

通信速度の理解と、古い時代の通信速度を体験してもらうため、試しに「2000ドット✕1500ドットのRGB画像(1ドット3byte)のデータ(無圧縮)を、9600bps で通信したら、どの程度の時間を要するか、いくらかかるのか?」を計算してみよう。ちなみに2000年頃は、携帯電話では、1Kbyteあたり10円の通信料がかかった。

→答え:

データ量 2000✕1500✕3✕8 [bit] = 72 M[bit]
通信速度 9600[bps] であれば、72 M / 9600 = 7500[sec] = 約2時間(1/5に圧縮されても24分)
通信費  72M[bit]/8/1000 = 9000[Kbyte]、
通信料金 9000[Kbyte]=9000[パケット]、1パケット(1KB)10円だから90,000円 😥
# 画像が320✕240✕RGB(16bit)で圧縮で1/5であれば、それでも100円超え

J-PHONE(J-SH04,200年発売)で始めてカメラ付き携帯が登場。(解像度の低い自撮り写真をスマホで1枚送れば100円かかった時代)

ネットワークトポロジ

ネットワークに機器を接続する形態をネットワークトポロジと言う。

1本の線を共有するバス型、機器どうしがリング型に接続するリング型、中央の機器を通して接続されるスター型が基本となる。

基本的に、Ethernet は 1本の線を機器で共有するバス型。ただし、10BASE-T,100BASE-TX などの HUB で繋がることから、HUB を中心に広がるスター型とも言える。それぞれれのネットワークは相互につながることから、木の枝状に見えるものはツリー型と呼ばれる。また、上流ネットワークでは、機器が故障した場合に一切の通信ができなくなるのは問題があるため、複数のネットワークで相互に接続される。この場合、網が絡むような構造になることから、ネットワーク型と呼ばれる。

データベースの用語など

データベースの機能

データベースを考える時、利用者の視点で分類すると、以下の3つの視点の違いがある。

  1. データベースの管理者(データベース全体の管理)、
  2. 応用プログラマ(SQLなどを使って目的のアプリケーションに合わせた処理を行う)、
  3. エンドユーザ(データベース処理の専門家でなく、DBシステムのGUIを使ってデータベースを操作する)

データベース管理システム(DBMS)では、データとプログラムを分離してプログラムを書けるように、データ操作言語(SQL)で記述する。

また、データは独立して扱えるようにすることで、データへの物理的なアクセス方法があっても、プログラムの変更が不要となるようにする。

データベースは、利用者から頻繁に不定期にアクセスされる。このため、データの一貫性が重要となる。これらを満たすためには、(a) データの正当性の確認、(b) 同時実行制御(排他制御)、(c) 障害回復の機能が重要となる。

これ以外にも、データベースからデータを高速に扱えるためには、検索キーに応じてインデックスファイルを管理してくれる機能や、データベースをネットワーク越しに使える機能などが求められる。

データベースに対する視点

実体のデータをそれぞれの利用者からデータベースを記述したものはスキーマと呼ばれる。そのスキーマも3つに分けられ、これを3層スキーマアーキテクチャと呼ぶ。

  • 外部スキーマ – エンドユーザからどんなデータに見えるのか (create view の例)
  • 概念スキーマ – 応用プログラマからは、どのような表の組み合わせで見えるのか、表の中身はどのようなものなのか。
  • 内部スキーマ – データベース管理者からみて、表の中身は、どのようなファイル名でどのような形式でどう保存されているのか

データモデル

データを表現するモデルには、いくつかのモデルがある。

関係データベースの基礎

関係データベースは、1970年頃に、E.F.コッド博士によりデータベースのための数学的な理論が確立された。

1974 – IBM SEQUEL言語開発, Ingres初期型
1977 – ラリー・エリソンらが Oracle の前進となった SDL 社を創業, 1982に Oracle に改称

  • 集合 A, B – 様々なデータ
  • 直積 AB = { (x,y| xA , yB } 集合A,Bのすべての組み合わせ
  • 関係 R(A,B) すべての組み合わせのうち、関係があるもの。直積A,Bの部分集合

例えば、A={ s,t,u } , B={ p,q } (定義域) なら、

AB = { (s,p) , (s,q) , (t,p) , (t,q) , (u,p) , (u,q) }

このうち、Aが名前(sさん,tさん,uさん)、Bが性別(p=男性,q=女性)を表すなら、

R(A,B) = { (s,p) , (t,q) , (u,p) } (例)
(例):(sさん,男性) , (tさん,女性) , (uさん,男性)

前の例を使うなら、集合<学生>, 集合<科目> であり、直積 <学生> ✕ <科目> のうち、実際に受講したという結果が、関係 R( <学生> , <科目> ) に相当する。

SQLの導入

コッドが提唱した関係データベースの理論に基づいて作った Alpha 言語を元に、IBM が SEQUEL を開発したが、商標の問題で SQL と名前が変更された。同じころにコッドらの論文を元に、ラリー・エリソンらにより Oracle が開発されている。

SQLは、データベース管理システム(RDBMS)において、データの操作や定義を行うためのデータベース言語問い合わせ言語)である。プログラミングにおいてデータベースへのアクセスのために、他のプログラミング言語と併用される。COBOL の影響が大きく英語の文章のような文法となっている。

SQLの機能は、以下の3つに大きく分けられている。

  • データ定義言語(Data Definition Language)
    • CREATE , DROP , ALTER
  • データ操作言語(Data Manipulation Language)
    • INSERT INTO , UPDATE…SET , DELETE FROM , SELECT…FROM…WHERE
  • データ制御言語(Data Control Language)
    • GRANT , REVOKE , COMMIT , ROLLBACK

今回の授業では、Paiza.IO の MySQL 環境を使って演習を行う。

理解確認

  • データベースにおける3層スキーマアーキテクチャについて説明せよ
  • 集合A,Bが与えられた時、関係R(A,B) はどのようなものか、数学定義や実例をあげて説明せよ。

双方向リストとdeque

deque(両端キュー)

双方向循環リストを使うと、(1)先頭にデータを挿入(addFirst/unshift)、(2)先頭のデータを取り出す(removeFirst/shift)、(3)末尾にデータを追加(addLast/push)、(4)末尾のデータを取り出す(removeLast/pop)、といった処理が簡単に記述できる。この4つの処理を使うと、単純リスト構造で説明した、待ち行列(queue)やスタック(stack) が実現できる。この特徴を持つデータ構造は、先頭・末尾の両端を持つ待ち行列ということで、deque (double ended queue) とも呼ばれる。

先頭への挿入/削除

末尾への挿入削除

import java.util.*;

class BDListNode {
    BDListNode prev ;
    int        data ;
    BDListNode next ;
    
    BDListNode( BDListNode p , int d , BDListNode n ) {
        this.prev = p ;
        this.data = d ;
        this.next = n ;
    }
    BDListNode() {
        this.prev = this ;
        this.data = -1 ; // dummy
        this.next = this ;
    }
}

public class Main {
    static void addFirst( BDListNode p , int d ) {
        p.next = new BDListNode( p , d , p.next ) ;
        p.next.next.prev = p.next ;
    }
    static int removeFirst( BDListNode p ) {
        int ans = p.next.data ;
        p.next = p.next.next ;
        p.next.prev = p ;
        return ans ;
    }
    static void addLast( BDListNode p , int d ) {
        p.prev = new BDListNode( p.prev , d , p ) ;
        p.prev.prev.next = p.prev ;
    }
    static int removeLast( BDListNode p ) {
        int ans = p.prev.data ;
        p.prev = p.prev.prev ;
        p.prev.next = p ;
        return ans ;
    }
    public static void main(String[] args) throws Exception {
        BDListNode top = new BDListNode() ;
        // 先頭で Stack
        addFirst( top , 11 ) ;
        addFirst( top , 22 ) ;
        System.out.println( removeFirst( top ) ) ;
        System.out.println( removeFirst( top ) ) ;
        // 末尾で Stack
        addLast( top , 33 ) ;
        addLast( top , 44 ) ;
        System.out.println( removeLast( top ) ) ;
        System.out.println( removeLast( top ) ) ;
        // Queue として使う
        addFirst( top , 55 ) ;
        addFirst( top , 66 ) ;
        System.out.println( removeLast( top ) ) ;
        System.out.println( removeLast( top ) ) ;
    }
}

Javaでの LinkedList(Deque) のクラスの使い方

Javaで Deque のような双方向リスト(2重リスト)を使うには、LinkedList<E> を用いる。クラス宣言などで <E> の内側に型Eを指定する機能はジェネリクスと呼ばれる。クラス名に Deque というのもあるけど、LinkedList などの内部の処理を記述するために使われているものなので、ユーザが直接 Deque クラスを使うことはない。

LinkedList でデータ構造を宣言する場合には、LinkedList<E> のように、どのテータ構造を要素にもつ LinkedList なのかを指定する。ただし int,double といったプリミティブ型を型の欄に書くことはできない。こういった場合には Integer,Double といったオブジェクトを記載すること。

また、リストの要素で繰り返しをする場合には、for( 型 item : LinkedList型 ) {…} のように書けば、繰り返しが記述できる。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        LinkedList<Integer> lst = new LinkedList<Integer>() ;
        lst.addFirst( 11 ) ;  // lst の先頭に11を挿入
        lst.addFirst( 22 ) ;  // lst の先頭に22を挿入
        for( Integer item : lst ) {
            System.out.println( item ) ;
        }
        lst.addLast( 33 ) ;   // lst の末尾に33を追加
        // 全要素の繰り返し
        for( Integer item : lst ) {
            System.out.println( item ) ;
        }
        // 先頭を取り出す
        while( !lst.isEmpty() ) { // isEmpty() : lst の要素が空か判定する
            System.out.println( lst.removeFirst() ) ; // lstの先頭要素を取り出す
        }
    }
}

LinkedList クラスは双方向リストだけど、単純リスト専用のクラスは無い。このため、これまでの授業のように自分でクラスを宣言して使う必要がある。

ArrayList クラス

これと同じように自由な配列サイズのデータクラスとして、ArrayListクラスも存在する。以下の例は、ArrayList<Integer> の使用例。

ArrayList はジェネリクスを使った、要素数を増やすことができる配列である。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> lst = new ArrayList<Integer>() ;
        lst.add( 11 ) ;
        lst.add( 22 ) ;
        for( Integer item : lst ) {
            System.out.println( item ) ;
        }
        lst.add( 33 ) ;
        for( Integer item : lst ) {
            System.out.println( item ) ;
        }
        for( int x = 0 ; x < lst.size() ; x++ ) {
            System.out.println( lst.get( x ) ) ;
        }
    }
}

型推論

上記のプログラムで、LinkedList<Integer> lst = new LinkedList<Integer&gt() ; で宣言しているが、初期化の右辺と左辺で型を揃える必要があるが、複雑な型だとプログラムが煩雑になる。このため、最近の Java には型推論の機能があるため、下の様に型を省略して記述できる。

LinkedList<Integer> lst = new LinkedList<Integer>() ; // 型推論なし
LinkedList<Integer> lst = new LinkedList<>() ;        // 後半のIntegerは明らか
var lst = new LinkedList<Integer>() ; // lst の型は new LinkedList<Integer> で明らか

for( Integer item : lst ) { ... } // 型推論なし
for( var item : lst ) { ... }     // 型推論でIntegerを省略

LinkedListとArrayListの違い

LinkedList や ArrayList は同じ使い方ができるようにadd(int,E),get(int)クラスメソッド等が定義されている。しかし、LinkedList は双方向リスト、ArrayList は配列を使って実装されているので、同じ名前のメソッドでもそのメソッドの処理時間には、以下のような特徴がある。

処理時間のオーダ LinkedList ArrayList メソッドの機能
addFirst(E e)
/
add(E e)
O(1) O(1) リストの末尾に要素を追加
add(int index, E e) 指定した場所探しにO(N)
その場所に挿入O(1)
指定した場所探しにO(1)
その場所に挿入O(N)
リストの指定したインデックスに要素を追加
addFirst(E e) / add(0, E e) 先頭に挿入O(1) 先頭に挿入O(N) リストの先頭に要素を追加
E get(int index) O(N)  O(1) リストのインデックス番目を参照

理解確認


ランダムアクセス・シーケンシャルアクセスから双方向リスト

ランダムアクセスO(1)とシーケンシャルアクセスO(N)の説明を踏まえ、リスト構造のO(N)の改善にむけた解説を行う。

リスト構造の利点と欠点

リストを使った集合演算のように、データを連ねたリストは、単純リストとか線形リストと呼ばれる。特徴はデータ数に応じてメモリを確保する点や、途中へのデータの挿入削除が得意な点があげられる。一方で、配列は想定最大データ件数で宣言してしまうと、実際のデータ数が少ない場合、メモリの無駄も発生する。しかし、想定件数と実データ件数がそれなりに一致していれば、無駄も必要最小限となる。リスト構造では、次のデータへのポインタを必要とすることから、常にポインタ分のメモリは、データにのみ注目すれば無駄となる。

例えば、整数型のデータを最大 MAX 件保存したいけど、実際は それ以下の、平均 N 件扱うとする。この時のメモリの使用量 M は、以下のようになるであろう。(sizeof()はC言語での指定した型のメモリByte数を返す演算子)

配列の場合 リスト構造の場合

(ただしヒープ管理用メモリ使用量は無視)

シーケンシャルアクセス・ランダムアクセス

もう1つのリストの欠点はシーケンシャルアクセス。テープ上に記録された情報を読む場合、後ろのデータを読むには途中データを読み飛ばす必要があり、データ件数に比例したアクセス時間を要する。このような N番目 データ参照に、O(N)の時間を要するものは、シーケンシャルアクセスと呼ばれる。

一方、配列はどの場所であれ、一定時間でデータの参照が可能であり、これは ランダムアクセスと呼ばれる。N番目のアクセス時間がO(1)を要する。配列であれば、N/2 番目のデータをO(1)で簡単に取り出せるから2分探索法が有効だが、リスト構造であれば、N/2番目のデータを取り出すのにO(N)かかってしまう。

このため、プログラム・エディタの文字データの管理などに単純リストを用いた場合、1つ前の行に移動するには、先頭から編集行までの移動で O(N) の時間がかかり、大量の行数の編集では、使いものにならない。ここで、シーケンシャルアクセスでも1つ前にもどるだけでも処理時間を改善してみよう。

単純リストから双方向リストへ

ここまで説明してきた単純リストは、次のデータへのポインタを持つ。ここで、1つ後ろのデータ(N番目からN+1番目)をアクセスするのは簡単だけど、1つ前のデータ(N-1番目)を参照しようと思ったら、先頭から(N-1)番目を辿るしかない。でも、これは O(N) の処理であり時間がかかる処理。
ではどうすればよいのか?

この場合、一つ前のデータの場所を覚えているポインタがあれば良い。

Javaの場合

import java.util.*;

class BDListNode {
    BDListNode prev ;
    int        data ;
    BDListNode next ;
    
    BDListNode( BDListNode p , int d , BDListNode n ) {
        this.prev = p ;
        this.data = d ;
        this.next = n ;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        BDListNode top
            = new BDListNode( null , 1 ,
                 new BDListNode( null , 2 ,
                    new BDListNode( null , 3 , null ) ) ) ;
        top.next.prev = top ;
        top.next.next.prev = top.next ;
        for( BDListNode p = top ; p != null ; p = p.next )
            System.out.println( p.data ) ; // 1,2,3
        for( BDListNode p = top.next.next ; p != null ; p = p.prev )
            System.out.println( p.data ) ; // 3,2,1
    }
}

C言語の場合

// 双方向リストの宣言
struct BD_List {
    struct BD_List* prev ; // 1つ前のデータへのポインタ
    int             data ;
    struct BD_List* next ; // 次のデータへのポインタ
} ;

このデータ構造は、双方向リスト(bi-directional list)と呼ばれる。では、簡単なプログラムを書いてみよう。双方向リストのデータを簡単に生成するための補助関数から書いてみる。

// リスト生成補助関数
struct BD_List* bd_cons( struct BD_List* p ,
                         int d ,
                         struct BD_List* n ) {
    struct BD_List* ans ;
    ans = (struct BD_List*)malloc(
                         sizeof( struct BD_List ) ) ;
    if ( ans != NULL ) {
        ans->prev = p ;
        ans->data = d ;
        ans->next = n ;
    }
    return ans ;
}
void main() {
    struct BD_List* top ;
    struct BD_List* p ;

    // 順方向のポインタでリストを生成
    top = bd_cons( NULL , 1 ,
          bd_cons( NULL , 2 ,
          bd_cons( NULL , 3 , NULL ) ) ) ;
    // 逆方向のポインタを埋める
    top->next->prev = top ;
    top->next->next->prev = top->next ;

    // リストを辿る処理
    for( p = top ; p->next != NULL ; p = p->next )
        printf( "%d\n" , p->data ) ;
    for(         ; p->prev != NULL ; p = p->prev )
        printf( "%d\n" , p->data ) ;
}

双方向リストの関数作成

以上の説明で、双方向の基礎的なプログラムの意味が分かった所で、練習問題。

先のプログラムでは、1,2,3 を要素とするリストを、ナマで記述していた。実際には、どんなデータがくるか分からないし、指定したポインタ p の後ろに、データを1件挿入する処理 bd_insert( p , 値 ) , また、p の後ろのデータを消す処理 bd_delete( p ) を書いてみよう。

Javaの場合

public class Main {
    // 指定したノードの後ろに1件追加
    static void bd_insert( BDListNode p , int d ) {
        p.next = new BDListNode( null , d , p.next ) ;
        p.next.next.prev = p.next ;
        p.next.prev = p ;
    }
    // 指定したノードの後ろを1件削除
    static void bd_delete( BDListNode p ) {
        p.next = p.next.next ;
        p.next.prev = p ;
    }
    public static void main(String[] args) throws Exception {
        // 双方向リストの追加,削除            
        BDListNode top2
            = new BDListNode( null , 1 ,
                 new BDListNode( null , 2 ,
                    new BDListNode( null , 4 , null ) ) ) ;
        top2.next.prev = top2 ;
        top2.next.next.prev = top2.next ;
        
        // 途中に挿入の実験
        bd_insert( top2.next , 3 ) ;
        for( BDListNode p = top2 ; p != null ; p = p.next )
            System.out.println( p.data ) ; // 1,2,3,4
        for( BDListNode p = top2.next.next.next ; p != null ; p = p.prev )
            System.out.println( p.data ) ; // 4,3,2,1
        
        // 途中を削除の実験
        bd_delete( top2.next ) ;
        for( BDListNode p = top2 ; p != null ; p = p.next )
            System.out.println( p.data ) ; // 1,2,4
        for( BDListNode p = top2.next.next ; p != null ; p = p.prev )
            System.out.println( p.data ) ; // 4,2,1
    }
}

C言語の場合

// 双方向リストの指定場所 p の後ろに、値 d を要素とするデータを挿入せよ。
void bd_insert( struct BD_List* p , int d ) {
   struct BD_List*n = bd_cons( p , d , p->next ) ;
   if ( n != NULL ) {
      p->next->prev = n ;
      p->next = n ;
   }
}

// 双方向リストの指定場所 p の後ろのデータを消す処理は?
void bd_delete( struct BD_List* p ) {
   struct BD_List* d = p->next ;
   d->next->prev = p ;
   p->next = d->next ;
   free( d ) ;
}

// この手のリスト処理のプログラムでは、命令の順序が重要となる。
// コツとしては、修正したい箇所の遠くの部分を操作する処理から
// 書いていくと間違いが少ない。

番兵と双方向循環リスト

前述の bd_insert() だが、データの先頭にデータを挿入したい場合は、どう呼び出せば良いだろうか?
bd_insert() で、末尾にデータを挿入する処理は、正しく動くだろうか?

同じく、bd_delete() だが、データの先頭のデータを消したい場合は、どう呼び出せば良いだろうか?
また、データを消す場合、最後の1件のデータが消えて、データが0件になる場合、bd_delete() は正しく動くだろうか?

こういった問題が発生した場合、データが先頭・末尾で思ったように動かない時、0件になる場合に動かない時、特別処理でプログラムを書くことは、プログラムを読みづらくしてしまう。そこで、一般的には 循環リストの時にも紹介したが、番兵(Sentinel) を置くことが多い。

しかし、先頭用の番兵、末尾用の番兵を2つ用意するぐらいなら、循環リストにした方が便利となる。このような双方向リストでの循環した構造は、双方向循環リスト(bi-directional ring list)と呼ばれる。

情報ネットワーク基礎・ガイダンス

情報ネットワーク基礎では、インターネットがどのような仕組みなのか、どのようにして動いているのかを説明する。TCP/IPって何? IPアドレスって何? セキュリティって何?

あなたが使っているネットワーク機能は?

まずは、Teams にて「あなたが使っているネットワーク機能は?」を返信にて書き込んでください。
ただしネットワークを使うことでコンピュータがどう動いているかの視点で答えてほしい。

共有:ネットワークプリンタ、ファイル共有… – サービスを利用する視点
(ハードウェアや情報を共有)

分散:大量のコンピュータで負荷分散リスク分散… – サービスを提供する視点
(仕事を分散し全体で高速化, 沢山のコンピュータの1台が壊れても全体は動く)

ネットワークの歴史

昔のコンピュータは、開発にお金がかかるため1台のコンピュータを全員で使うもの(TSS: Time Sharing System/時分割システム)だった。冷戦の時代、軍の重要な処理を行うコンピュータでは、コンピュータのある所に核攻撃を加えられ、軍の機能がすべて動かなくなることは問題だった。1970年頃にアメリカ国防総省ARPANETがインターネットの原型(TCP/IP)を作る。

1980年代には、パソコンが同じ組織内でネットワークで繋がるようになるLAN(Local Area Network)が使われるようになる。1990年代には、LANどうしを遠隔地接続をするWAN(Wide Area Network)が発達し、Internet(広域コンピュータネットワーク)という言葉が広く普及していった。欧州原子核研究機構(CERN)で、ティム・バーナーズ=リーWorld Wide Web/httpを開発(1989)。1995年、マイクロソフトの家庭用パソコンのOS Windows95の普及と共にWWWが普及する。

  • 1980年代:パソコン通信
  • 1997年:weblog(blog:自分で作るwebページの拡大)
  • 1998年:Google検索
  • 1999年:2ch
  • 2003年:SNSの誕生(2004:mixi)
  • 2006年:Twitter,Facebook(一般開放)

コンピュータインタフェースとネットワーク(物理層)

ネットワークにおける情報伝達において、伝送媒体(電気信号,光)にて0/1を伝えるための取り決めは、物理層という。まずは、コンピュータと機器の接続について考えると、シリアル通信パラレル通信に分類できる。(シリアル通信は時間を細かく区切って複数の信号を送ることから時分割多重通信と呼ぶこともある)

通信の高速化に伴い、伝送の配線はコンデンサやインダクタンスを考慮したインピーダンスマッチングが重要となる。このため、高速通信のインタフェース両端は終端抵抗(ターミネータ)が必要だった。

1本の信号線で単位時間あたりのデータ通信速度が同じであれば、パラレル通信の方が高速であるが、長い通信路ではノイズ対策が重要でありノイズ対策をきちんとした線が複数本あるとケーブルが太くなることから、長い通信路ではシリアル通信が使われる。少ない信号線に対してノイズ対策をきちんと施すことができるので、長い通信路ではシリアル通信の方が高速となる。

パラレル通信の例:パラレルポート(プリンタ用)IEEE 1284、ハードディスクATA(IDE)、計測器GP-IB

シリアル通信の例:RS-232C、IEEE1394(FireWire)、ハードディスク(SATA)USB1.1, USB2.0, USB3.0, USB3.1 Gen2, USB3.2 Gen2x2…、有線LAN/Ethernet

有線LAN/Ethernetの種別

  • 半二重通信 – 送信/受信を1本の信号線でおこなう。
  • 全二重通信 – 送信用の信号線、受信用の信号線がそれぞれ別。送信受信を同時にできる。
    • 複信 Simplex(一方的な放送), Half Duplex(半二重), Full Duplex(全二重)
  • 10BASE/* – 10Mbit/sec
    • 10BASE/5 – ケーブルに針を刺して増設 – 半二重通信/ターミネータが必要
    • 10BASE/2T型BNCケーブルで延長 – 半二重通信/ターミネータが必要
    • 10BASE/T – HUBで分配(終端抵抗などの問題はHUBが解決してくれる) – 全二重通信
  • 100BASE-T – 100Mbit/sec / CAT5
    • 信号線のノイズ対策は、シールドで覆う、信号線を“より線”(ツイストペア)にするなどの対策が重要
      • シールドや”より線”の方式でカテゴリー CAT5,CAT6,CAT7 などで分類される
  • 1000BASE-T ギガビット – 1000Mbit/sec = 1Gbit/sec / CAT6
  • 10000BASE , 10GBase – 10Gbit/sec / CAT7

同軸ケーブル

ツイストペアケーブル

ツイストペアケーブルがノイズに強い理由

理解確認

  • ネットワークにおける共有と分散について例をあげて説明せよ。
  • TSSのような通信によるコンピュータと、TCP/IPによる通信網を比べ何がどう良いのか?
  • シリアル通信とパラレル通信、それぞれの利点欠点は?
  • 10BASE/5,10BASE/2,10BASE/Tのそれぞれの問題点は?
  • CD1枚のデータを1000BASE-Tのネットワークで転送するのに何秒かかる?
    • サンプリングレート44.1kHz,16bit,ステレオ2ch,74分

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー