PowerPoint

アルゴリズムとデータ構造
講義スライド 1





イントロダクション
基礎的知識
ウォーミングアップ
アルゴリズムの計算量
数値計算
講義はプロジェクタを利用して行います.見にくい学生はスクリー
ンに近い座席に移動してください.
茨城大学 工学部 知能システム工学科 井上 康介
E2棟801号室
担当教員自己紹介
 井上 康介(いのうえ こうすけ)
 身分:講師,居室:E2棟 8階801号室
 専門分野:生物模倣型ロボット(現在は主にヘビ型ロボッ
トを扱っています)
■
2
授業の注意点
 次回以降,出席をとる (代返対策します)
 2回に1回程度宿題を出します.
 指定教科書を参照しながら進めるので,教科書を入手し,
毎回の講義に持ってきてください.
 成績評価については「宿題+中間+期末」で行う予定 (比
率は1:1:1の予定)
 授業中・時間外を問わず,質問することを躊躇しないこ
と! よく分からないままドロップアウトしないように.
 ぼーっと聞いていると簡単に脱落する 気がするので,ある
程度の気合と集中力を要するかもしれません.
 井上は教えるのが下手糞です.必死で分かろうとしている
人以外も自然に分かってしまうような講義には…ならない
と思います.無理矢理にでも食いつかないと厳しいです.
3
授業の進め方
基本的に,教科書に沿って進める.
数値計算については,軽くふれる程度.
ソートとサーチはきっちりやる.
再帰,データ構造,ツリー,グラフと進む.
グラフィック,パズル・ゲームは扱わない.
実際にコンピュータを使った実習ができるのが理想だが…
時間的にムリなのでやらない.
 講義は PowerPoint を使って行う.つまり,全てノートに
取ることはムリ.スライドは全て,ホームページ上に掲載
する.
 教科書上に重要なポイントをメモすること.
 用語がいろいろと出てくる.中間・期末テストの1問目は,
こういった用語・概念の意味などを問う問題を出題する.
配点多め.ホームページ上に過去問あり.






4
この科目の位置づけ
 プログラミング言語の,いわば「作法」を学ぶのが プログ
ラミング演習 I・II.
 高度なプログラムを作成してコンピュータに複雑な処理を
行わせるのが 知的情報処理 I・II およびその演習.
 その間に入るのがこの講義である.
 プログラミング言語の「書き方」だけがわかっていても,
いざ具体的にコンピュータに特定の問題を解かせるとき,
どのような考え方でコードを書けばよいか は分からない.
ex) 最大公約数を求めるプログラムをどう書く?
 コンピュータの内部的な仕組みも踏まえ,どのような戦略
でプログラムの構造を作っていくか,その基礎的な考え方
を訓練するのがこの講義であり,その意味では極めて重要
(配属研究室や就職先企業によっては,マジで重要です).
5
この科目を履修すべきか?
 本学科の本来の趣旨は,「メカと情報の接合分野を取り扱
うことのできるエンジニアの育成」であり,自動車やエア
コンその他,現在生産される多くの工業製品の設計・生産
において重要な領域(例えば,組み込み系 エンジニア)
 したがって,情報についての常識を有しておくことは,上
記の趣旨に即したエンジニアになるためには 必須
 …と,いいつつも,どうにもプログラミングに興味がわか
ず,そこを避けて生きていきたい学生もいると思われます
 「プログラミングがどうにもこうにも理解できない」とい
う学生が,この科目で単位を取得するのは厳しい可能性が
ある,ということだけ,お伝えしておきます
 「アルゴリズムとデータ構造の基礎を習得した」とみなし
にくい学生に無理に単位を与えることは,しません
6
C言語の習得について
 知能システム工学科の養成しようとするエンジニア,メカ
と情報の融合分野を扱うエンジニアになろうとするとき,
「プログラムはからっきし」みたいな状態は許されない.
 現時点でプログラミング演習の講義でドロップアウト気味
の学生は,なんとか努力して,習得しなおすべきである.
 本講義では,プログラミング演習は習得していることを前
提として,ソース・コードを参照しながら講義を進める.
 各回で出てくるソース・コードの中に理解できない部分が
一つでも生じている場合,放置してはならない.プログラ
ミング演習の資料を復習する,井上や友達に質問するなど
して,必ずつぶしておくこと.
ガイダンスは以上.質問があればしてください
7
授業で用いる資料
 指定教科書:必携 (講義に必ず持ってきてください)
C言語によるはじめてのアルゴリズム
入門 改訂第3版
加西 朝雄 著
技術評論社
定価 2,780円+消費税 = 3,002円
 授業で見せるスライドは,ホームページ上にアップロード
してあります.以下のURLをメモること.
http://biorobot2.ise.ibaraki.ac.jp/inoue/lecture/ad/
8
今日の内容





講義ガイダンス (完了)
「プログラム」に関する基礎的事項
アルゴリズム・データ構造とは何か
授業の目的 (いいプログラマを目指す)
アルゴリズムの評価方法 (一般論)
9
コンピュータに仕事をさせるには?
コンピュータ:「電子計算機」 つまり「計算」をする
計算とは?  今のコンピュータにおいては「データの処理」
例)住所録データから特定の人のデータを探す
例)Mpegムービーを再生する
例)ホームページを表示する
例)入力された数値データ255と105から,最大公約数
__15 を求めて表示する
ではこのような仕事をさせるためにどうすればよいか?
プログラム (手順書) を書いて渡してやる
10
大問題:コンピュータは賢くない!
 仕事:入力した2数の最大公約数を求めよ
 入力:225,105
経験的直感
人間がやる場合…
まずは5だ
5
225
105
3
45
21
15
7
3で割れる
互いに素だ
答)5×3=15
コンピュータには経験的直感や常識は一切ない!
詳細に指定された手順を正確・高速になぞるだけ
11
どうすればいい?
 人間が常識や直感に頼って行うような作業手順 を 詳細に
指定した手順書 に変換して渡してやる
 手順書とは プログラム であり, 手順書を作るのは プログ
ラマである
ということは…
 手順書に曖昧さ・漠然性があってはならない
 常識や直感を要する指示はできない
 どのデータをどのように処理するか,一切の曖昧さ
を含まないように事細かにきちんと指定してやる
例)「最大公約数を求めろ」という指示を与えると…
→「え…サイダイコウヤクスウって何?」
「なんか数字データが2つ来たけど,どうすれば…?」
12
手順書作成 (プログラミング) の例
 仕事:「最大公約数を求めよ」
 直感には頼れない
 機械的操作の繰り返しなどを考える
ユークリッドの互除法
2整数 m, n (m>n) があるとき, m と n の最大公約数は
m-n と n との最大公約数に等しい
手順
1. m ≠ n の間 2. を繰り返せ.m = n になったら 3. へ
2. m > n なら m := m-n , m < n なら n := n-m
3. m (=n) が求められる最大公約数
13
実行過程
手順
1. m ≠ n の間 2. を繰り返せ.m = n になったら 3. へ
2. m > n なら m := m-n , m < n なら n := n-m
3. m (=n) が求められる最大公約数
m
n
24 18
24-18
6 18
18-6
6 12
12-6
6
6
OK!
機械的操作の繰り返しによって
最大公約数が求まった
この機械的な手順をプログラム
に変換すればよい
14
手順書 (プログラム) の例 (p. 38)
#inculude <stdio.h>
プリプロセッサ (コンパイラへの指示)
void main(void) main関数
{
int
ちょっと確認
このプログラム,読めますね?
a, b, m, n; 変数の宣言
printf("Input two integers: "); 入力処理
scanf("%d %d", &a, &b);
}
m = a; n = b; 変数 m, n の初期化
while(m != n) { ループ (m != n の間繰り返す)
if (m > n)
m = m - n; 大きい方の変数を
else
n = n - m; 差の値に置き換える
}
printf("The G.C.D. is %d\n", m); 出力処理
15
プログラム作成に必要なもの
 プログラムでは,作業の仕方を詳細に指定する
 考えなくてはならない項目は 2つ
 まず,データをどのように処理するかを指定する.
 アルゴリズム
 次に,処理すべきデータや処理されたデータをコンピュー
タ上でどのように入出力したり保持したりするかを指定す
る.
 データ構造
16
アルゴリズムとは?
 アルゴリズム:コンピュータに行わせる手順のこと
→ 先程の例では,「ユークリッドの互除法」の手順
 アルゴリズムの実装:プログラミング言語の利用
例) C,C++,Java,VisualBasic, Fortran…
 実装に当たっては,言語特有の制御構造などを使っ
て,手順を言語において定められた文法で書く
制御構造
 順次実行 (基本は上から下へ)
 条件分岐 (if文,switch文など)
 繰り返し (for文,while文など)
17
余談:勉強すべきプログラミング言語
C
 幅広い実用,多くのプログラミング言語への影響大,デバ
イスドライバ開発や組み込み系などハードウェアに近いプ
ログラムにも有用性大 (つまり知能システム工学科的)
 とにかく最初にこれを学ぶべき.研究室の多くはこの
習得を前提としている.
C++
 C言語にオブジェクト指向という概念を組み込んだもの.
企業における大規模プログラムの共同開発で広く実用.
 学ぶ価値は大.多くのエンジニアがメインに使ってい
る.ただし,難易度高めなので,軽い気持ちで手を出さな
い方が良い.Cを十分理解してから.
18
余談:勉強すべきプログラミング言語
Java
 企業で広く利用.多様なプラットフォーム上で動く移植性
の高いプログラムに適している.動作は C や C++ に比
べて遅く,もっさりしている.若干古い.
 知っておく価値はある.C言語理解の後で学ぶべき.
Perl, PHP, Python, Javascript
 Web上で動くウェブアプリケーションの言語.
 商業的にはウェブアプリケーションもそれなりの規模
があることを考えれば,学ぶ価値は 0 ではない.わりと簡
単に理解できる.一方,ネットとかスマホとかのビジネス
に興味ない人は知らなくて良いし,必要が生じてからでも
十分簡単に理解できる.(C言語が一通り分かってれば)
19
余談:勉強すべきプログラミング言語
C#, VisualBasic
 .NETフレームワークというMicrosoftの特殊な環境上で動
くプログラムを開発.なんかXbox系ゲームを作るには必
要らしい.
 学生の時点で修得する価値はほぼない.内定先の企業
が要求してきたときとかに勉強すれば良い.
閑話休題,プログラムの基本要素の2つ目の話に戻る
20
データ構造とは?
 計算とは,「データの処理」であった.
 データをどのように処理するか,その手順であるアルゴリ
ズムとは別に,データをどのように管理するかを規定する
必要がある.
 データ構造:データの管理方法
 データをどのような形式で扱うか (型など)
 データをどのように保存するか (配列?ツリー?)
21
ユークリッドの互除法では?
#inculude <stdio.h>
void main(void)
{
int a, b, m, n;
 この部分がデータ構造に関わる
printf("Input
two integers:
");nを使うよ」と宣言
「int型の4つの変数
a, b, m,
scanf("%d %d", &a, &b);
m = a; n = b;
コンピュータのメモリ空間上に,int型のサイズ
while(m
!= n) {
if (m のメモリ領域が4つ確保され,
> n)
m = m - n;
(4バイト)
変数の
n = n - m;
名前else
(a,b,m,n) との対応付けがなされる.
}
printf("The G.C.D. is %d\n", m);
}
具体的には何が起こっているのか?
22
データの管理
 一般的なコンピュータ
は数ギガバイトの大容
量のメモリ (記憶領域)
を持っている.
 メモリの空間上には位
置を示すアドレスがつ
けられている.
 単位は1バイト.
 アドレスはふつう,16
進数で表記される.
 メモリは様々なプログ
ラムが共有している.
メモリ空間
0x00000000番地
Windows
Visual Studio
Outlook
0x3FFFFFFF番地
23
データの管理
 ユーザプログラム (み
なさんが作るプログ
ラム) は,空いている
領域を使う.
 まず,プログラムを
起動するとプログラ
ム自体がメモリ空間
上の空き領域にロー
ドされる.
 さらに,プログラム
で使うデータの領域
も必要に応じて確保
される.
メモリ空間
0x00000000番地
Windows
Visual Studio
program
Outlook
a
b
0x3FFFFFFF番地
m
n
24
データの管理
 先程の例のプログラムの
場合,int型の変数
a,b,m,nを使う.
 int型は4バイトなので,
メモリ空間上で4バイト
の領域を4つ確保.
 プログラム側では,変数
名 (例えば「a」)と,そ
のデータの格納アドレス
の対応を保持する.
 (よって,プログラマはア
ドレスを直接意識しなく
てよい.)
メモリ空間
0x00000000番地
Windows
Visual Studio
program
4バイト
Outlook
a
b
変数 a をしまった場所
は,0x25A88000番地
だったな.
0x3FFFFFFF番地
m
n
25
大量・複雑なデータの扱い
 大量・複雑なデータに対しては,データの構造化・組織化
が必要となる
 データの構造化:構造体 (,共用体)
 データの組織化:配列,スタック,キュー,ツリー,グラ
フなど
例) 住所録を作りたい場合
個人データ (名前,住所,…) を,inoue_tel,
inoue_addrなどと個別に変数にするか?
→ 井上のデータは構造体 inoue_data にまとめる
井上のデータを inoue_data,佐藤のデータを
sato_data などとするか?
→ 配列などを使ってすっきりと記述
26
ここでC言語の勉強:構造体とは
 本講義では,より実践的な学習を目指して,ソース・コー
ドに即した形でアルゴリズム・データ構造を学ぶ.
 特に,データ構造を学ぶに当たっては,構造体 および ポ
インタ の知識が 必須 である.
 そして,一般的に,C言語の習得に当たってこの2つの事
項は最も理解が難しい鬼門と呼ばれる.逆に言えば,これ
らが分かって初めて,C言語が分かったとも言える.
 「プログラミング演習」におけるこれら 2項目の扱いは,
習う時期や詳しさにおいて,本講義には必ずしも対応でき
ていない可能性がある.
 そこで,今回は「構造体」についてこちらの講義で導入を
行う.「ポインタ」においても,初出のタイミングで導入
を行う.
27
構造体とは
 例えば住所録プログラムを作成することを考える.
 扱うべきデータは「個人データ」であり,一人一人の個人
データには,氏名,性別,年齢,住所,電話番号などの 要
素データ が含まれなくてはならない.
 それぞれの要素的データを個別の変数として扱うのではな
く,一人ごとに関連データをバインダにまとめるようにし
てまとめられると都合がよい.
 そのバインダの働きをするものが,C言語における 構造体
である.
28
構造体の使い方
 まず,構造体 (バインダ) の形式を定義しておく.
 person 構造体
struct person {
char name[256];
char tel[64];
char address[256];
};
 中かっこの中に要素的変数を
羅列して宣言する.
 上の宣言により,person という名前の構造体が定義され
る.person 構造体には,文字列 name,文字列 tel,お
よび 文字列 address という要素データが含まれることと
なった.
 これらの要素的変数を メンバ変数 という.
29
構造体の使い方
 次に,定義されたバインダ形式で個人データの変数を作る
には,以下のようにすればよい.
struct person {
char name[256];
char tel[64];
char address[256];
};
struct person inoue_data;
 person 構造体の変数
inoue_data を宣言
 これにより,例えば井上の住所を inoue_address など
と個別の変数にする必要はなくなって,井上の個人データ
を1つの変数 inoue_data の中にまとめて宣言できる.
30
構造体の使い方
struct person {
char name[256];
char tel[64];
char address[256];
};
struct person inoue_data;
 メモリ空間上では,構造体は1まとま
りのブロックとして作成される.
メモリ空間上では
inoue_data
name
tel
address
31
構造体のメンバの参照
struct person {
char name[256];
char tel[64];
char address[256];
};
ドット (.) をつける
struct person inoue_data;
 構造体のメンバへの参照は以下のように行う.
printf(“TEL of Inoue is %s\n, inoue_data.tel);
 井上の電話番号を表示
scanf(“%s”, inoue_data.address);
 井上の住所をユーザがキーボード入力
(ここに “&” がいらない理由は?)
32
データの組織化
 先程定義した person構造体の変数をたくさん使って住所
録プログラムを作ることを考える.
struct person
arai, inoue, sato1, sato2, wada;
こう宣言すれば,新井さん,井上さん,佐藤さん2名,
和田さんの5人のデータの変数が定義できるが…
でも住所録とは,不特定多数の人を登録するもの…
人数も名前も事前には分からない!
 そこで,データをうまく組織化することを考える.
組織化の方法:配列,リスト,ツリー,グラフ…
33
配列による組織化
メモリ空間上では
#include <stdio.h>
struct person { 構造体定義
char name[256];
char tel[64];
char address[256];
};
data[0]
void main(void) {
struct person
data[2]
name
tel
address
data[1]
data[4]; 変数の宣言
“Kousuke Inoue”
sprintf(data[2].name,
data[3]
変数への書込み
"%s",
"Kousuke Inoue");
}
34
文字列とポインタ
struct person {
char name[256];
char tel[64];
char address[256];
};
inoue_data
name
tel
struct person inoue_data;
address
 実は文字列の実体は構造体の外部にあ
り,構造体には配列の先頭アドレス
(ポインタ) が記入される.
(つまり inoue_data.name はポインタ)
 理解できない学生は,プログラミング
演習を必死で復習すること.
‘K’
‘o’
‘u’
‘s’
35
ツリーによる組織化
 階層的なデータにはツリー構造 (木構造) が適する
例) 茨城大学の組織のデータを表現するには?
茨城大学
人文学部
情報工学科
理学部
知能システム工学科
工学部
メディア通信工学科
これを構造体として,教員リスト,
学生数などのデータを格納
36
授業の目的
 プログラム = アルゴリズム + データ構造
 特定の問題をコンピュータに処理させるには,手順 (アル
ゴリズム) とデータの扱い方 (データ構造) を細かく指定
してやる必要がある → プログラミング
 同じ作業をするにも,いろんなプログラムがあり得る.
 プログラムを上手に書く能力を身につけるには?
 典型的なアルゴリズム・データ構造を学ぶ
 アルゴリズムの良し悪しの評価方法を学ぶ
これにより,問題に応じて適切なアルゴリズム・データ
構造を選択し,上手なプログラムを作成する能力を養う
 これがこの授業の目的
37
プログラムの評価 (一般論)
信頼性:精度の良い正しい結果を出す (当たり前)
効率性:計算回数が少なく,処理スピードが速い
一般性:特定の状況だけでなく,多様な状況に対応
拡張性:仕様変更に対して簡単に修正可能
(cf. デス・マーチ)
 可読性:他の人にも読みやすくして,メンテナンスを__
__容易にする
 移植性:他のOSなどにも移植が容易にできる




これらの評価基準において良質なプログラムを書く人が
良いプログラマである.
この授業では,特に効率性について詳細な評価方法を学ぶ
38
質問対応の時間
 ガイダンスということで,今日はここまで.
 本講義では,授業終了後に質問対応の時間をとります.
また,その時間では都合が悪いという場合は,メール等で
時間を決めて,その時間に質問に来てください.
 メアド:
(メモを)
 どの講義においても,「分からないまま放置」という戦略
は最低であるということを認識してください.
39
第1章 ウォーミングアップ…の前に
 必要な基礎知識を導入しておく.
 データ型
 オーバーフロー
 プリプロセッサ
40
データ型とは (復習)
種別
符号 ビット長 表現
範囲
整数
あり 8
(signed) char
-128~+127
16
(signed) short
-32768~+32767
32
(signed) long
-2147483648~+2147483647
unsigned char
0~255
16
unsigned short
0~65535
32
unsigned long
0~4294967295
float
約10-38~10+38 (10進数7桁)
double
約10-306~10+306 (10進数16桁)
なし 8
浮動小 あり 32
数点数
64
41
数データの実体 (整数)
 以下のようにビットを利用して表現している
unsigned short型 各ビットに0か1を入れ2進数を表現
16ビット=2バイト
15 14
3210
例)0000000000101001
→ 25+23+20 = 41
範囲:0 ~ 216-1 = 65535まで
(signed) short型 符号ビットを除いた15ビット分を
16ビット=2バイト
正負両側に割り振る
範囲:-215 = -32768 から
215-1 = 32767 まで
符号ビット (0:正,1:負)
42
数データの実体 (整数)
 符号付き整数型での負の数でのビットの扱い
例えば (signed) char型の場合…
127
126
…
1
0
-1
-2
…
-127
-128
01111111
01111110
…
00000001
00000000
11111111
11111110
…
10000001
10000000
つまり,符号ビット以外の7
桁での大小関係が保たれてい
る
43
数データの実体 (浮動小数点数)
 浮動小数点数のビット表現は以下の通り
±1.[仮数部]×2[指数部]
※ 0は全てのビットが0の場合とする
4バイト
8ビット
23ビット
float型
………
指数部
符号ビット
double型
11ビット
仮数部
8バイト
52ビット
………
指数部
符号ビット
仮数部
44
int 型とは何か?
 実際には,int 型として宣言した変数は,CPU のアーキ
テクチャやコンパイラに依存した別の整数型として扱われ
る (それぞれの環境において最も自然な整数型)
 通常は long 型として扱われるが,マイコンなどでは
short 型で扱われることも多い (教科書では short 型)
 通常,計算のスピード的には int 型を素直に使うのが速
いことが多い (が,通常は問題になるほどではない).
 一方で「どの環境でも確実に動くプログラムを作る」こと
を考慮するのであれば,int 型を使うべきではない かも
しれない.
45
オーバーフローとは何か
 既に述べたように,数のデータにおいては,型に応じて扱
いうる数値の範囲がある
 例えば char 型では,扱いうるのは -128 から +127 まで
の数である
 では,例えば以下のようなことをすると何が起こる?
char i;
やってみましょう
for (i = 0; i < 500; i++)
printf("i = %d\n", i);
本来の意図: i を 0 から 1 ずつ増加させて 499 まで表示
だが,char 型は 127 までしか記述できない!
46
オーバーフローとは何か
 結果:iは127まで増加した直後,i++
によって-128となった → 永遠に 500
に到達しないので,無限ループ
 同様に,例えば i=20*20; としてみる
と i=-112 という結果になる
 プログラマとしては,当然ながら,
オーバーフローを起こすようなプログ
ラムを書いてはならない
 オーバーフローを防ぐためにアルゴリ
ズムを変えることも時には必要となる
 「オーバーフロー」という用語自体に
は,もっと広い意味がある.
i
i
i
…
i
i
i
…
i
i
i
i
…
= 0
= 1
= 2
= 127
= -128
= -127
=
=
=
=
-1
0
1
2
47
プリプロセッサとは
 C言語プログラミングにおいては,C言語で書かれたソー
スコードをコンパイラによってコンパイルし,できたオブ
ジェクトファイル,ライブラリファイルをリンクして実行
ファイルを作成する (分割コンパイル)
コンパイルの前に前処理を行う
ソースファイル
プリプロセッシング (前処理)
コンパイル
オブジェクトファイル
オブジェクトファイル
オブジェクトファイル
リンク
ライブラリ
実行ファイル
48
プリプロセッサとは
 コンパイルに先立って行われる前処理に関してコンパイラ
に指示を行う機能をプリプロセッサという
 ソースコードの特定の位置に指定されたファイルの内容を
挿入する,ソースコード内の特定の文字列を別の文字列に
置換するなど,いくつかの機能がある
 ファイルの取り込み
#include <stdio.h>
このプリプロセッサ指示は, 「ソースコードのこの位置に
stdio.h というファイルの内容を取り込め」という意味
を持つ
※ 指示文の行末にはセミコロン「;」はつけない
49
プリプロセッサとは
 文字列の置換・マクロ定義
#define NUM 12
このプリプロセッサ指示以降,ソースコード中の NUM
という文字列は「12」という文字列に変換される
#define MULT2(X) ((X)*(X))
defineプリプロセッサは引数をとることができる.
上のプリプロセッサ指示以降,例えば MULT2(12) と
書くと,((12)*(12)) に変換される
MULT2(X) X*X
X*X と定義してしま
※ 例えば #define MULT2(X)
うと,MULT2(a+b)*2 が a+b*a+b*2 と解釈されて
しまう → かっこが重要!(教科書 p.58など)
50
プリプロセッサの例
#include <stdio.h> ← ヘッダファイルの取り込み
#define N 10 ← この指示以降,「N」という文字は
「10」として解釈される
void main(void)
{
int a[N]; ← この宣言で確保される配列の
サイズは10となる
a[3] = 100;
}
51
1-1 漸化式
 例題1 nCr の計算
計算途中でのオーバーフローを防ぐために,計算方法 (ア
ルゴリズム) 自体を変更した例
 練習問題1 Hornerの方法
多項式の計算の効率性 (乗算・加算の回数の少なさ) のた
めに漸化式に書き換えてから計算した例
※ 係数の列を「係数の入った配列へのポインタ」として
関数に渡している点に注意!
※ 関数 fn()の引数において係数列へのポインタは
double a[] と書かれているが,これは double *a
と同じ意味.
52
1-3 順位付け
 例題3:素朴なやり口
個別の得点が全体で何位かを求める問題:他の点数を 1 つずつ調べ,
対象の点数より高得点の学生が何人いるかをカウントして,最後に 1
を足せばよい.
 人数が n 人の時,一人の順位を調べるのに n 回調べる手間がかか
り,全員について一人ひとりの順位を調べると総合で n2 回の手間
 練習問題 3-1:改良版
完全な発想の転換により効率化
取りうる点数ごとに,(その点数+1) 点をとった人間の順位を記録す
る手順を踏んでおけば,人数と取りうる点数の数との和の手間ですむ
(2次ではなく1次のオーダ)
 練習問題 3-2 は飛ばす
53
1-6 ユークリッドの互除法
 既に説明した方法は以下の通り
m
n
36
8
36-8
28
8
28-8
20
8
20-8
12
8
12-8
4
8
4
4
36から繰り返し8を引きま
くるため,手間がかかる
どうせ4回も8を引くなら,
一気に引いてしまう
8-4
36%8 という演算で,
一気に4までもっていける
54
ユークリッドの互除法の効率化
 剰余の計算を使うとこうなる
計算の繰り返し回数が減少
do {
k = m % n;
m = n; n = k;
} while (k != 0);
効率性の高いアルゴリズム
m
n
k
m
n
k
36
8
4
8
36
8
8
4
0
36
8
4
4
0
8
4
0
4
0
55
1-7 エラトステネスのふるい
 …の前に,ソースコード上の注意がいくつかある
(次ページ以降)
 まず問題にするのは,教科書 p.41 のコード Rei7.c の中
の以下のコード
while (printf("data? "), scanf("%d",&n)!=EOF){
カンマ記号の意味
scanf() の返り値
56
式の値
 プログラミングでは何らかの演算を行う命令文をすべて
「式」と呼び,すべての式は値を伴う.
 代入文 x = 100:代入される 100 が式の値.
 条件式 a > b:真偽が式の値.C言語だと偽は 0,真は
「0以外の数」と決まっており,通常は真の場合 1.
 関数 printf(“a”):これも一つの「式」であり,値
は関数の返値.
 if 文や while 文,for 文の中で行う条件判定は「式の値
が 0 か 0 以外か」を見ている.
57
カンマ演算子
 式をカンマ区切りでつなげる演算子
 a = 1; b = 2;  a = 1, b = 2;
 式の値としては,カンマ演算子でつながる式の右端の値が
採用される.
 メリット:複数の操作を一つの「式」として書ける.
 while文の条件判定部など,式を 1つしか書けない縛り
があるときに便利.
 while(printf(“data? “),scanf(“%d”,&n)!=EOF)
の場合,関数 scanf() が EOF を返すかどうかで判定
 for (i=0,j=0; i>=j; i++,j--) { … } とかも可能.
58
関数 scanf() の返値
 関数 scanf() は,以下の値を返す
 成功した場合: 入力したデータの個数
 失敗した場合: EOF (end of file)
 EOF は stdio.h において定義された定数である:
 #define EOF (-1)
 具体的には…
 教科書のコードでは,起動すると “data?” という表示を
出した状態で入力待ち状態になる (カーソルが点滅).こ
のとき数を入力すると,それが素数かどうかに応じて「素
数」とか「素数でない」と表示され,続いて再び “data?”
と表示されて入力待ちになる.
 「もういいよ」と思ったら…
59
関数 scanf() の返値
 scanf() が入力待ち状態の時に,Windows なら「Ctrl
キー + Z キー」,UNIX や Linux なら「Ctrl キー + D
キー」を同時に押すことで,scanf() に対して「もうこ
れ以上データはありませんよ」という信号を送ることがで
きる.
 このとき scanf() は,それを呼び出した関数に対して
EOF を返り値として返す (入力処理の失敗の報告).
 while (scanf(……) != EOF) というコードは「Ctrl + Z
(あるいは D)」が入力されるまで入力を繰り返す」という
処理の定石
 ちなみに一般的に「Ctrl + C」はプログラムの強制終了.
60
1-7 エラトステネスのふるい

61
インクリメントの順序
 練習問題7-1のソースコード Dr7_1 において,
prime[m++]=n;
というコードがある (m はそれまでに得られた素数の個
数,n は新しく見つかった素数).ここで…
prime[m++]=n;
同義
prime[m]=n; m++;
prime[++m]=n;
同義
m++; prime[m]=n;
 つまり,インクリメント (1を足す操作) の順序が ++ の
位置によって異なる.
 なお,この例のように,見つけたものを順々に配列に格納
するような処理において,このやり方は簡潔で便利.
62
1-7 エラトステネスのふるい
 練習問題7-1は,まず最初に思いつくような素朴な方法
1
 しかし,これでは計算回数が n n 回かかってしまう
2
 練習問題7-2:エラトステネスのふるい
小さい素数から順にその倍数を「ふるい」から一括で落と
してしまうという根本的な発想の転換により,計算回数を
低減
※ 後述する「計算量のオーダ」では O(n log log n) となる (詳細は省略)
63
理解すべきこと
 以上,「ウォーミングアップ」としていくつかの事例に対
するアルゴリズム・データ構造を見てきた
 理解すべきことは以下の2点
 扱う問題ごとに,様々なアルゴリズムやデータ構造があり
得る
 それらの中で,プログラマは「良いプログラム」を目指
し,良いアルゴリズム・良いデータ構造を使わなくてはな
らない.そのための工夫の余地はいろいろある
 アルゴリズム論では,中でも効率性について重視する
効率をどう測るのか?:計算量 (computational complexity)
(同じ仕事をするにも,手順を n2 回繰り返す方法より n 回ですむ方がよい)
64
アルゴリズムの計算量
 繰り返し述べてきたとおり,アルゴリズム論ではアルゴリ
ズムの効率性,即ちどれだけ少ない繰り返し回数の手順で
問題を解けるかに着目する.
 これを定量的に扱ったものが 計算量 (computational
complexity) である.
 問題のサイズ n が増加するとき,繰り返し回数 (これは処
理時間に比例する) がどのように増加するかを問題とす
る.すなわち,n2 に比例して2次関数的に増加するのか,
あるいは n logn に比例して増加するのか.
 細かい数字は気にせず,「何次式か」という,いわば桁の
ようなもの,オーダ (order) を評価する.
65
計算量評価の実例
 第3回課題の例で計算量のオーダを評価してみよう
 問題の「サイズ」とは,この例では点の数 n である.
 点が n 個あるとき,解答例のアルゴリズムでは,三角形
の面積を計算する回数は nC3 回であった.
1 3 1 2
1
n - n + n
n C 3 = n (n - 1)(n - 2) / 6 =
6
2
3
 「オーダ」を評価するとは,この式の「桁」のようなもの
を評価することである.
 実際の実行時間は CPU,OSなどによって変化しうるの
で,細かい数字を見ることは無意味である.
 それよりは,「何次式か?」を見る.
66
計算量評価の実例
 オーダを求めるには,以下の通りにする.
 最大次数以下の項は無視する.なぜなら,n が大きくなる
につれて,それらの項目は誤差の範囲となるから.
 定数の係数は無視する.定数倍程度の差異は CPU,OSの
違いにより生じるから.
1 3 1 2
1
n - n + n
6
2
3
O(n 3 )
ビッグ・オー表記
「n3 のオーダである」
→ n が非常に大きくなると
実用的時間では計算できない
67
最大次数に着目せよ!
 問題サイズ n に対して計算量が 20n + n n + 5n ln n とな
るアルゴリズムがあるとして,計算量のオーダはどの程度
になるか?
 n が大きくなっていくとき,第1項と第2項,第3項のいず
れが急激に大きくなるかによって決まる.
 グラフを見てみると…
68
最大次数に着目せよ!
(ここでの log は自然対数)
※ log の底は,底変換しても定数倍
になるので,オーダには無関係
69
最大次数に着目せよ!
70
最大次数に着目せよ!
71
最大次数に着目せよ!
x*sqrt(x) は急激に増加
それ以外の項は比率としては
小さくなっていく
72
最大次数に着目せよ!
 n log n よりも n n の方が,大きい n に対して増加率が大
きい.最大次数以外は誤差の範囲に落ちる.
 すなわち,20n + n n + 5n ln n に対して,そのオーダは
O(n n ) と評価しなくてはならないことが分かる.
全ての項のうち,どの項が最も急激に増加
するかを見極めよ!
73
要点
 計算の手間の大きさを定量的に評価するために,繰り返し
回数を問題サイズ n の関数として求める
 細かい数字を気にしても仕方がない  求まった n の関数
が,どういう種類の関数に分類できるか を問題とする
 多項式であればそれが何次式であるか,多項式でなければ
それがどういう関数か (指数関数 (ex) か,対数関数 (logn)
か,対数関数に1次式をかけたもの (nlogn) か,など) に
基づいて関数の形式を分類する
 代表的な例:
 バブル・ソート:O(n2)
 クイック・ソート:O(nlogn)
74
第2章:数値計算
 コンピュータ:電子計算機
 元来は数値計算を行う目的で開発された
例) 砲弾の着弾地点を予測する,戦術の決定を科学的・数
学的に行う (OR:オペレーションズリサーチ)
 コンピュータにおける計算は,常に近似的であるから,完
全に正確な計算はできず,常に誤差が存在する
 誤差には以下のものが存在する:
■ 丸め誤差 (桁数制限のために四捨五入する)
■ 打ち切り誤差 (有限回数で計算を打ち切る)
■ 桁落ち (近い値同士での引き算で生じる)
教科書 p.48-49
75
2-2 数値積分
 解析的に積分ができない場合,コンピュータで逐次的に足
し込むことで積分値を求める.
本来の積分値は
この面積
76
2-2 数値積分
 解析的に積分ができない場合,コンピュータで逐次的に足
し込むことで積分値を求める.
台形則
線分
台形則では,区間
ごとの台形の面積
を足していく
77
2-2 数値積分
 解析的に積分ができない場合,コンピュータで逐次的に足
し込むことで積分値を求める.
シンプソン則
放物線
シンプソン則では
区間の両端と中点
を通る放物線で近
似する
78
2-2 数値積分
 ソースコード上のちょっとした注意点
double 型への scanf()では,%f ではなく%lf とする.
printf()による表示では %f でOK.わけ分かんない.
 教科書の例では
y
y =
4 - x2
2
0 から 2 まで積分すると,
真の積分値は
p = 3.14159…
のはず
0
2 x
 結構大きな誤差
79
台形則と中点則の違い
 台形則では,凸関数や凹関数に対して,常に関数の下側あ
るいは上側を通るため,誤差が累積する.
積分:
グレー領域の
面積を加算
赤い領域は
誤差として蓄積
80
2-3 テイラー展開
打ち切り誤差について
 本来の意味は「繰り返し計算を途中で打ち切ることにより
計算結果と真の値の間に生じる差」のこと.
 教科書では
 打ち切り誤差: もう1回計算したときの増分
 相対打ち切り誤差:もう1回計算したときの増加率
としているが,本来の語義とは若干異なる ことに注意.
81
2-4 非線形方程式の解法
2分法について
 教科書では打ち切り条件を high - low / low < EPS として
いるが,方程式の解がちょうど x = 0 だった場合,分母・
分子がともに 0 に漸近するため,収束しない.
 本来は high - low の値,すなわち解の探索範囲の幅が
十分小さくなったときを打ち切り条件とする.
このとき,得られる解の最大誤差はこの幅となる.
Newton 法について
 一般的には Newton-Raphson 法 と呼ばれる.
 縛りとして,導関数 f ¢(x ) が求まっている必要があるが,
実際上はこれは必ずしも成り立たない.
 この場合も打ち切り条件は x n - x n - 1 < EPS とすべき.
82
数値計算
 授業では扱わないが,教科書にある以下の例も重要:
 2-5 補間
 2-8 連立(一次)方程式の解法
 2-10 最小2乗法
 教科書にないが,微分方程式の数値解法 も重要である.
(例:坪井研の研究では水などの流体の運動をシミュレー
トするなどを行うが,あらかじめ分かっているのは部分部
分の動き方の法則性と初期状態だけであり,運動法則は微
分方程式として与えられる)
 講義「数値シミュレーション」で学ぶ.
83