ホーム » スタッフ » 斉藤徹

斉藤徹」カテゴリーアーカイブ

2021年9月
 1234
567891011
12131415161718
19202122232425
2627282930  

最新の投稿(電子情報)

アーカイブ

カテゴリー

専攻科の履修登録の確認作業

特例認定の専攻科で、学位授与機構の学位授与してもらうために、専攻科2年の学生さんの履修計画書のアップロードの期間となっている。履修科目の間違い(履修時期)のチェックも必要だけど、大量の行のチェックは大変。

履修登録のWebシステムで、履修科目のファイルを CSV 出力させた「申請者科目データ.csv」を、他の学生と比較してみる。

$ nkf -Lu -w 申請者科目データ.csv
  | awk -F, '{print $4,$9,$16}' > aa.csv
     nkf -Lu (行末文字コードを¥n)
         -w (文字コードをUTF8に変更)
     awk -F, (コンマで区切る)
         '{print $4,$9,$16}' (科目名,履修時期,履修/未習得)だけ抽出

$ diff -u aa.csv bb.csv | grep -v '^ ' | grep -v "0"$
     diff -u        違いを出力
     grep -v '^ '   先頭が空白の行を削除(違いがなかった)
     grep -v '"0"$' 行末が"0"を行を削除(履修していない)

キャンパスツアー2021

今日は、福井高専のオープンキャンパスです。「キャンパスツアー」ということで、各学科の展示を次々と見学します。

電子情報工学科では、各卒研室の中からいくつかのテーマにて発表してもらいます。

以前であれば、中学生と保護者の方が一緒に見学してもらっていましたが、コロナ対策ということで保護者の方には、Teamsのリモート会議の機能を使って別室にて各学科の発表会場の内容を見学してもらっています。

{CAPTION}

{CAPTION}

データと誤差(クラフテックラボ)

電子情報工学科のジュニアドクター養成講座・クラフテックラボでは、9/12(日)に、データと誤差の講座を行いました。
ストップウォッチでの時間の測定の実験を通して、誤差のズレやばらつきについて考えてもらう内容でした。
{CAPTION}

{CAPTION}

緊急連絡システムにSendGridを使ったら

丹南地区の緊急連絡システムを動かしているけど、最近はメールが届かないといった連絡を受けることも多い。

2021/09/10 については、docomo.ne.jp 宛のメールの配送でトラブルが発生し、設定を見直すことで無事に配送ができるように復旧することができました。

基本的に無料で提供しているサービスだけれど、Azure のサーバから送信すると、迷惑メールの送信者と誤認される可能性が高く、最近のキャリアではメールが届かないなどの問題が起こりやすい。

正しく配信されるようにするために、サーバを信用してもらえるような構成にすることも考えられるが、設定のメンテナンスの負担も懸念される。そこで、Azure サーバでメールを配信するための、有料サービス SendGrid を使うことを検討してみた。

最初に、有料システムを利用することになるので、どの課金体系を利用するのかを判断するために、現在の緊急連絡システムの月別のメール流量を集計してみた。この結果、最近ではコロナによる連絡も多いのか平均4万通/月のメールが流れている。多い時には、10万通を超える場合もある。(下図参照)

これを、元にSendGrid を使った場合の費用を算出してみる。一番安い料金 “Essencial 4k” では、1900円/月 (4万通/月,これ以上は、0.125円/通)、”Essencial 10k” では、3800円/月(10万通/月, これ以上は0.094円/通)となる。これを見ると、現状の平均 4万通 からしても Essencial 4k を採用すれば、約 年額 45,000 円 以下に収まると思われる。(下図参照)

 

教員室にhomebridge導入

自宅では、iPhoneとの連動で、Raspberry-Pi にhomebridgeを入れて、家電制御で便利に使っているけど、教員室の Linux サーバにも導入してみた。

自分のスマホに ping で所在確認するプラグインと、自室の在室や予定を表示している掲示板システムを操作するプラグインを入れてみた。

在室状態に応じて掲示板にメッセージを表示していたけど、更新間隔が1時間おきとかなので、「不在表示なのに居るじゃん…」とか学生からのチェックが入るので、ドアや充電器の所に置いてあるNFCをタッチすると、掲示板の在室/不在表示+予定表示するようにしてみた。

# 以前から同様なことはしていたけど、手抜き Web API 経由だったのを、ホームアクセサリとして動かしてみた。

変態コード

Twitterで以下のようなコードが紹介されていた。
ポイントは、a[i] と書くべき所が、*(a + i) と等価であり、*(i + a) = i[a] と書かれている点。

{CAPTION}

でも、昔どこかで見たという点では、以下のコードの方がさらに変態っぽいでしょ。

{CAPTION}

8/14(土)学校周辺のハザードマップ

福井県は、大きく心配するほどじゃないけど、危険性の確認。吉野瀬川の方が先に危なくなるらしい。
{CAPTION}

ゼロトラスト研究会

EVER/IPのコネクトフリーさんが、鯖江市・越前市・福井県・地元企業と協力して、EVER/IPの活用を見いだすためのゼロトラスト研究会が行われました。

{CAPTION}

ゼロトラストとは

  • LANなどの小さなネットワーク内を安全とする境界型セキュリティから脱却し、世界中のコンピュータを結ぶ。
  • 境界型セキュリティの限界。LANの中でも信用しないという考え方

IPv6

  • IPアドレスの詐称の可能性がある

EVER/IP

  • Advanced Security
  • Autonomous Networking
  • Authenticated Connectivity
  • 公開鍵暗号。公開鍵のハッシュがIPアドレス
  • ゼロタッチプロビジョニング

自治体ネットワーク、三層分離の見直し

  • 自治体の3層構造
    • 個人番号利用事業系
    • LGWAN接続系
    • インターネット接続系
  • LGWAN系、インターネット接続系に、リモートワークシステムが接続するには。

さくらクラウドの実験サーバのアップグレード

学生実験で、Webプログラムのセキュリティ問題の対応をテーマに実験しているけど、Ubuntu 18系を20系にアップグレードを行った。基本的な実験だけなので、Apache+PHP程度なので、移行も手間取らないだろうと踏んでいたけど、sulinux でちょっと手間取った。

# systemctl enable apache2
# aptitude install update-manager
# aptitude update
# aptitude dist-upgrade
# do-release-upgrade -d

無事に、更新が終わって再起動をかけたら、ブートに失敗。ssh などが繋がらなくなる。

どうしようもなくなったので、さくらのクラウドのコンソールをみると、sssd 関連でエラーが出てブート途中で止まっている。
冷や汗をかきながら、Ubuntu ブート時のリカバリーモードの起動を試みて、ようやく成功。

起動時にESCを押すと、ブートメニューが表示される。インストール済みの kernel とそのリカバリーモードの一覧がでるので、リカバリーモードでログイン。”aptitude remove sssd-common” により一旦、sssd を削除(sulinux関連)すると、無事に起動したので、基本機能の確認をして、再び”aptitude install sssd-common”。

アップグレード作業中に、apache2 が disable されていたり、php の関連パッケージが入っていなかったので、インストール。

# systemctl enable apache2
# systemctl start apache2
# aptitude install php-mbstring php-sqlite3
# a2enmod php7.4

集合とリスト処理

リスト構造は、必要に応じてメモリを確保するデータ構造であり、データ件数に依存しないプログラム が記述できる。その応用として、集合処理を考えてみる。集合処理の記述には、2進数を使った方式リストを用いた方法が一般的である。以下にその処理について示す。

bit演算子

2進数を用いた集合処理を説明する前に、2進数を使った計算に必要なbit演算子について復習してみる。

bit演算子 計算の意味 関連知識
& bit AND 3 & 5
0011)2 & 0101)2= 0001)2
論理演算子
if ( a == 1 && b == 2 ) …
| bit OR 3 | 5
0011)2 | 0101)2= 0111)2
論理演算子
if ( a == 1 || b == 2 ) …
~ bit NOT ~5
~ 00..00,0101)2= 11..11,1010)2
論理否定演算子
if ( !a == 1 ) …
^ bit EXOR 3 ^ 5
0011)2 ^ 0101)2= 0110)2
<< bit 左シフト 3 << 2
0011)2 << 2 = 001100)2
x << y は x * 2y と同じ
>> bit 右シフト 12 >> 2
1100)2 >> 2 = 11)2
x >> y は x / 2y と同じ
#include <stdio.h>

int main() {
   // bit演算子と論理演算子
   printf( "%d¥n" , 12 &  5 ) ;  // 1100 & 0101 = 0100 よって 4が表示される
   printf( "%d¥n" , 12 && 0 ) ;  // 0が表示 論理演算子とbit演算子の違い
   printf( "%d¥n" , 12 |  5 ) ;  // 1100 | 0101 = 1101 よって 13が表示される
   printf( "%d¥n" , 12 || 0 ) ;  // 1が表示 
   // シフト演算子
   printf( "%d¥n" ,  3 << 2 ) ;  // 12が表示
   printf( "%d¥n" , 12 >> 2 ) ;  // 3が表示
   // おまけ
   printf( "%d¥n" , ~(unsigned)12 + 1 ) ;  // 2の補数(NOT 12 + 1) = -12
   return 0 ;
}

2進数を用いた集合計算

リストによる集合の前に、もっと簡単な集合処理を考える。

最も簡単な方法は、要素に含まれる=1 か 含まれない=0 を配列に覚える方法であろう。数字Nが集合に含まれる場合は、配列[N]に1を覚えるものとする。この方法で積集合などを記述した例を以下に示す。ただし、自分で考える練習として穴埋めを含むので注意。

しかし、上述のプログラムでは、要素に含まれる/含まれないという1bitの情報を、整数型で保存しているためメモリの無駄である。

データ件数の上限が少ない場合には、「2進数の列」の各ビットを集合の各要素に対応づけし、要素の有無を0/1で表現する。この方法を用いるとC言語のビット演算命令で 和集合、積集合を計算できるので、処理が極めて簡単になる。

2進数を用いた集合計算

扱うデータ件数が少ない場合には、「2進数の列」の各ビットを集合の各要素に対応づけし、要素の有無を0/1で表現する。この方法を用いるとC言語のビット演算命令で 和集合、積集合を計算できるので、処理が極めて簡単になる。

以下のプログラムは、0〜31の数字を2進数の各ビットに対応付けし、 ba = {1,2,3} , bb = {2,4,6} , bc= {4,6,9} を要素として持つ集合で、ba bb , bb bc , ba  bc の計算を行う例である。

// 符号なし整数を uint_t とする。
typedef unsigned int uint_t ;

// uint_tのbit数
#define UINT_BITS (sizeof( uint_t ) * 8)

// 集合の内容を表示
void bit_print( uint_t x ) {
   for( int i = 0 ; i < UINT_BITS ; i++ )
      if ( (x & (1 << i)) != 0 )
         printf( "%d " , i ) ;
   printf( "\n" ) ;
}
void main() {     // 98,7654,3210
   // ba = {1,2,3} = 00,0000,1110
   uint_t ba = (1<<1) | (1<<2) | (1<<3) ;
   // bb = {2,4,6} = 00,0101,0100
   uint_t bb = (1<<2) | (1<<4) | (1<<6) ;
   // bc = {4,6,9} = 10,0101,0000
   uint_t bc = (1<<4) | (1<<6) | (1<<9) ;

   // 集合積(bit AND)
   bit_print( ba & bb ) ; // ba ∩ bb = {2}                 
   bit_print( bb & bc ) ; // bb ∩ bc = {4,6}
   // 集合和(bit OR)
   bit_print( ba | bc ) ; // ba ∪ bc = {1,2,3,4,6,9}
}

有名なものとして、エラトステネスのふるいによる素数計算を2進数を用いて記述してみる。このアルゴリズムでは、各bitを整数に対応付けし、素数で無いと判断した2進数の各桁に1の目印をつけていく方式である。

uint_t prime = 0 ; // 初期値=すべての数は素数とする。

void filter() {
   // 倍数に非素数の目印をつける
   for( int i = 2 ; i < UINT_BITS ; i++ ) {
      if ( (prime & (1 << i)) == 0 ) {
         // iの倍数には、非素数の目印(1)をつける
         for( int j = 2*i ; j < UINT_BITS ; j += i )
            prime |= (1 << j) ;
      }
   }
   // 非素数の目印の無い値を出力
   for( int i = 2 ; i < UINT_BITS ; i++ ) {
      // 目印のついていない数は素数
      if ( (prime & (1 << i)) == 0 )
         printf( "%d\n" , i ) ;
   }
}

リスト処理による積集合

前述の方法は、リストに含まれる/含まれないを、2進数の0/1で表現する方式である。しかし、2進数であれば、unsigned int で 32要素、unsigned long long int で 64 要素が上限となってしまう。 (32bitコンピュータ,gccの場合)

しかし、リスト構造であれば、リストの要素として扱うことで、要素件数は自由に扱える。また、今までの授業で説明してきた cons() などを使って表現すれば、簡単なプログラムでリストの処理が記述できる。

// 先週までに説明してきたリスト構造と補助関数
struct List {
   int     data ;
   struct List* next ;
} ;
struct List* cons( int x , struct List* n ) {
   struct List* ans ;
   ans = (struct List*)malloc( sizeof( struct List ) ) ;
   if ( ans != NULL ) {
      ans->data = x ;
      ans->next = n ;
   }
   return ans ;
}
void print( struct List* p ) {
   for( ; p != NULL ; p = p->next ) {
      printf( "%d " , p->data ) ;
   }
   printf( "\n" ) ;
}
int find( struct List* p , int key ) {
   for( ; p != NULL ; p = p->next )
      if ( p->data == key )
         return 1 ;
   return 0 ;
}

例えば、積集合(a ∩ b)を求めるのであれば、リストa の各要素が、リストb の中に含まれるか find 関数でチェックし、 両方に含まれたものだけを、ans に加えていく…という考えでプログラムを作ると以下のようになる。

// 集合積の計算
struct List* set_prod( struct List* a , struct List* b ) {
   struct List* ans = NULL ;
   for( ; a != NULL ; a = a->next ) {
      // aの要素がbにも含まれていたら、ansに加える
      if ( find( b , a->data ) )
         ans = cons( a->data , ans ) ;
   }
   return ans ;
}
void main() {
   struct List* a = cons( 1, cons( 2, cons( 3, NULL ) ) ) ;
   struct List* b = cons( 2, cons( 4, cons( 6, NULL ) ) ) ;
   struct List* c = cons( 4, cons( 6, cons( 9, NULL ) ) ) ;
   print( set_prod( a , b ) ) ;
   print( set_prod( b , c ) ) ;
}

例題として、和集合差集合などを考えてみよう。

リストの共有と削除の問題

リスト処理では、mallocを使うが、メモリリークをさせないためには、使用後のリストの廃棄は重要である。リストの全要素を捨てる処理であれば、以下のようになるであろう。

void list_free( struct List* p ) {
   while( p != NULL ) {
      struct List* d = p ;
      p = p->next ;
      free( d ) ; // 順序に注意
   }
}

一方、前説明の和集合(a ∪ b)のプログラムを以下のように作った場合、list_freeの処理は問題となる。

// 集合和
struct List* set_union( struct List*a, struct List*b ) {
   struct List* ans = b ;
   for( ; a != NULL ; a = a->next )
      if ( !find( b , a->data ) )
         ans = cons( a->data , ans ) ;
   return ans ;
}
void main() {
   struct List*a = cons( 1, cons( 2, cons( 3, NULL ) ) ) ;
   struct List*b = cons( 2, cons( 3, cons( 4, NULL ) ) ) ;
   struct List*c = set_union( a , b ) ;
   // a,b,cを使った処理
   // 処理が終わったので、a,b,cを捨てる
   list_free( a ) ;
   list_free( b ) ;
   list_free( c ) ;
   // c = { 1 , (bのリスト) }
   // (b)の部分は先のlist_free(b)で解放済み
}

このような、リストb,リストcで共有されている部分があると、データの廃棄処理をどのように記述すべきなのか、問題となる。

これらの解決方法としては、(1) set_union() の最初で、ans=b となっている部分を別にコピーしておく、(2) 参照カウンタ法を用いる、(3) ガベージコレクタのある言語を用いる…などがある。(2),(3)は後期授業で改めて解説を行う。

// 同じ要素を含む、新しいリストを作る
struct List* copy( struct List*p ) {
   struct List*ans = NULL ;
   for( ; p != NULL ; p = p->next )
      ans = cons( p->data , ans ) ;
   return ans ;
}
struct List* set_union( struct List*a, struct List* b ) {
   struct List* ans = copy( b ) ;
   // この後は自分で考えよう。
}

理解確認

  • 2進数を用いた集合処理は、どのように行うか?
  • リスト構造を用いた集合処理は、どのように行うか?
  • 積集合(A ∩ B)、和集合(A ∪ B)、差集合(A – B) の処理を記述せよ。