ホーム » スタッフ » 斉藤徹 » 講義録 (ページ 47)

講義録」カテゴリーアーカイブ

2025年5月
 123
45678910
11121314151617
18192021222324
25262728293031

検索・リンク

オーダー記法と再帰処理の導入

先週に、2重の繰り返し処理の時間分析をやったので、次のステップに。

2分探索法の処理時間

データを探す処理において、単純検索より速い方法ということで、2分探索法の処理速度見積もりを行う。

// 2分探索法 O(log N)
int a[ 1000 ] = { 対象となるデータ } ;
int size = N ;  // データ数 N
int L = 0 ;     // L=下限のデータの場所
int R = size ;  // R=上限のデータ+1の場所
while( L != R ) {
   int M = (L + R) / 2 ;  // 計算は整数型で行われることに注意
   if ( a[M] == key )     // 見つかった
      break ;
   else if ( a[M] < key ) // |L         |M.         |R
      L = M + 1 ;         // |----------|-+---------|
   else                   // |L---------|M|
      R = M ;             //              |M+1------|R
}

上記のようなプログラムの場合、処理に要する時T(N)は、

 # Mは繰り返し回数

処理は、対象となるデータ件数が繰り返し毎に半分(正確には(N-1)/2だけどここでは厳密な分析はしない)となり、対象データ件数が1件になれば処理が終わる。このことから、データ件数Nとループ回数Mの間には以下の関係が成り立つ。

よって の関係が成り立つ。よって、は、以下のように表せる。

# T(N)の式の中では、logの底については書かないことが一般的。(後の練習問題を参照)

単純なソート(最大選択法)の処理時間

次に、並べ替え処理の処理時間について考える。

int a[ 1000 ] = { 対象となるデータ } ;
int size = N ;

for( int i = 0 ; i < size - 1 ; i++ ) {
    int tmp ;
    // i..size-1 の範囲で一番大きいデータの場所を探す
    int m = i ;
    for( int j = i + 1 ; j < size ; j++ ) {
        if ( a[j] > a[m] )
            m = j ;
    }
    // 一番大きいデータを先頭に移動
    tmp = a[i] ;
    a[i] = a[m] ;
    a[m] = tmp ;
}

このプログラムの処理時間T(N)は… (参考 数列の和の公式)

となる。

オーダー記法

ここまでのアルゴリズムをまとめると以下の表のようになる。ここで処理時間に大きく影響する部分は、最後の項の部分であり、特にその項の係数は、コンピュータの処理性能に影響を受けるが、アルゴリズムの優劣を考える場合は、それぞれ、 の部分の方が重要である。

単純サーチ
2分探索法
最大選択法

そこで、アルゴリズムの優劣を議論する場合は、この処理時間の見積もりに最も影響する項で、コンピュータの性能によって決まる係数を除いた部分を抽出した式で表現する。これをオーダー記法と言う。

単純サーチ オーダーNのアルゴリズム
2分探索法 オーダー log N のアルゴリズム
最大選択法 オーダー N2 のアルゴリズム

練習問題

  1. ある処理のデータ数Nに対する処理時間が、であった場合、オーダー記法で書くとどうなるか?
  2. コンピュータで2分探索法で、データ100件で10[μsec]かかったとする。
    データ10000件なら何[sec]かかるか?
    (ヒント: 底変換の公式)
  3. の処理時間を要するアルゴリズムを、オーダー記法で書くとどうなるか?また、このようなアルゴリズムの例を答えよ。
  4. の処理時間を要するアルゴリズムを、オーダー記法で書くとどうなるか?
    (ヒント: ロピタルの定理)
  • 2と4の解説
  • 1は、N→∞において、N2<<2Nなので、O(2N) 。厳密に回答するなら、練習問題4と同様の説明を行う。
  • 3は、O(1)。誤答の例:O(0)と書いちゃうと、T(N)=Tα×0=0になってしまう。事例は、電話番号を、巨大配列の”電話番号”番目の場所に記憶するといった方法。(これはハッシュ法で改めて講義予定)

再帰呼び出しの予習

次の講義の基礎を確認という意味で、再帰呼出しと簡単な処理の例を説明する。

最初に定番の階乗(fact)

次に、フィボナッチ数列の場合

次の講義への導入問題

ここで示す導入問題をすべて答えるには、若干の予習が必要です。まずはどういう考え方をすれば解けるかな…を考えてみてください。

  • fact(N)の処理時間を、のような式で表現し、処理時間をオーダ記法で答えよ。
  • 以下のプログラムの実行結果を答えよ。また、関数sum()の処理時間を対象となるデータ件数N=R-Lを用いてのような式で表現せよ。
int a[] = { 1 , 5 , 8 , 9 , 2 , 3 , 4 , 7 } ;
int sum( int a[] , int L , int R ) {
   if ( R-L == 1 ) {
      return a[L] ;
   } else {
      int M = (L + R) / 2 ;
      return sum( a , L , M ) + sum( a , M , R ) ;
   }
}
int main() {
   printf( "%d¥n" , sum( a , 0 , 8 ) ) ;
   return 0 ;
}

創造工学演習・予備実験・パズル問題

プログラミングコンテストの競技部門では、パズルのような組み合わせ問題が 出題されることが多い。また、課題部門や自由部門であっても、複数の条件の組み合わせの中から最良のものを選ぶといった処理も求められる。そこで、この予備実験では、きわめて単純なパズル問題(組み合わせ問題) のプログラムについて扱う。

組み合わせ問題の基礎

簡単な問題として「100未満の整数の値を3つ選び、その値を辺の長さとした場合、 直角三角形となるものをすべて表示する。」について考える。

一番簡単な方法は、以下となるであろう。

#include <stdio.h>
#include <math.h>
#include <time.h>

// 整数比の直角三角形の一覧を求める。
void integer_triangle( int n ) {
   for( int a = 1 ; a < n ; a++ ) {
      for( int b = 1 ; b < n ; b++ ) {
         // 一番ダサい方法
         for( int c = 1 ; c < n ; c++ ) {
            if ( a*a + b*b == c*c ) {
               printf( "%d %d %d\n" , a , b , c ) ;
            }
         }
      }
   }
}
int main() {
   integer_triangle( 100 ) ;
   return 0 ;
}

しかしこのプログラムの欠点としては、100×100×100回のループで無駄な処理が多い。

ループ回数を減らすだけなら、最も内側の処理を、計算で整数値か確認すればいい。

void integer_triangle( int n ) {
   for( int a = 1 ; a < n ; a++ ) {
      for( int b = 1 ; b < n ; b++ ) {
         // ココも改良できるよね?
         int d = a*a + b*b ;
         int c = (int)sqrt( d ) ;
         // 斜辺Cの整数値を求め、改めて確認する。
         if ( c*c == d ) {
            printf( "%d %d %d\n" , a , b , c ) ;
         }
      }
   }
}

(1) 計算誤差の問題を考えてみよう。

たとえば、3:4:5の直角三角形で、3*3+4*4 = 25 だが、sqrt(25)は実数で計算するから、 計算誤差で4.99999で求まったらどうなるだろうか?

1~100までの数値で、”int c = sqrt( (double)(i*i) ) ;” を計算してみて、 異なる値が求まることはあるか? 多少の計算誤差があっても正しく処理が行われるにはどうすればいいか、考えてみよう。

(2) 無駄な答えについて考えてみよう。

このプログラムの答えでは、簡単な整数比の答えの「整数倍の答え」も表示されてしまう。 たとえば、(3:4:5)の答えのほかに、(6:8:10)も表示される。 こういった答えを表示しないようにするにはどうすればよいか?

また、この2つのプログラムの処理時間を実際に比べてみる。

#include <stdio.h>
#include <time.h>

int main() {
   time_t start , end ;
   // time() 関数は、秒数しか求まらないので、
   // あえて処理を1000回繰り返し、数秒かかる処理にする。
   start = time( NULL ) ;
   for( int i = 0 ; i < 1000 ; i++ ) {
      // ただし、関数内のprintfをコメントアウトしておくこと
      integer_triangle( 100 ) ;
   }
   end = time( NULL ) ;
   printf( "%lf\n" , difftime( end , start ) ) ;
   return 0 ;
}

再帰プログラミング

組み合わせ問題では、forループの多重の入れ子で問題を解けない場合が多い。 (書けないことはないけど無駄なループで処理が遅くなるか、入れ子段数が可変にできない。)

こういった場合には、再帰プログラミングがよく利用される。 もっとも簡単な再帰の例として、階乗のプログラムを考える。 通常であれば、以下のような for ループで記述することになるだろう。

// 階乗の計算
int fact( int x )
{  // ループ
   int f = 1 ;
   for( int i = 2 ; i <= x ; i++ )
      f = f * i ;
   return f ;
}

再帰呼び出しでは、関数の処理の中に、自分自身の関数呼び出しが含まれる。 また、無限に自分自身を呼び出したら処理が止まらないので、 問題を一つ小さくして、これ以上小さくできないときは処理を止めるように記述する。

int fact( int x )
{  // 再帰呼び出し
   if ( x <= 1 )
      return 1 ;
   else
      return x * fact( x - 1 ) ;
}

ここ以降は、指定長さを指定辺の組み合わせで作る課題と、後に述べるFlood-fill 課題の選択とする。

指定長を指定辺の組み合わせで作る(テーマ1)

再帰を使った簡単なパズル問題として、以下のプログラムを作成したい。

配列の中に、複数の辺の長さが入っている。これを組み合わせて指定した長さを作れ。 使用する辺はできるだけ少ない方がよい。
int a[] = { 4 , 5 , 2 , 1 , 3 , 7 } ;
(例) 辺の長さ10を作るには、(5,4,1)とか(7,3)などが考えられる。
これは、ナップサック問題の基本問題で、容量の決まったナップサックに最大量入れる組合せを求めるのと同じである。

このプログラムを解くには…

10 を [4,5,2,1,3,7] で作るには...
 (0) 6=10-4 を [4|5,2,1,3,7]で作る。
 (1) 5=10-5 を [5|4,2,1,3,7]で作る。
 (2) 8=10-2 を [2|5,4,1,3,7]で作る。
 (3) 9=10-1 を [1|5,2,4,3,7]で作る。
 (4) 7=10-3 を [3|5,2,1,4,7]で作る。
 (5) 3=10-7 を [7|5,2,1,3,4]で作る。

そこで、ここまでの考えを、以下のようなプログラムで記述してみる。
# まだ再起呼び出しにはしていない。

// 指定されたデータを入れ替える。
void swap( int*a , int*b )
{  int x = *a ;
   *a = *b ;
   *b = x ;
}
void check( int array[] , int size , int len , int n )
{
   // array[] 配列
   // size    配列サイズ
   // len     作りたい長さ
   // n       使った個数
   for( int i = n ; i < size ; i++ ) {
      // i番目を先頭に...
      swap( &array[ n ] , &array[ i ] ) ;
      printf( "check( array , %d , %d , %d )\n" ,
              size , len - array[ n ] , n+1 ) ;
      // 最初のswapでの変更を元に戻す。
      swap( &array[ i ] , &array[ n ] ) ;
   }
}
int main() {
   int a[] = { 4 , 5 , 2 , 1 , 3 , 7 } ;
   check( a , 6 , 10 , 0 ) ;
}

(1) これを再帰呼び出しにしてみよう。どう書けばいい?

void check( int array[] , int size , int len , int n )
{
   // array[] 配列
   // size    配列サイズ
   // len     作りたい長さ
   // n       使った個数
   if ( len < 0 ) {
      // 指定した丁度の長さを作れなかった。
      ;
   } else if ( len == 0 ) {
      // 指定した長さを作れたので答えを表示。
      for( int i = 0 ; i < n ; i++ ) {
         printf( "%d " , array[ i ] ) ;
      }
      printf( "\n" ) ;
   } else {
      // 問題を一つ小さくして再帰。
      for( int i = n ; i < size ; i++ ) {
         swap( &array[ n ] , &array[ i ] ) ;
         printf( "check( array , %d , %d , %d )\n" ,
                 size , len - array[ n ] , n+1 ) ;
         check( array , size , len - array[ n ] , n + 1 ) ;
         swap( &array[ i ] , &array[ n ] ) ;
      }
   }
}

(2) 少ない組み合わせの方がポイントが高い場合には、プログラムをどう変更する?
(3) 答えが1つだけで良い場合は、プログラムをどう変更する?
(4) このプログラムでは、冗長な答えがあるか?ないか?検討せよ。
(5) 前設問の整数比直角三角形のプログラムで、冗長な答えを削除するプログラムを作成せよ。

# レポートでは、(2),(3),(4)を検討した結果を実験すること。(5)までチャレンジした人は(2),(3),(4)の説明は簡単に記載するだけで良い。

Flood fill アルゴリズム

前の問題は、今年度のプログラミングコンテストの課題のパズルとは、方向性が違うので、今年度のプロコンの競技部門に近いパズルネタで演習。(Wikipedia Flood-fill参照)

以下の image のような2次元配列が与えられたら、指定座標(x,y)を中心に周囲を塗りつぶす処理を作成せよ。

include <stdio.h>

// *は壁 SPCは白 この領域の指定位置を#で塗りつぶす。                            
char image1[10][10] = { // (4,4)始点で塗りつぶし後
        "*********" ,   // ********* 
        "*   *   *" ,   // *   *###*
        "*   *   *" ,   // *   *###*
        "*  *    *" ,   // *  *####*
        "***   ***" ,   // ***###***
        "*    *  *" ,   // *####*  *
        "*   *   *" ,   // *###*   *
        "*   *   *" ,   // *###*   *
        "*********" ,   // *********
} ;

char image2[10][10] = { // 応用問題用の画像例
        "*********" ,   //  *   のような隙間は通り抜けられる
        "*   *   *" ,   // *    ようにするにはどうすればいい?
        "*  **   *" ,   //   **
        "* **    *" ,   //  **  これは通り抜けられない
        "***   ***" ,   // **
        "*    *  *" ,
        "*   *   *" ,
        "*   *   *" ,
        "*********" ,
} ;

// 盤面を表示                                                                   
void print_image( char image[10][10] ) {
  for( int y = 0 ; y < 9 ; y++ ) {
    for( int x = 0 ; x < 9 ; x++ ) {
      printf( "%c" , image[y][x] ) ;
    }
    printf( "\n" ) ;
  }
}

// 再帰呼び出しを使った flud_fill アルゴリズム                                  
void flood_fill( char image[10][10] , int x , int y , char fill ) {
  // image: 塗りつぶす画像
  // x,y:   塗りつぶす場所
  // fill:  書き込む文字
  // 指定座標が空白なら
  if ( image[y][x] == ' ' ) {
    // その座標を埋める
    image[y][x] = fill ;
    //////////////////////////////////////
    // ここに周囲をflud_fillする処理を書く  //
    ////////////////////////////////////// 
  }
}

int main() {
  print_image( image1 ) ;
  flood_fill( image1 , 4 , 4 , '#' ) ;
  print_image( image1 ) ;
  return 0 ;
}

応用問題

Wikipediaのflood-fill のプログラムの説明のアルゴリズムでは、左図黒のような斜めに並んだブロックは、境界として通り抜けられないようにつくられている。

そこで、斜めに並んだブロックは通り抜けられるルールとした場合のプログラムを記述せよ。

レポート提出

レポートでは、指定長を辺の組み合わせで作るテーマか、Flood-fill のテーマのいずれかにて、以下の内容をまとめてレポートとして提出すること。

    • レポートの説明(自分の選んだテーマと自分なりの説明)
    • プログラムリスト
    • 動作確認の結果

情報構造論の質問の回答

今日は、遠隔授業形式で初めての情報構造論を行った。

ペン書きを交えながらの説明はそれなりにうまくいったかな。

今日は、2重forループの処理時間の分析の説明を行ったけど、最後の説明は次回の授業への橋渡しで(To be continued…)的に終わらせたんだけど、積極的な学生さんからチャットで質問があったので、解説しちゃえ。

2重forループの処理時間の見積もり

内容は、以下のようなプログラムが、foo(100)で1msecかかったとして、foo(1000)は何秒かかる?

int foo( int n ) {
   int c = 0 ;
   for( int i = 0 ; i < n ; i++ )
      for( int j = 0 ; j < i ; j++ )
         c++ ;
   return c ;
}

勘がいい人なら、2重forループだし処理時間は に比例するから、が10倍なら処理時間は100倍なので 100msec と速攻で答えるかもしれない。でもここはもう少し複雑な見積もりの基礎を説明するのが目標ということで、もう少し厳密な説明を示す。

授業でも解説したが、このプログラムの処理時間は、以下の式で表せる。

よって、の条件で解けばいい…。

ただ、未知変数がと3つある癖に、与えられた式は1つだけ。本来なら、この方程式は解けない。

こういった処理速度を予測するといったニーズでは、通常Nの値は、ある程度大きな値。しかも、正確な時間を求めるのではなく、大まかな見積もりがしたいだけ。だとすると、 の項目は、 に比べて小さな値のはずなので、最初の方程式も以下の式で考えればいい。

そうなると、なので、となる。

よって、となる。

授業が終わってのアンケート、初めての遠隔授業だったけど、おべんちゃらも入ってるだろうけど「わかりやすかった」との言葉をもらえました。学生さんの感想の中には「先生の操作を近くの画面で見れるので、説明も理解しやすかった。トイレにいても授業に参加できます!」との楽しい💩感想ももらえました。👍

値渡しとポインタ渡し

前回はWeb提示資料と課題でガイダンスを行ったが、今日は遠隔授業形式での初回。前回のガイダンス資料を前半ざっと流し、後半はC言語をあまりやっていない学科の人向けのC言語の基礎。ただし、参照渡しについては電子情報の授業でも細かく扱っていない内容なので電子情報系学生の人はそこだけ注意してね。

オブジェクト指向のプログラムでは、構造体のポインタ渡しを多用するが、その基本となる関数との値の受け渡しの理解のため、以下に値渡し・ポインタ渡し・参照渡しについて説明する。

ポインタと引数

値渡し

// 値渡しのプログラム
void foo( int x ) {  // x は局所変数(仮引数は呼出時に
                     // 対応する実引数で初期化される。
   x++ ;
   printf( "%d¥n" , x ) ;
}
void main() {
   int a = 123 ;
   foo( a ) ;  // 124
               // 処理後も main::a は 123 のまま。
   foo( a ) ;  // 124
}

このプログラムでは、aの値は変化せずに、124,124 が表示される。
でも、プログラムによっては、124,125 と変化して欲しい場合もある。
どのように記述すべきだろうか?

// 大域変数を使う場合
int x ;
void foo() {
   x++ ;
   printf( "%d¥n" , x ) ;
}
void main() {
   x = 123 ;
   foo() ;  // 124
   foo() ;  // 125
}

しかし、このプログラムは大域変数を使うために、間違いを引き起こしやすい。

// 大域変数が原因で予想外の挙動をしめす簡単な例
int i ;
void foo() {
   for( i = 0 ; i < 2 ; i++ )
      printf( "A" ) ;
}
void main() {
   for( i = 0 ; i < 3 ; i++ )  // このプログラムでは、AA AA AA と
      foo() ;                   // 表示されない。
}

ポインタ渡し

C言語で引数を通して、呼び出し側の値を変化して欲しい場合は、変更して欲しい変数のアドレスを渡し、関数側では、ポインタ変数を使って受け取った変数のアドレスの示す場所の値を操作する。

// ポインタ渡しのプログラム
void foo( int* p ) {  // p はポインタ
   (*p)++ ;
   printf( "%d¥n" , *p ) ;
}
void main() {
   int a = 123 ;
   foo( &a ) ;  // 124
                // 処理後 main::a は 124 に増えている。
   foo( &a ) ;  // 124
}               // さらに125と増える。

ポインタを利用して引数に副作用を与える方法は、ポインタを正しく理解していないプログラマーでは、危険な操作となる。C++では、ポインタ渡しを使わないようにするために、参照渡しを利用する。

参照渡し

// ポインタ渡しのプログラム
void foo( int& x ) {  // xは参照
   x++ ;
   printf( "%d¥n" , x ) ;
}
void main() {
   int a = 123 ;
   foo( a ) ;  // 124
               // 処理後 main::a は 124 に増えている。
   foo( a ) ;  // 124
}              // さらに125と増える。

構造体のポインタ渡し

// 構造体のポインタ渡しのプログラム
struct Person {
   int name[ 20 ] ;
   int age ;
} ;
// 構造体にデータを代入するための関数
void set_Person( struct Person* p , char nm[] , int ag ) {
   // ポインタ参照で書くと以下の通り
   strcpy( (*p).name , nm ) ;
   (*p).age = ag ;
   // アロー演算子を使うとシンプルに書ける。
   // strcpy( p->name , nm ) ;
   // p->age = ag ;
}
// 構造体のデータを表示するための関数
void print_Person( struct Person* p ) {
   printf( "%s %d¥n" , p->name , p->age ) ;
}
// 関数名さえ処理の意図がつたわる名前を使えば、
// 値をセットして、表示する...ぐらいは一目瞭然。
// 構造体の中身を知らなくても、関数の中身を知らなくても、
// やりたいことは伝わる。...隠蔽化
void main() {
   struct Person tsaitoh ;
   // tsaitohに set して、
   set_Person( &tsaitoh , "t-saitoh" , 55 ) ;
   // tsaitohを print する。
   print_Person( &tsaitoh ) ;
}

nitfukui の名前取得

このコロナ騒動の中、Office365のID+PWは、本科-新1年生に配布できていない状態。公式の情報を伝えるのにも一苦労。その中で今回、校長のことばを伝えるために、YouTube を試験的に取り入れた。メディア研究室のチャネルを使うとかいう話もあったけど、新たに Gmail の nitfukui のアカウントをとったりしながら、開設となった。

こういったSNSなどで使う名前は、原則早い者勝ちが基本ルール。ただあまりにも広く知れ渡っているものは、あとで取り返すことができる。サイバースクワッティング

実際、Twitter の某アカウントは、福井高専のOBが取得していて、アイコンも校章で、投稿内容は学校のイメージダウンするような内容だったりする。

このため、サイバースクワッティング対策としては、早い者勝ちで取るしかない。

https://facebook.com/fukui.kosen とか、https://twitter.com/FukuiKousen などは、以前に私が取得し、非公式だけどそれなりに活用中。ただ、法人化になってからは、福井高専は、National Institute of Technology, fukui college なので、短縮名だと “nitfukui” だし、Twitter のアカウントで、 @nitfukui を取得しておきたい。

ちなみに、slack の nit-fukui.slack.com, nitfukui.slack.com は、私が取得してある。(つかってねーけど)

 

オブジェクト指向/2020/ガイダンス

専攻科2年のオブジェクト指向プログラミングの授業の1回目。コロナ対策でこのページを見て簡単な初回課題を提示。

シラバスは、ここに示すように、最近のプログラミングの基本となっているオブジェクト指向について、その機能についてC++言語を用いて説明し、後半では対象(オブジェクト)をモデル化して設計するための考え方(UML)について説明する。

評価は、3つの課題と最終テストを各25%づつで評価を行う。ただし、今回の感染予防の休み期間のレポート課題を別途含める。

オブジェクト指向プログラミングの歴史

最初のプログラム言語のFortran(科学技術計算向け)の頃は、処理を記述するだけだったけど、 COBOL(商用計算向け)ができた頃には、データをひとまとめで扱う「構造体」(C言語ならstruct …}の考えができた。(データの構造化)

// C言語の構造体
struct Person { // 1人分のデータ構造をPersonとする
   char name[ 20 ] ;             // 名前
   int  b_year, b_month, b_day ; // 誕生日
} ;

一方、初期のFortranでは、プログラムの処理順序は、繰り返し処理も if 文と goto 文で記載し、処理がわかりにくかった。その後のALGOLの頃には、処理をブロック化して扱うスタイル(C言語なら{ 文 … }の複文で 記述する方法ができてきた。(処理の構造化)

      // ブロックの考えがない時代の雰囲気をC言語で表すと
      int i = 0 ;
LOOP: if ( i >= 10 ) goto EXIT ;
         if ( i % 2 != 0 ) goto NEXT ;
            printf( "%d " , i ) ;
NEXT:    i++ ;
      goto LOOP ;
EXIT:
      // C 言語で書けば
      int i ;
      for( i = 0 ; i < 10 ; i++ ) {
         if ( i % 2 == 0 ) {
            printf( "%d¥n" , i ) ;
         }
      }

このデータの構造化・処理の構造化により、プログラムの分かりやすさは向上し、このデータと処理をブロック化した書き方は「構造化プログラミング(Structured Programming)」 と呼ばれる。

この後、様々なプログラム言語が開発され、C言語などもできてきた。 一方で、シミュレーションのプログラム開発(例simula)では、 シミュレーション対象(object)に対して、命令するスタイルの書き方が生まれ、 データに対して命令するという点で、擬人法のようなイメージで直感的にも分かりやすかった。 これがオブジェクト指向プログラミング(Object Oriented Programming)の始まりとなる。略記するときは OOP などと書くことが多い。

この考え方を導入した言語の1つが Smalltalk であり、この環境では、プログラムのエディタも Smalltalk で記述したりして、オブジェクト指向がGUIのプログラムと親和性が良いことから、この考え方は多くのプログラム言語へと取り入れられていく。

C言語にこのオブジェクト指向を取り入れ、C++が開発される。さらに、この文法をベースとした、 Javaなどが開発されている。最近の新しい言語では、どれもオブジェクト指向の考えが使われている。

構造体の導入

C++でのオブジェクト指向は、C言語の構造体の表記がベースになっているので、まずは構造体の説明。詳細な配布資料を以下に示す。

// 構造体の宣言
struct Person {      // Personが構造体につけた名前
   char name[ 20 ] ; // 要素1
   int  phone ;      // 要素2
} ;                  // 構造体定義とデータ構造宣言を
                     // 別に書く時は「;」の書き忘れに注意
// 構造体変数の宣言
struct Person saitoh ;
struct Person data[ 10 ] ;
// 実際にデータを参照 構造体変数.要素名
strcpy( saitoh.name , "t-saitoh" ) ;
saitoh.phone = 272925 ;
for( int i = 0 ; i < 10 ; i++ ) {
   scanf( "%d%s" , data[ i ].name , &(data[ i ].phone) ) ;
}

感染予防による休講期間中の課題

以下の3つについてレポートにまとめ、ここに示すURLのファイル共有に提出せよ。Office365の接続がうまくいかない場合は、メールにてレポートを提出でも良い。

  1. 今まで、プログラムを作成する際に、わかりやすいプログラムを作成するために、自分自身がどのような考え方をとっていたか(工夫していたか)、考え方を10行程度に考えをまとめること。
  2. オブジェクト指向の歴史に関連する以下のワードの中から、2つを選んでインターネットをつかって調べ、自分なりの言葉でまとめ、簡単に説明せよ。
    • Fortran90, ALGOL, Smalltalk80, 入れ子とインデント, Dijkstraのgoto文有害論, プログラミングパラダイム
  3. 以下に、C言語の構造体を使った基本的なプログラムを示す。このプログラムでは、国語,算数,理科の3科目と名前の5人分のデータより、各人の平均点を計算している。このプログラムを動かし、以下の機能を追加せよ。レポートには プログラムリストと動作結果の分かる結果を付けること。
    • 国語の最低点の人を探し、名前を表示する処理。
    • 算数の平均点を求める処理。
#include <stdio.h>

struct Student {
  char name[ 20 ] ;
  int  kokugo ;
  int  sansu ;
  int  rika ;
} ;

struct Student table[5] = {
  // name ,      kokugo , sansu , rika                                          
  { "Aoyama" ,   56 ,     95 ,    83 } ,
  { "Kondoh" ,   78 ,     80 ,    64 } ,
  { "Saitoh" ,   42 ,     78 ,    88 } ,
  { "Sakamoto" , 85 ,     90 ,    36 } ,
  { "Yamagoshi",100 ,     72 ,    65 } ,
} ;

int main() {
  int i = 0 ;
  for( i = 0 ; i < 5 ; i++ ) {
    double sum = table[i].kokugo + table[i].sansu + table[i].rika ;
    printf( "%-10.10s %3d %3d %3d %6.2lf\n" ,
            table[i].name , table[i].kokugo , table[i].sansu , table[i].rika ,
            sum / 3.0 ) ;
  }
  return 0 ;
}

制御構文の理解

コロナ対策で、4年向けの情報構造論も始められないので、自習用資料。

制御構文の理解

基本として、C言語の制御構文の意味をフローチャートで表すと以下のようになる。

条件分岐if

繰り返しwhile, for, do-while

繰り返し命令の中では、break文でループを抜ける、continue文で次の繰り返し先頭に飛ぶ。フローチャートで表すと、上記の図中の赤、青の部分。

条件分岐switch

条件分岐のswitch文は、以下のようなフローチャートで示すことができる。
ただしcase の後には定数式しか書けない。 一般的には、分岐処理のAAA;BBB;CCC;の後ろには、break を書くのが一般的。繰り返し処理の中にswitch文があった場合、break命令では、switch文を抜けるだけで、ループを抜け出す訳ではない。

もし、breakを書き忘れると、以下のようなフローチャートになるので要注意。

このフローチャートを見てわかるように、breakが無いと、X=Bの時、処理BBB,CCCが実行することになる。

文の定義

C言語では、とは(大雑把に言うと)以下の定義である。複文の所が要注意。

for()の後には1つの文が書ける。単純な計算式であれば、式;で1つの文なので、{,} は本来不要。forの中で複数の処理を書きたい時は、{ 文 文 … } のように複文で1つの文の塊をつくる。

((( C言語の文 )))

// 式 : 計算式が ; で終わっているもの
式 ;

// if文
if ( 式 ) 文
else      文   // else以下略あり

// while文   
while( 式 ) 文
// for文
for( 式 ; 式 ; 式 ) 文
// do-while文
do 文 while( 式 ) ;   // セミコロンまででdo-while文

// 複文
{ 文 文... }   // 複数の文を1つの文として扱う
// 空文
;             // for(;;); これも文法としては正しい

例題

上記の制御構文の意味を踏まえた上で、過去のテスト問題より具体例で説明。

以下のプログラムの動作順序を(A)〜(M)の記号で答えよ。
答えは20ステップ目までで良い。

前述の文の定義を踏まえ、前述の問題の中では、以下の様な命令の塊(ブロック)が存在する。

ここで、水色部分(c)の if 文の break は{,} は不要なのか?という質問をする人が多いけど、if(式) { 文 } と書いても、if(式) 文 でも、文の部分に複文を使うか使わないかの違いにすぎない。

これを踏まえて、フローチャートを書くと以下の通り。

よって、この問題の回答は、以下のようになる。

A(i=0)→ B(i==0)D(j=0) E(j==0)G→H→   F→
E(j==1)G→H→I(break)
J(j=0) K(j==0)M→L
K(j==1) C(i=1)
B(i==1)D(j=0) E(j==0)G→H→   F→
E(j==1)G→H→I(break)
J(j=0) K(j==0)M→L
K(j==1)M→L
K(j==2) C(i=2)
B(i==2)

第2回レポート課題

(1) 以下の rep1-1〜rep1-4 の中から1つを選び、処理順序の結果を答えよ。rep-1-(出席番号%4+1)を答えること。(例:出席番号10番の人はrep1-3)

rep-1-1

rep-1-2

rep-1-3

rep-1-4

(2) 以下の foo(n) , bar(n) の関数で、プログラムの(a)〜(i)の計算式の1命令の実行に10[nsec]とする。(それ以外の実行時間は無視して良い)

引数が n=2 , n=4 の場合、各関数foo(n),bar(n)の返り値 cnt の値、foo(n),bar(n) の実行にかかる時間を答えよ。ただし、答えた理由がわかるような情報をつけること。

例えば、命令に実行回数を数えるためのチェックマークの数がわかる資料でも良い。

nの値 foo(n) bar(n)
cnt 処理時間 cnt 処理時間
n=2
n=4

(3) n=1024 の時、foo(n),bar(n)はどんな値になると思いますか?

どのように考えて計算すればいいか、考え方を提案してください。

レポート課題は、上記(1),(2),(3)を簡単に(A4×1〜2ページ)まとめ、この共有フォルダに提出して下さい。提出するファイル名は、出席番号-名前-レポート名.拡張子としてください。提出締め切りは、(2020/04/24までとする)

2020年度情報構造論ガイダンス

基本的なガイダンス

情報構造論のシラバスを、ここに示す。プログラムを作成する上で、どのような考え方で作れば処理速度が速いのかを議論する。基本的に、4回のテストのたびに、レポート課題を実施する。各テスト毎の評価は、テスト素点と、「テスト素点×60%+レポート評価×40%」の良い方とする。テストに自信のない人は、レポート課題をきちんと提出すること。ただし、今後の休講などの影響で評価方法は随時変更を行う。

プログラムを評価する3つのポイント

まずは以下を読む前に、質問。

  • あなたが良いプログラムを作るために何を考えて作りますか? ※1
    • ここまでの段階で3つの要点を考えメモしておいてください。
    • ガイダンス最初のレポートに使います。

具体的な言葉で要点を考えると、いろいろなものがでてくるだろうが、端的なポイントにまとめると、次の3つに分類できるはずである。

  • プログラムの速度
  • プログラムのわかり易さ
  • メモリの使用量

プログラムを作る場合、この3要素がトレードオフの関係にある。プログラムの速度を優先すると、プログラムが分かり難くなったり、メモリを大量浪費するものだったりする。

メモリの使用量の影響

メモリを大量に使用すると、どういった影響がでるのか? OSの機能を知らないと、メモリ(主記憶)を使い果たしたら、プログラムが動かないと思うかもしれないけど、最近のOSは仮想メモリ機能があるため、主記憶がメモリが足りなければ待機状態のプロセスのメモリを補助記憶に保存することで、プログラムを動かすことはできる。(仮想記憶)

しかし、プロセスが切り替わる度に、補助記憶への読み書きが発生するため、処理性能は低下する。(スワッピング)

ソフトウェアとアルゴリズムとプログラム

用語として、ソフトウェア、アルゴリズム、プログラムという表現があるが、この違いは何か?

  • アルゴリズム – 計算手順の考え方。
  • プログラム – アルゴリズムを特定のプログラム言語によって記述したもの。
  • ソフトウェア – プログラムと、その処理に必要なデータ。(日本語を変換するプログラムは、日本語の辞書データが無いと動かない)
  • パラダイム – プログラムをどう表現すると分かりやすいか?

トレードオフ関係をプログラムで確認

例えば、配列の中から、目的データを探すプログラムの場合、最も簡単なプログラムは以下の方法であろう。

// ((case-1))
// 単純サーチ O(N)
#define SIZE 1024
int a[ SIZE ] ; // 配列
int size ;      // 実際のデータ数(Nとする)
int key ;       // 探すデータ
for( int i = 0 ; i < size ; i++ )
   if ( a[i] == key )
      break ;

しかし、もっと早く探したいのであれば、2分探索法を用いるだろう。でも、このプログラムは、case-1 のプログラムよりは分かり難い。(速度⇔わかり易さ)

// ((case-2))
// 2分探索法
int L=0 , R=size ; // プログラムは複雑になった 
while( L != R ) {
   int M = (L + R) / 2 ;
   if ( a[M] == key )
      break ;
   else if ( a[M] < key )
      L = M + 1 ;
   else
      R = M ;
}

でももっと速いプログラムとしたければ、大量のメモリを使えば一発でデータを探せる。(速度⇔メモリ使用量)

// ((case-3))
// 添字がデータ O(1)
// 探すデータが電話番号 272925 のような 6 桁ならば
int a[ 1000000 ] ;
a[ 272925 ] = 272925 ;
// 処理速度はクソ速いけど、メモリは大量消費

良いプログラムを作るとは

プログラムを作る時には、メモリが大量に使えるのなら、速いものを使えばいい。だけど実際には、そのシステムには限られた予算があるだろう。

実際には、限られた予算からメモリやCPUが決まり、その会社の人員やら経験やらでプログラム開発に使える時間がきまる。プログラムをデザインするとは、限られた条件の中で、適切な速度のコンピュータ、適切な量のメモリでコンピュータを用意し、限られた納期の中でシステムを完成させることである。

皆さんも、ゲームを買った時、処理速度が遅くてキャラクターがカクカク動いたら幻滅するでしょ?ゲームがバグですぐに変な動きしたらキレるでしょ!発売日の予定どおりに買えなかったらさみしいでしょ!!プログラムがでかすぎてローディングに時間がかかったら、寝ちゃうでしょ!!!

第1回課題

  • 最初の※1で、あなたが考えた「良いプログラムを作るために考えること」を3つあげてください。
  • あなたが考えた3つは、それぞれ、「速度わかりやすさメモリの使用量」のどれに関係すると思いますか?(複数に関連する場合もあります)
  • 仮想メモリスワッピングプログラミング・パラダイムの用語の中から1つを選び、意味を調べて簡単にまとめてください。

この3つの内容を簡単に(A4×1ページ)まとめ、この共有フォルダに提出して下さい。提出するファイル名は、出席番号-名前-レポート名.拡張子 (例 01-愛上男-第1回課題.pdf) としてください。提出締め切りは、(2020/04/17までとする)

(2020/04/10,13:40) 当初のフォルダが書き込み禁止状態だったので、提出場所を変更、書き込み許可を与えたので改めて提出をお願いします。

サーヴェイとは(FD研修会)

発想法の指導に関するFD研修会

サーヴェイ

はじめに

  • 論理立てて思考する起点を見出すのがサーヴェイ
  • デザインの作ろうとする物事の仕組みや考えを論理立てて説明出来るための要
  • 自身が納得できて、説得するためではなく説明の基礎

アイデア・イメージメモ

  • 考えの写真・図・イラスト
  • コメント1(思いつき、気付き、疑問、不、好感、利点など)
  • コメント2(提案、発展、課題など)
  • その他

Surveyとは、

  • SurveyとResearchの違い
    • Research…研究・調査
      • 研究する、調査する
    • Survey …調査、探査、測量、実地踏査
      • 見渡す、概観する
    • field work …実地研究、野外調査、実地調査、現地調査、巡見
  • 探検・発見

ResearchとSurveyの相違

  • リサーチ
    • 準備して調べること
      • 縛りが強い、決め事がある
      • 客観的
      • 設定
      • 定数的
      • やや論理的
      • ロジカル、裏付け的
  • サーヴェイ
    • 特に準備なく体感すること
      • 縛りがゆるやか、決め事はない
      • 主観的
      • 想定
      • 定性的
      • 感覚的
      • エモーショナル、曖昧さがあっていい

Surveyでの留意点

  • サーヴェイは、情報・データを得る方法の1つ
  • 問題を発見する方法
  • ポイント
    • 先入観を持たずに概観(常識や固定観念をはずす)
      • 自分の経験や知識を一度仕舞い込む
      • 常識や規則、良識に囚われていないかと自分に問う
    • 自分の五感+αに素直(感じや思いを表出する)
      • 感じるがまま、思うがままを表現
      • 幼少時の感覚を思い起こす
        • 純真な子供に成り代わって素直に対象と接する
    • 気付き、思いつきをメモ
      • 記号化 – 話し言葉、書き言葉、絵、写真、図など「うつし」
        • 取捨選択をしない
        • あまり深く考えない
        • 結論や解を求めない
        • 評価しない
  • 気になることがない…という場合には
    • アイデアイメージを書けない理由
      • こんなことはできない、不可能だから言わない、提案しても採用されない…
    • 曼荼羅法→根拠理由を考えて再度行う
      • なぜ書けないを曼荼羅法で気づかせる
    • なぜ、どうしてと問わない。関わり、関係を考えない

Surveyからメモ作成での姿勢と方向

  • 対象の概観(観察)
  • 時間を区切る
    • (5) / 15 / (30) / 45 / 90 分
    • 5分でまとめる(連想法を5分で…)
    • 30分以上同じことをさせない
  • 自他の評価を考えない・行わない
  • 対象の良い点も悪い点も見つける
    • 曼荼羅法、
      • 3×3の表
      • 真ん中にテーマと気付き
      • その周り8コマにアイデア(上下左右にプラスマイナス)
    • プラスマイナスカード法、
    • Brain Writing法、
    • Brain Storming法(希望点列挙法,欠点列挙法)
  • 素直に問う
    • 気になることを「何だろう」「どうしてかな」と自問する
  • 話す・見せる
  • ▶︎サーヴェイによるデータ・資料作成のプロセスとなる

サーヴェイの位置付け・必要性・実施の意味

  • サーヴェイ
    • 問題の発見 … 問う「なぜ?」「どうして?」と疑問に思うこと
    • 問題の探索 … さぐる、さがす
    • 問題の把握 … 全貌と細部(ディテール、詳細) → 現状把握・確認
  • 「発想のネタ」、「創造のいとぐち」を見出すためのプロセスの最初の行為であり思考
  • サーヴェイと問題
    • 発見、把握、探索

サーヴェイの発散・収束の思考

  • イメージからアイデアに
    • 探索→発見→把握→探索…のスパイラルアップ
  • 感覚的から論理的に
  • 関係や関わりも探り考える(空間・場・時間・機会・遊・休・知・美・感・交・操・演)

発想を出す時のテクニック

  • Surveyする時の写真は、モノクロの方がいい
    • 余計な情報が先入観になってしまう
  • 写真にマーカーを入れる
    • マーク付けした所をもとに議論

 

ネットワークセキュリティのためのTips

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー