ホーム » t-saitoh の投稿 (ページ 19)

作者アーカイブ: t-saitoh

2025年5月
 123
45678910
11121314151617
18192021222324
25262728293031

検索・リンク

Microsoft AutoUpdate Trouble

MacOS で、Office 系を立ち上げると、以下の警告ポップアップがしつこく表示されるようになった。ウィンドウ閉じても数秒後に沸いて出てくるし、OK 押しても同じ。

表示されたメッセージでググると、こちらのページが見つかる。/Library/Application Support/Microsoft の中を探して Microsoft AutoUpdate のフォルダを消せと書いてある。はぁ…

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ cd /Library/Application\ Support/Microsoft/
$ find -name "*AutoUpdate*" -print
$ cd MAU2.0
$ sudo rm -rf Microsoft\ AutoUpdate.app
$ cd /Library/Application\ Support/Microsoft/ $ find -name "*AutoUpdate*" -print $ cd MAU2.0 $ sudo rm -rf Microsoft\ AutoUpdate.app
$ cd /Library/Application\ Support/Microsoft/
$ find -name "*AutoUpdate*" -print
$ cd MAU2.0
$ sudo rm -rf Microsoft\ AutoUpdate.app

んで、うざい画面は出なくなったけど、当然ながら AutoUpdate が動かなくなったので、Office 365 の再インストール。Office系はすでにインストールをしてあるし、新たに AutoUpdate で問題があったので AutoUpdate をインストールするよ…と表示されて、無事復旧。

事の始めは、Microsoft Teams Classic を使ってたら、”新しい Teams” という表示があってインストールしたのが原因かも。ということで、新しい Teams は入れないのが正解かな。

2分探索木

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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 key ;
else if ( a[m] > key )
R = m ;
else
L = m + 1 ;
}
return -1 ; // 見つからなかった
}
void main() {
printf( "%d¥n" , bin_search( array , 0 , 8 ) ) ;
}
// 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 key ; else if ( a[m] > key ) R = m ; else L = m + 1 ; } return -1 ; // 見つからなかった } void main() { printf( "%d¥n" , bin_search( array , 0 , 8 ) ) ; }
// 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 key ;
      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分探索木とよぶ。

このデータ構造をプログラムで書いてみよう。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct Tree {
struct Tree* left ;
int data ;
struct Tree* right ;
} ;
// 2分木を作る補助関数
struct Tree* tcons( struct Tree* L ,
int d ,
struct Tree* R ) {
struct Tree* n = (struct Tree*)malloc(
sizeof( struct Tree ) ) ;
if ( n != NULL ) { /* (A) */
n->left = L ;
n->data = d ;
n->right = R ;
}
return n ;
}
// 2分探索木よりデータを探す
int tree_search( struct List* p , int key ) {
while( p != NULL ) {
if ( p->data == key )
return key ;
else if ( p->data > key )
p = p->left ;
else
p = p->right ;
}
return -1 ; // 見つからなかった
}
struct Tree* top = NULL ;
void main() {
// 木構造をtcons()を使って直接生成 (B)
top = tcons( tcons( tcons( NULL , 13 , NULL ) ,
27 ,
tcons( NULL , 38 , NULL ) ) ,
42 ,
tcons( tcons( NULL , 64 , NULL ) ,
72 ,
tcons( NULL , 81 , NULL ) ) ) ;
printf( "%d¥n" , tree_search( top , 64 ) ) ;
}
struct Tree { struct Tree* left ; int data ; struct Tree* right ; } ; // 2分木を作る補助関数 struct Tree* tcons( struct Tree* L , int d , struct Tree* R ) { struct Tree* n = (struct Tree*)malloc( sizeof( struct Tree ) ) ; if ( n != NULL ) { /* (A) */ n->left = L ; n->data = d ; n->right = R ; } return n ; } // 2分探索木よりデータを探す int tree_search( struct List* p , int key ) { while( p != NULL ) { if ( p->data == key ) return key ; else if ( p->data > key ) p = p->left ; else p = p->right ; } return -1 ; // 見つからなかった } struct Tree* top = NULL ; void main() { // 木構造をtcons()を使って直接生成 (B) top = tcons( tcons( tcons( NULL , 13 , NULL ) , 27 , tcons( NULL , 38 , NULL ) ) , 42 , tcons( tcons( NULL , 64 , NULL ) , 72 , tcons( NULL , 81 , NULL ) ) ) ; printf( "%d¥n" , tree_search( top , 64 ) ) ; }
struct Tree {
   struct Tree* left ;
   int          data ;
   struct Tree* right ;
} ;

// 2分木を作る補助関数
struct Tree* tcons( struct Tree* L ,
                    int          d ,
                    struct Tree* R ) {
   struct Tree* n = (struct Tree*)malloc(
                       sizeof( struct Tree ) ) ;
   if ( n != NULL ) { /* (A) */
      n->left = L ;
      n->data = d ;
      n->right = R ;
   }
   return n ;
}

// 2分探索木よりデータを探す
int tree_search( struct List* p , int key ) {
   while( p != NULL ) {
      if ( p->data == key )
         return key ;
      else if ( p->data > key )
         p = p->left ;
      else
         p = p->right ;
   }
   return -1 ; // 見つからなかった
}
struct Tree* top = NULL ;

void main() {
   // 木構造をtcons()を使って直接生成 (B)
   top = tcons( tcons( tcons( NULL , 13 , NULL ) ,
                       27 ,
                       tcons( NULL , 38 , NULL ) ) ,
                42 ,
                tcons( tcons( NULL , 64 , NULL ) ,
                       72 ,
                       tcons( NULL , 81 , NULL ) ) ) ;
   printf( "%d¥n" , tree_search( top , 64 ) ) ;
}

この方式の注目すべき点は、struct Tree {…} で宣言しているデータ構造は、2つのポインタと1つのデータを持つという点では、双方向リストとまるっきり同じである。データ構造の特徴の使い方が違うだけである。

理解度確認

  • 上記プログラム中の、補助関数tcons() の(A)の部分 “if ( n != NULL )…” の判定が必要な理由を答えよ。
  • 同じくmain() の (B) の部分 “top = tcons(…)” において、末端部に NULL を入れる理由を答えよ。

2分木に対する処理

2分探索木に対する簡単な処理を記述してみよう。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// データを探す
int search( struct Tree* p , int key ) {
// 見つかったらその値、見つからないと-1
while( p != NULL ) {
if ( p->data == key )
return key ;
else if ( p->data > key )
p = p->left ;
else
p = p->right ;
}
return -1 ;
}
// データを全表示
void print( struct Tree* p ) {
if ( p != NULL ) {
print( p->left ) ;
printf( "%d¥n" , p->data ) ;
print( p->right ) ;
}
}
// データ件数を求める
int count( struct Tree* p ) {
if ( p == NULL )
return 0 ;
else
return 1
+ count( p->left )
+ count( p->right ) ;
}
// データの合計を求める
int sum( struct Tree* p ) {
if ( p == NULL )
return 0 ;
else
return p->data
+ count( p->left )
+ count( p->right ) ;
}
// データの最大値
int max( struct Tree* p ) {
while( p->right != NULL )
p = p->right ;
return p->data ;
}
// データを探す int search( struct Tree* p , int key ) { // 見つかったらその値、見つからないと-1 while( p != NULL ) { if ( p->data == key ) return key ; else if ( p->data > key ) p = p->left ; else p = p->right ; } return -1 ; } // データを全表示 void print( struct Tree* p ) { if ( p != NULL ) { print( p->left ) ; printf( "%d¥n" , p->data ) ; print( p->right ) ; } } // データ件数を求める int count( struct Tree* p ) { if ( p == NULL ) return 0 ; else return 1 + count( p->left ) + count( p->right ) ; } // データの合計を求める int sum( struct Tree* p ) { if ( p == NULL ) return 0 ; else return p->data + count( p->left ) + count( p->right ) ; } // データの最大値 int max( struct Tree* p ) { while( p->right != NULL ) p = p->right ; return p->data ; }
// データを探す
int search( struct Tree* p , int key ) {
   // 見つかったらその値、見つからないと-1
   while( p != NULL ) {
      if ( p->data == key )
         return key ;
      else if ( p->data > key )
         p = p->left ;
      else
         p = p->right ;
   }
   return -1 ;
}
// データを全表示
void print( struct Tree* p ) {
   if ( p != NULL ) {
      print( p->left ) ;
      printf( "%d¥n" , p->data ) ;
      print( p->right ) ;
   }
}
// データ件数を求める
int count( struct Tree* p ) {
   if ( p == NULL )
      return 0 ;
   else
      return 1
             + count( p->left )
             + count( p->right ) ;
}
// データの合計を求める
int sum( struct Tree* p ) {
   if ( p == NULL )
      return 0 ;
   else
      return p->data
             + count( p->left )
             + count( p->right ) ;
}
// データの最大値
int max( struct Tree* p ) {
   while( p->right != NULL )
      p = p->right ;
   return p->data ;
}

これらの関数では、木構造の全てに対する処理を実行する場合には、再帰呼び出しが必要となる。

2分探索木にデータを追加

前回の授業では、データの木構造は、補助関数 tcons() により直接記述していた。実際のプログラムであれば、データに応じて1件づつ木に追加するプログラムが必要となる。この処理は以下のようになるだろう。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct Tree* top = NULL ;
// 2分探索木にデータを追加する処理
void entry( int d ) {
struct Tree** tail = &top ;
while( *tail != NULL ) {
if ( (*tail)->data == d ) // 同じデータが見つかった
break ;
else if ( (*tail)->data > d )
tail = &( (*tail)->left ) ; // 左の枝に進む
else
tail = &( (*tail)->right ) ; // 右の枝に進む
}
if ( (*tail) == NULL )
*tail = tcons( d , NULL , NULL ) ;
}
int main() {
char buff[ 100 ] ;
int x ;
while( fgets( buff , sizeof( buff ) , stdin ) != NULL )
if ( sscanf( buff , "%d" , &x ) != 1 )
break ;
entry( x ) ;
return 0 ;
}
struct Tree* top = NULL ; // 2分探索木にデータを追加する処理 void entry( int d ) { struct Tree** tail = &top ; while( *tail != NULL ) { if ( (*tail)->data == d ) // 同じデータが見つかった break ; else if ( (*tail)->data > d ) tail = &( (*tail)->left ) ; // 左の枝に進む else tail = &( (*tail)->right ) ; // 右の枝に進む } if ( (*tail) == NULL ) *tail = tcons( d , NULL , NULL ) ; } int main() { char buff[ 100 ] ; int x ; while( fgets( buff , sizeof( buff ) , stdin ) != NULL ) if ( sscanf( buff , "%d" , &x ) != 1 ) break ; entry( x ) ; return 0 ; }
struct Tree* top = NULL ;

// 2分探索木にデータを追加する処理
void entry( int d ) {
   struct Tree** tail = &top ;
   while( *tail != NULL ) {
      if ( (*tail)->data == d )       // 同じデータが見つかった
         break ;
      else if ( (*tail)->data > d )
         tail = &( (*tail)->left ) ;  // 左の枝に進む
      else
         tail = &( (*tail)->right ) ; // 右の枝に進む
   }
   if ( (*tail) == NULL )
      *tail = tcons( d , NULL , NULL ) ;
}

int main() {
   char buff[ 100 ] ;
   int x ;

   while( fgets( buff , sizeof( buff ) , stdin ) != NULL )
      if ( sscanf( buff , "%d" , &x ) != 1 )
         break ;
      entry( x ) ;

   return 0 ;    
}

このプログラムでは、struct Tree** tail というポインタへのポインタ型を用いている。tail が指し示す部分をイメージするための図を以下に示す。

理解確認

  • 関数entry() の14行目の if 判定を行う理由を説明せよ。
  • 同じく、8行目の tail = &( (*tail)->left ) の式の各部分の型について説明せよ。
  • sscanf() の返り値を 1 と比較している理由を説明せよ。
  • entry() でデータを格納する処理時間のオーダを説明せよ。

ポインタのポインタを使わない挿入

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct Tree* insert( struct Tree* p , int x ) {
if ( p == NULL )
p = tcons( NULL , x , NULL ) ;
else if ( p->data == x )
;
else if ( p->data > x )
p->left = insert( p->left , x ) ;
else
p->right = insert( p->right , x ) ;
return p ;
}
int main() {
struct Tree* top = NULL ;
int x ;
while( scanf( "%d" , &x ) == 1 ) {
top = insert( top , x ) ;
}
print( top ) ;
return 0 ;
}
struct Tree* insert( struct Tree* p , int x ) { if ( p == NULL ) p = tcons( NULL , x , NULL ) ; else if ( p->data == x ) ; else if ( p->data > x ) p->left = insert( p->left , x ) ; else p->right = insert( p->right , x ) ; return p ; } int main() { struct Tree* top = NULL ; int x ; while( scanf( "%d" , &x ) == 1 ) { top = insert( top , x ) ; } print( top ) ; return 0 ; }
struct Tree* insert( struct Tree* p , int x ) {
   if ( p == NULL )
      p = tcons( NULL , x , NULL ) ;
   else if ( p->data == x )
      ;
   else if ( p->data > x )
      p->left = insert( p->left , x ) ;
   else
      p->right = insert( p->right , x ) ;
   return p ;
}

int main() {
   struct Tree* top = NULL ;
   int x ;
   while( scanf( "%d" , &x ) == 1 ) {
      top = insert( top , x ) ;
   }
   print( top ) ;
   return 0 ;
}

日本ゲーム大賞2023「U18部門」で2EI福嶋君コラボレーションプラットフォーム賞🏆

一般社団法人コンピュータエンターテインメント協会による、18歳以下の方が開発したゲーム作品を対象に審査、表彰する「U18部門」で、2EI福嶋くんが
コラボレーションプラットフォーム賞(Nintendo Switch プチコン4 SmileBASIC 作品名:CRYPTFACTOR)を受賞🏆しました。

落ちてくる数字に、数を入力すると素因数分解され、1になると撃破….(^_^;

サブネットとWiFiネットワーク

CSMA/CD方式

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アドレスを確認できる。

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

旧式のHUB(Dumb HUB)は、電気的に信号を増幅するだけなので、物理層(レイヤー1)だけで通信を行う。
スイッチングHUBは、MACアドレスを見て通信相手を判断(データリンク層/レイヤー2)する。最近では、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) なので注意。

双方向リストとdeque

番兵と双方向循環リスト

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

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

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

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

deque(両端キュー)

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

理解確認

  • 双方向リストとはどのようなデータ構造か図を示しながら説明せよ。
  • 双方向リストの利点と欠点はなにか?
  • 番兵を用いる利点を説明せよ。
  • deque の機能と、それを実現するためのデータをリストを用いて実装するには、どうするか?
  • 双方向リストが使われる処理の例としてどのようなものがあるか?

 

第34回 高専プロコン 競技部門 優勝🏆

令和5年10月14日(土),15日(日)にサンドーム福井で開催された、第34回 全国高専プログラミングコンテスト(福井大会)の競技部門で、4EI 高橋くん、辻くん、前田くん、指導教員 西先生 による「蟹高専」チームが優勝🏆しました。🎉

{CAPTION}

競技部門 優勝 蟹高専

自由部門 敢闘賞 チャリレコ

同じく、自由部門にて本選に参加した 4EI 藤野間くん、中西くん、鈴木くん、山腰くん、治内 杏太、指導教員 小松 先生 のチームによるチャリレコも敢闘賞となりました。

メディア掲載

※は学内のみアクセス可

データベースの用語など

データベースの機能

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

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

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

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

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

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

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

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

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

データモデル

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

関係データベースの基礎

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

  • 集合 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さん,男性)

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) はどのようなものか、数学定義や実例をあげて説明せよ。

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間の通信をパケットを送る毎に交代しながら通信路を利用する。

    • 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/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)の組み合わせによって、送受信を行う。

当初は、300bps程度であったが、最終的には64Kbps 程度の通信速度が得られた。

これらの通信速度の改善のため、電話線にデジタル信号で送る ISDN , 電話線の音の信号の高帯域を使った通信 ADSLなどが用いられた。

最近では、光ファイバによる FTTH(Fiber To The Home) により 1Gbps を越える通信が可能となっている。

通信速度の理解と、古い時代の通信速度を体験してもらうため、試しに「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時間
通信費  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 を中心に広がるスター型とも言える。それぞれれのネットワークは相互につながることから、木の枝状に見えるものはツリー型と呼ばれる。また、上流ネットワークでは、機器が故障した場合に一切の通信ができなくなるのは問題があるため、複数のネットワークで相互に接続される。この場合、網が絡むような構造になることから、ネットワーク型と呼ばれる。

第34回全国高専プログラミングコンテスト福井大会

10/14(土),15(日)の両日、第34回全国高専プログラミングコンテストが、福井高専主管にてサンドーム福井で開催されます。 今年は、一般の方も入場可能です。 procon.gr.jp

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

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

リスト構造の利点と欠点

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

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

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

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

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

もう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) の処理であり時間がかかる処理。
ではどうすればよいのか?

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 双方向リストの宣言
struct BD_List {
struct BD_List* prev ; // 1つ前のデータへのポインタ
int data ;
struct BD_List* next ; // 次のデータへのポインタ
} ;
// 双方向リストの宣言 struct BD_List { struct BD_List* prev ; // 1つ前のデータへのポインタ int data ; struct BD_List* next ; // 次のデータへのポインタ } ;
// 双方向リストの宣言
struct BD_List {
    struct BD_List* prev ; // 1つ前のデータへのポインタ
    int             data ;
    struct BD_List* next ; // 次のデータへのポインタ
} ;

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// リスト生成補助関数
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 ) ;
}
// リスト生成補助関数 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 ) ; }
// リスト生成補助関数
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 ) を書いてみよう。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 双方向リストの指定場所 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 ) ;
}
// この手のリスト処理のプログラムでは、命令の順序が重要となる。
// コツとしては、修正したい箇所の遠くの部分を操作する処理から
// 書いていくと間違いが少ない。
// 双方向リストの指定場所 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 ) ; } // この手のリスト処理のプログラムでは、命令の順序が重要となる。 // コツとしては、修正したい箇所の遠くの部分を操作する処理から // 書いていくと間違いが少ない。
// 双方向リストの指定場所 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 ) ;
}

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

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー