情報科学(1) データのモデル化(1)

情報科学(1)
対象のモデル化とデータ構造(1)
対象のモデル化
• 扱いたい問題にはさまざまな対象が含まれる
例 車,お金,音,写真,プラズマ,鉄道網,文書,…
• モデル化:対象を扱いやすい形で表現すること
例 飛行機の模型(モデル)による風洞実験
例 群集の個々人を力学相互作用のある点と見なす解析
• 同じ対象でも関心が異なれば異なる見方になる
例 経済活動主体としての会社,組織構造としての会社,
人間の集まりとしての会社,…
同じ対象でも対象をどういう関心で見るかでモデル化は異
なってくる
前期の「情報」で基礎は習った
データモデル
• データ:コンピュータが扱える符号化された情報
コンピュータが扱える最も基本的なデータは2進数
ビット(bit) 1桁の2進数
バイト(byte) 8桁の2進数 (またはoctet)
ワード(word) 4バイトの2進数
ダブルワード(double word) 8バイトの2進数
2進数を基礎にして豊かなデータの種類を構築
• データモデル:対象のモデル化をコンピュータが扱え
るデータの形にしたもの
「情報」でないものも,デジタル情報に写像
例 高分子を3次元の幾何データとして表現する
例 ある領域の波の形を大きな行列(の要素の数値)で表現
データの種類が多いほどデータモデルの作成は容易
データ(内部表現)とデータ表記(外部表現)
• コンピュータの中の0,1と,それを人がどう表記するか
に絶対的な結びつきはない
極端な例
zero, one
零,壱
例 01011110という1バイトの表記
#b1011110
0b1011110
1011110B 10111102 #o136 #x5E 94
0o136 0x5E
• 結びつきを決めるのはプログラミング言語
世の中には実にたくさんの言語がある (>1,000?)
同じデータでも表記はまちまち
データだけではなく計算の書き方もまちまち
多数の自然言語があるのと同じと思って諦めるしかない
実はそれぞれに合理的な理由がある
一つのプログラミング言語(母国語)を覚えると,他の言語
の習得も比較的容易
データ型(data type)
• データ型:似ているデータを総称したもの
例 整数,ブール値,文字,配列,レコード,…
多様なデータモデルに対処するためにはまず分類
分類は学問の事始
• 基本データ型とデータ構造
基本データ型: データ型の中でも基本的なもの
例 整数,ブール値,文字,…
データ構造: いろいろなデータ型が結合されて,より大き
なデータになったもの
例 配列,レコード,リスト,…
例 文字列 文字が一次元的に並んだものとしてはデータ構造
実際には多くの言語で基本データ型になっている
基本データ型には基本演算が用意されている
例 整数には四則演算
数(number)
• 数学の世界にはいろいろな数がある
例 整数,有理数,実数,複素数,4元数,…
そのまま完全にコンピュータのデータとして表現できるか?
No!
例 実数は一般に小数点以下無限桁
メモリが有限のコンピュータでは表現不可能
• コンピュータ内の数(総論)
実用上差し支えない範囲に制限
例 整数を32ビットの2の補数で表現
足りなければ64ビットの2の補数で表現
実用上差し支えない誤差で近似
例 実数を浮動小数点数で表現
1.5142e-4 (1.5142x10-4の意味,コンピュータ内では2進数)
データ構造として表現
例 複素数を実数部の数と虚数部の数の対で表現
数のデータ型(1)
• 整数(integer)
実際には制限された範囲の整数(int, fixnum)
単整数 32ビットで表現できるもの
-2147483648~2147483647 (コンマを入れない)
倍長整数 64ビットで表現できるもの
64ビットマシンでは当たり前
-9223372036854775808~9223372036854775807
任意多倍長整数 十分に広い範囲の整数
データ構造として実現
符号なし整数
2の補数ではなく,32ビットや64ビット(あるいは1バイト)を全
ビット使って非負整数を表すビットの並び(ビットの配列)
32ビットの場合,0~4294967295
1バイトの場合,0~255
数のデータ型(2)
• 浮動小数点数(floating point number, real, float)
2つの数値の合成
仮数部(有効な桁に相当する部分)
指数部(2の何乗が仮数部に掛かるかを示す)
どのコンピュータも浮動小数点数の計算を高速化する専用ハードウェア
を持つので基本データ型に入れる
単精度(32ビット),倍精度(64ビット),4倍長精度(128ビット),…
• 浮動小数点数4.2195x101の表記
例 42.195
4.2195e1
ほとんどの言語はこの表記に沿っている
精度を明確に表すために 倍長では4.2195d1といった書き方をす
る言語もある
• 近似値なので,計算を続けると誤差が拡大する
誤差をなるべく抑える計算法が必要
ブール値(Boolean)
• ある記述が成り立っている(真)か,成り立っていな
い(偽)かを表すデータ ― 1ビット相当の情報量
命題: 記述が変数を含んでいない
述語: 記述が変数を含んでいる
例 x>0
• ブール値の表記はまちまち
true / false
#t / #f
0以外 / 0 (どちらかと言うとデータ型ではなくて内部表現そのまま)
• ブール値に対する基本演算は命題論理で規定されたもの
論理和(or),論理積(and),否定(not),排他論理和(exor)など
文字(character, char)
• 文字の種類の数に応じたビット数でコード化
ASCII文字 コンピュータの世界での標準
改行やタブなどの制御文字を含めた英数記号
通常のキーボードで打てる文字をすべて含む
128種類 7ビットを1バイトに収めて内部表現
[ex] ASCIIコード表を調べてみよう
JIS文字 日本語文字
2バイトで内部表現
Unicode文字 世界中の文字を表現できる
マルチバイトで内部表現
• 文字の表記
例 'a' '1' 'あ’
?a ?1
#\a #\1 #\あ
"a" "1" "あ" (長さ1の文字列として扱う)
文字列(string,str)
• 文字の任意個の並び
例 "abcde" "1234" "おはようございます"
"" (空文字列 ― 0個の文字の並び! ― cf. 0の発見)
• 文字の集合をΣ とすると,文字列の集合はΣ* と書ける
Σ* はΣ の要素を0個以上並べたものの集合(Kleene閉包)
• 文字列に対する基本演算
n 番目の文字にアクセスする
2つの文字列をつなぐ
文字列の長さを調べる
文字列同士の辞書式順序を調べる
文字列の集合には全順序が定義されている
計算を楽にするために,コードの数値の大小比較を敷衍
空文字列が最小元
文字列同士を「パターン照合」する
…
FAQ
Q: 整数,浮動小数点数,ブール値,文字,文字列はみんな同じ2進数で表現されて
いるのにどうしてコンピュータは区別できるの?
A: 一般にはプログラマの指示(データ型の宣言)に従って,この変数が表すのは整
数,あの変数が表すのは浮動小数点数などと,コンパイラが事前に仕分けをして
おき,計算の実行時に迷わないようにしている
データや変数自体にどんなデータ型なのかを示す情報を付加的にもたせて,計
算の実行時にそれを見て判別している言語もある ― 対話型言語はこちらの方式
Q: 数というデータ型はあるの?
A: 同じ種類のデータ型をまとめて,それを「抽象的」なデータ型とする言語もある ―
チワワ種と柴犬種などをまとめて「犬」と呼ぶようなもの
こうしておくと整数と浮動小数点数を足したら結果を浮動小数点数にするといっ
た約束が合理的に書ける
すべてのデータ型を代表するデータ型をもつ言語もある
配列(array)
• 数学のベクトルや行列に似たもの
要素が規則正しく「配列」しているデータ構造
要素は数に限らないが,(通常)同じデータ型で揃っている
添字(そえじ)は1から始まらなくてもよい
最近の言語では0から始まる
文字列=文字の(可変長の)1次元配列 とするのが一般的
…
• 配列要素アクセスの表記
Rubyはこちら
例 1次元配列を a とすると a[i]
2次元配列を b とすると b[i, j],あるいはb[i][j]
…
(これとは異なる表記の言語もある)
• 配列のどの要素も(基本的には)同じアクセス時間
∵ コンピュータのメモリに線形のアドレスがついている and
同じ大きさの要素が規則正しく並んでいる
配列構造の図(1)
整数の1次元配列(月ごとの日数の配列,ただし添字は1月を0,2月を1としている)
31
28
31
30
31
30
31
31
30
31
30
31
ブール値の1次元配列(月ごとの「なにか」の配列 ー クイズ,添字は上と同じ)
true
true
true
true
false false false false
true
true
true
true
実数の2次元配列 markov
markov[i][j] 3つの状態 i =0,1,2がそれぞれ次の状態 j に移る遷移確率を表す
j
i
0
1
2
0
0.3
0.2
0.5
1
0.2
0.5
0.3
2
0.6
0.3
0.1
配列構造の図(2)
• 2次元配列=1次元配列の1次元配列
3次元配列=2次元配列の1次元配列
…
という扱いの言語もある
• 表記も2次元配列配列 markov に対して markov[i][j] のようになる
概念的には
i
j
0.3
0.2
0.5
0.2
0.5
0.3
0.6
0.3
0.1
ユーザは通
常意識しな
くてよい
矢印はデータ構造が格納されているアドレスによる参照を意味する ― ポインタ
Ruby
Rubyを動かしてみよう
• 教室にあるコンピュータにはRubyが標準実装
この講義では対話的にRubyを使う
対話型で走らせるには
(1) ターミナルを起動
(2) 「irb」と入力
(3) Rubyプログラムを入力すると即座に実行
ファイルに記述したプログラムを実行するには
(1) 書いたプログラムをファイルに保存(例えば~/scripts/test.rb)
(2) ターミナルを起動
(3) 「ruby ~/scripts/test.rb」と入力すると,プログラムを実行
(4) irbの中で「load ~/scripts/test.rb」と入力してもよい
自習教材『はいぱーワークブック 』( http://hwb.ecc.u-tokyo.ac.jp/current/ )
(基本編:12. コマンド,13. ファイルシステム,15. エディタ)を参考のこと
• 自分のPCにRubyをインストールする方法は配布プリントを参照のこと
Windows, Linux, Mac OS X, …
インターネットにつながっていれば簡単
Ruby
配列
# irbのプロンプトのあとに以下を打ち込んでみよう (#の右側はコメント)
days_of_each_month = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
days_of_each_month[1]
# 2月の日数はいくつかを訊く.0 origin (0から始めるということ)
something_of_each_month =
[ true, true, true, true, false, false, false, false, true, true, true, true ]
something_of_each_month[10]
# 11月のほにゃららはどうかを訊く.
markov =
[ [0.3, 0.2, 0.5],
[0.2, 0.5, 0.3],
[0.6, 0.3, 0.1] ]
=は代入
最後に改行を
入れる
配列を使ったデータモデル例
例
例
例
例
例
例
例
例
3次元空間の座標
昨年1年間12ヶ月の月別売り上げ量
10点満点の試験の点数分布
別のコンピュータのメモリ空間をシミュレート
4次元空間座標の変換行列
x-y平面の温度分布
1028x768の画素の色情報
剛体内の応力分布
Ruby
配列を使ったデータモデル
例 3次元空間の座標
[1.0, 0.0, 0.0]
例 昨年1年間12ヶ月の月別売り上げ量
[1413, 1343, 1444, 1484, 1201, 1277,
1413, 1234, 1160, 1352, 1304, 1185, ]
例 10点満点の試験の得点分布
[2, 1, 4, 2, 7, 8, 9, 5, 10, 6, 5]
レコード(record,構造体(Struct))
• 数学のn 組(n-tuple)に対応するデータ型
n 組 <第1要素,第2要素,…,第n要素>
それぞれの要素は異なるデータ型でもよい
とくにn=2のときは順序対とも呼ぶ
プログラミング言語では各要素(フィールド)に名前をつける
例 複素数を実数部と虚数部の順序対で表現
realPart
imaginaryPart
3
4
<3, 4> 複素数3+4i
例 有理数(分数)を分子と分母の順序対で表現
numerator
22
denominator
7
<22, 7>
πの簡単な有理数近似 (約分をいつも保障するのは面倒)
Ruby
レコード(Struct)の例(1)
RubyでStructを使うことはまれで
通常は構造体より一般的なオブジェクト(Object)を使う
ComplexNumber = Struct.new(:realPart, :imaginaryPart)
zero = ComplexNumber.new(0, 0) # 0 + 0i
c = ComplexNumber.new(3, 4)
# 3 + 4i
: に続く名前
c.realPart # . はフィールドアクセスの記法
はシンボル
ドット( . )は「の」と読むとわかりやすい
eg. c の realPart
RationalNumber = Struct.new(:numerator , :denominator)
r = RationalNumber.new(22, 7) # 22/7
r.numerator
レコードからオブジェクトへ
• オブジェクト指向言語では,レコードをさらに強化した「オブジェクト」という概念を使
い,オブジェクト(レコード)に固有の関数や手続きをレコードのフィールドアクセスと
似た記法でアクセスできる(呼び出せる)ようにした
• 逆に,フィールドへのアクセス自体もそのオブジェクトに固有の手続きと見なすと,
オブジェクトのデータ構造の内部表現を隠す方法が出てくる
たとえば,複素数の内部表現が極座標(r,θ)でも,「虚数部は?」という問い合わせに対し
て直交座標に変換してから回答することが可能になる
オブジェクト
内部データ構造
+
処理手続き
隠蔽されている
レコードのフィールド
にアクセスするのと
似た形での質問や
動作の依頼
それに対する応答
以下のスライド
ではRuby言語の
オブジェクトを使う
(構造体も併用)
Ruby
class ComplexNumber
def initialize(x, y)
@realPart = x
@imaginaryPart = y
end
オブジェクトで定義した複素数
# ComplexNumberというクラスの定義
# 新しいComplexNumberオブジェクトの初期化
# @... は構造体のフィールドに相当するもの
# ここでは, realPartとimaginaryPartを定義
def realPart() @realPart end
# 実数部を取り出すメソッド
def imaginaryPart() @imaginaryPart end # 虚数部を取り出すメソッド
# ほかにも多数のメソッドが続く
end
オブジェクトに対する「質問」や
「命令」に答えてくれる
プログラム
c = ComplexNumber.new(3, 4) # Structとまったく同じ形
c.realPart
# これも同様
Ruby
class ComplexNumber
メソッド
# ComplexNumberというクラスの定義
# … (前のページと同じところは省略)
include Math
# 数学ライブラリを使うという宣言
def radius()
# 複素数の絶対値,**はべき乗の意味
sqrt(@realPart**2 + @imaginaryPart**2)
end
オブジェクトの外部からは,
def argument()
# 複素数の偏角 (ラジアン)
# 自分で書いてみよう (tanの逆関数はatan)
end
# ほかにも四則演算等の多数のメソッドが続く
end
c = ComplexNumber.new(3, 4)
c.radius
# 値は浮動小数点数になる
あたかもStructのような
フィールドが内部にあるか
のように見える
レコード(2)
• いろいろなレコード(2)
例 date 年月日をそれぞれ整数,文字列,整数の3組で表現
year
month
day
2000
"February"
2
西暦888年8月28日以来の偶数だけの日付
(しかし,この長い空白の前後では当たり前!)
例 person 人を姓(文字列),名(文字列),年収(整数),既婚・未
婚(ブール値)の4組で表現
familyName
firstName
"田中"
"一郎"
income married
450
true
レコード(3)
• いろいろなレコード(3)
customer
顧客を顧客ID(整数)とpersonと誕生日(date)の3組で表現
ID
person
birthday
1202805
familyName
firstName
"田中"
"一郎"
income married
450
true
year
month
day
1975
"August"
19
Ruby
レコード(Struct)の例(2)
Date = Struct.new(:year, :month, :day)
special_day = Date.new(2000, "Febrary", 2)
tanaka_san_birthday = Date.new(1975, "August", 19)
Person =
Struct.new(:familyName, :firstName, :income, :married)
tanaka_san = Person.new("田中", "一郎", 450, true)
Customer = Struct.new(:ID, :person, :birthday)
otokuisama = Customer.new(1202805, tanaka_san,
tanaka_san_birthday)
otokuisama.person.familyName # お得意様の姓を訊く
otokuisama.birthday.year
# お得意様の誕生年を訊く
Ruby
レコード(Struct)の例(3)
my_customers = [
Customer.new(1, Persoon.new("田中", "一郎", 450, true),
Date.new(1975, "August", 19)),
Customer.new(2, Persoon.new("佐藤", "次郎", 400, true),
Date.new(1977, "May", 4)),
Customer.new(3, Persoon.new("鈴木", "三郎", 500, false),
Date.new(1979, "November", 30)),
Customer.new(4, Persoon.new("中村", "史朗", 600, true),
Date.new(1980, "January", 21)),
]