2017年6月30日金曜日

MIT OCW 6.0002 02日目

Eric Grimson, John Guttag, and Ana Bell. 6.0002 Introduction to Computational Thinking and Data Science. Fall 2016. Massachusetts Institute of Technology: MIT OpenCourseWare, https://ocw.mit.edu. License: Creative Commons BY-NC-SA.

Lecture 3. Graph-theoretic Models

ある現実に存在するものや,抽象的な概念の集合(nodes)があって,それらの関係性(edges)を記述する方法にGraphがある.あるnodeから出るedgeを辿って他のnodeにたどり着けるとき,渡ってきたedgeたちをからへのpathという.edgeはnode間の関係性を表すが,必ずしも両方向でない関係性(好き嫌い,因果関係,所属)のような関係を記述するgraphをdirected graph(有向グラフ)といい,必ず両方向である関係(血縁,路線図)を記述するgraphをundirected graph(無向グラフ)という.undirected graphはdirected graphの特別な場合である.また,edge間に重みがつけられているgraphもある。.

特に重要なグラフの一種にtreeがある.treeでは任意の2つのedgeが一意のpathで結ばれている.


The network graph formed by Wikipedia editors (edges) contributing to different Wikipedia language versions (vertices) during one month in summer 2013
The network graph formed by Wikipedia editors (edges) contributing to different Wikipedia language versions (vertices) during one month in summer 2013

早速graphを実装する.

class Node(object):
  def __init__(self, name):
    self.name = name
  def getName(self):
    return self.name
  def __str__(self):
    return self.name

class Edge(object):
  def __init__(self, src, dest):
    """ Assumes src and dest are nodes"""
    self.src = src
    self.dest = dest
  def getSource(self):
    return self.src
  def getDestination(self):
    return self.dest
  def __str__(self):
    return self.src.getName() + '->' \
            + self.dest.geName()

class Digraph(object):         # directed graphのクラス
  """
  edge is a dict mapping each node
  to a list of its children.
  """

  def __init__(self):
    self.edges = {}
  def addNone(self, node):
    if node in self.edges:
      raise ValueError('Duplicate node')
    else:
      self.edges[node] = []

  def addEdge(self, edge):
    src = edge.getSource()
    dest = edge.getDestination()
    if not (src in self.edges and dest in self.edges):
      raise ValueError('Node not in graph')
    self.edges[src].append(dest)

  def childrenOf(self, node):
    return self.edges[node]

  def hasNode(self, node):
    return node in self.edges

  def getNode(self, name):
    for n in self.edges:
      if n.getName() == name:
        return n
      raise NameError(name)

  def __str__(self):
    result = ''
    for src in self.edges:
      for dest in self.edges[src]:
        result = result + src.getName() + '->'\
                + dest.getName() + '\n'

    return result[:-1] # omit final newline

  class Graph(Digraph):
    """
    transform a directed graph to a undirected graph
    """

    def addEdge(self, edge):
      Digraph.addEdge(self, edge)
      rev = Edge(edge.getDestination(), edge.getSource())
      Digraph.addEdge(self, rev)

古典的なグラフ最適化問題に,最短経路問題がある.node1, node2を結ぶ最短のpathを見つける問題である.ループ構造がある可能性があるので,無限ループを避ける工夫が必要.depth-first search(深さ優先探索)breadth-first search(幅優先探索)がある.

初期nodeから辿れるpathを目的nodeに到達するか,到達しないと判明するまで(ループ構造は,辿ってきたpathを保存しておいて照合することで回避する)辿る.

def DFS(graph, start, end, path, shortest, toPrint=False):
  path = path + [start]
  if toPrint:
    print('Current DFS path:', printPath(path))
  if start == end:
    return path
  for node in graph.childrenOf(start):
    if node not in path: #avoid cycles
      if shortest == None or len(path) < len(shortest):
        newPath = DFS(graph, node, end, path, shortest, toPrint)
        if newPath != Node:
          shortest = newPath
        elif toPrint:
          print('Already visited', node)
  return shortest


def  shortestPath(graph, start, end, toPrint=False):
  """ a wrapper function of DFS"""
  return DFS(graph, start, end, [], None, toPrint)

Breadth-first Search(BFS)

初期nodeから長さnのpathのnodeに目的nodeがあるかすべて探索してから,長さn+1のpathのnodeたちを調べる.depth-first searchよりもふつう低速である.

def BFS(graph, start, end, toPrint=False):
  initPath =  [start]
  pathQueue = [initPath]
  while len(PathQueue) != 0:
    tmpPath = pathQueue.pop(0)
    if toPrint:
      print('Current BFS path:', printPath(tmpPath))
      lastNode = tmpPath[-1]
      if lastNode == end:
        return tmpPath
      for nextNode in graph.childrenOf(lastNode):
        if nextNode not in tmpPath:
          newPath = tmpPath + [nextNode]
          PathQueue.append(newPath)
  return None

2017年6月29日木曜日

The Rust Programming Language 2nd 10日目 基本的なデータ構造

![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0

Common Collections

8.1 Vectors

Vec<T>,あるいはvector型は,メモリ上に順番に同じ<T>型の要素を並べてできるデータ型である.

Creating a New Vector

let v: Vec<i32> = Vec::new();

によって新しいベクトルを作れる.新規作成時に値を代入しない場合,Rustにvectorが保持する型がなんであるか明示的に知らせることができる.初期値を指定してベクトルを作るには,vec!macroを使う.

let v = vec![1, 2, 3];

Updating a Vector

vectorを作成した後新たに要素を追加するには,pushを使う.

let mut v = Vec::new();

v.push(5);
v.push(6);
v.push(7);

Dropping a Vector Drops its Elements

structと同様に,vectorはスコープの外に出ると削除される.

{
  let v = vec![1, 2, 3, 4];
}     // ここでvは削除される

Reading Elements of Vectors

let v = vec![1, 2, 3, 4, 5];

let third: &i32 = &v[2];              // (1)
let third: Option<&i32> = v.get(2);   // (2)

と,2つの方法でvectorの中の値を取得できる.tuppleなどと同様,vectorは0からインデクシングされる.
(1)において,vの三番目の要素のreferencereference型の変数thirdに渡している.仮に&v[100]などとするとpanic!エラーが生じてクラッシュする.
(2)において,getmethodによってvの三番目の要素(i32)を取って,Option<T>に代入している.この場合に&v[100]としても,(1)と異なり,thirdにはNoneが代入されて,クラッシュしない.

Invalid References

src/main.rs

let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0];

v.push(6);

このコードは一見無害だが,実際にコンパイルすると

shell

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
immutable
  |
4 | let first = &v[0];
  |              - immutable borrow occurs here
5 |
6 | v.push(6);
  | ^ mutable borrow occurs here
7 | }
  | - immutable borrow ends here

とエラーを生じる.vectorに値を追加するとき,仮に隣接するメモリーが使用中であるなら,一旦vector全てをより広い空間にコピーし,ポインタを貼り直す.このときに,firt=&v[0]が変更されないままであると,不正な値を参照してしまうので,rustは上のようなコードを許していない.

Using an Enum to Store Multiple Types

vectorは同じ型の値しか入れられないが,enumを使って異なった型の値が入っているように振る舞うことができる.例えば,int, float, stringの値を入れられるスプレッドシートのセルを考える.スプレッドシートの行はvectorと考えることができる.

enum SpreadsheetCell {
  Int(i32),
  Float(f64),
  Text(String),
}

let row = vec! {
  SpreadsheetCell::Int(3),                     
  SpreadsheetCell::Text(String::from("blue")),  
  SpreadsheetCell::float(10.12),
};

上のプログラムでvec!によってenum型の値が3つ入ったvectorが作られる.

Basic Analysis (Jiri Lebl) 31日目

CC BY-NC-SA 3.0

8.3 The derivative

この節で,で,開集合とする.

8.3.1 The derivative

において,

が存在するとき,それをにおける微分係数というのだった.微分係数が存在すると仮定すると,

が成立する.であることに着目すると,の近くでを近似する線形作用素と言える.この考えを多次元の場合に拡張する.

Definition 8.3.1

とする.
differentiable(微分可能)

とか,と書き,におけるderivativeという(訳語は色々あるが,微分係数か,単に微分と呼ぶことにする).で微分可能なとき,単にで微分可能であるという.

とする操作を微分するという.が存在するならそれは一意(Prop 8.3.2)だから,
は関数と考えることができて,の導関数と呼ぶ.

Proposition 8.3.2.

について,で,がともににおける微分係数なら,である.
proof.


ここで,とするとであり,,最左辺は,と書ける.
の作用素ノルムがだから,,したがって.

Proposition 8.3.5.

で微分可能なら,で連続である.
proof.

Theorem 8.3.6 (Chain rule)

, open. であって,
で,で微分可能で,それぞれの微分係数をとするとき,
で微分可能で,その微分係数はである.
proof. 略

8.3.2 Partial derivatives

Definition 8.3.7

について,

が存在するとき,におけるに対するpartial derivative(偏微分係数)という.
であるとき,におけるに対する微分係数をと書く.

Proposition 8.3.8

で微分可能であるとき,
であるが,これをの標準基底を使って表現するとき,

と書ける. これは

ということでもある.
であるとき,

である.
proof.

を固定する.

において,微分可能性より右辺はに近づく.よって

が成立する.と書くとすると,

が存在して,に等しい.

一方で,それぞれの偏微分係数が存在しても,が微分可能であるとは限らない.

8.3.3 Gradient and directional derivatives

が微分可能とする.gradient(勾配)を

と定める.はナブラと読む.
が成立する.(行列ベクトルとベクトルベクトル)
が微分可能でであるとき,このような関数とその像をcurve(曲線)という.とすると,
が定義できて,は微分可能である.

が成立する.

にある点が速度でベクトル方向に移動しているとき,の変化率を考える.
とすると,これはその点の軌跡を表す曲線であって,である.
であって,そのchain ruleから

が成立する.最左辺をと書くことにする.
とすると,Cauchy-Schwartzの不等式から

統合はの定数倍である時成立し,

である.このように,gradientはある関数が最も早く増大するような方向を向いている

8.3.4 The Jacobian

Definition 8.3.9

が微分可能であるとする.このとき,Jacobian(ヤコビアン)を,

と定める.Jaobianは

と書かれることがある.行列式が幾何学的に面積や体積を表すのと同様に,Jacobianは写像がの元をに写すとき,もとの元に比べて像はどれほど引き伸ばされたり,押しつぶされているかの指標になる.

2017年6月28日水曜日

The Rust Programming Language 2nd 09日目

![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0

Controlling Visibility with pub

今まで作ってきたライブラリ(ドンガラだが)を外から使ってみる.それには,communicator/src/main.rsを作成して,その中でcommunicatorを呼び出せば良い.

src/main.rs

extern crate communicator;

fn main() {
  communicator::client::connect();
}

ここでcargo buildすると,
shell

error: module `client` is private
 --> src/main.rs:4:3
  |
4 |   communicator::client::connect();
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: Could not compile `communicator`.

To learn more, run the command again with --verbose.

とエラーが出る.clientmoduleがprivateであるというエラーである.

Making a Function Public

pubキーワードによってfunctionやmoduleをpublicにできる.
clientライブラリをpublicにするには,
src/main.rs

pub mod client;
mod network;

とする.ここでcargo buildしても,

error: module `client` is private
 --> src/main.rs:4:3
  |
4 |   communicator::client::connect();
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

と怒られる.今度はsrc/client.rsを書き換えてclient::connectをpublicにすると,ビルドできるようになる.

warning: function is never used: `connect`
 --> src/network/mod.rs:1:1
  |
1 |   fn connect() {
  |  _^ starting here...
2 | | }
  | |_^ ...ending here
  |
  = note: #[warn(dead_code)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.53 secs

という警告は,network::connectがprivateで,そのライブラリプロジェクトの中でも使われていないから起こる.network::connectとnetwork::server::connectもpublicにする.

Privacy Rules

  1. あるアイテムがpublicであるとき,親moduleを通じてそのアイテムにアクセスできる.
  2. あるアイテムがprivateであるとき,そのアイテムがあるmoduleとその子moduleからしかそのアイテムにはアクセスできない.

Privacy Examples

mod outermost {                             // private                    
  pub fn middle_function() {}               // public in private           
  fn middle_secret_function() {}            // private                      

  mod inside {                              //  private in private           
    pub fn innner_function() {}             //  public in private in private  
    fn secret_function() {}                 // private in private in private 
  }
}

fn try_me() {
  outermost::middle_function();             // PrivateRule1より,accessible
  outermost::middle_secret_function();      // PrivateRule2より,unaccessible
  outermost::inside::inner_function();      // PrivateRule2より,accessible
  outermost::inside::secret_function();     // PrivateRule2より,unaccessible
}

Importing Names

src/main.rs

pub mod a {
  pub mod series{
    pub mod of {
      pub fn nested_modules() {}
    }
  }
}

fn main() {
  a::series::of::nested_modules();
}

のように,module1の中のmodule2,module1の中のfunction1を指定するときには,module1::module2とかmodule1::function1とするのだった.
こうした記法では呼び出しが長くなりすぎる危険があるので,より簡潔に呼ぶ方法が実装されている.

Concise Imports with use

src/main.rs

pub mod a {
  pub mod series{
    pub mod of {
      pub fn nested_modules() {}
    }
  }
}

use a::series::of;

fn main() {
  of::nested_modules();
}

このように,useによって内側にあるmoduleを現時点の名前空間から記述して名前空間に入れることで,そのmoduleの名前だけでそのmoduleを特定できる.

enumもまた名前空間を作るので,useによってenumを簡潔に書ける.同じ名前空間から複数の要素をuseによって今の名前空間に入れるときは{}でまとめられる.

enum TrafficLight {
  Red,
  Yellow,
  Green,
}

use TrafficLight::{Red, Yellow};

fn main() {
  let red = Red;
  let yellow = Yellow;
  let green = TrafficLight::Green
}

### Glob Imports with ```*```
ある名前空間の中の全てを今の名前空間に入れたいとき,```*```を使う.

```rust
enum TrafficLight {
  Red,
  Yellow,
  Green,
}

use TrafficLight::*;

fn main() {
  let red = Red;
  let yellow = Yellow;
  let green = Green;
}




<div class="se-preview-section-delimiter"></div>

*globという.便利だがバグの温床となるので使うときは慎重に考えるべき.

Using super to Access a Parent Module

ライブラリを作成するとき,Cargoは同時にtests moduleを作成する.これについて詳しく述べる.
src/lib.rs

pub mod client;
pub mod network;





<div class="se-preview-section-delimiter"></div>

#[cfg(test)]
mod tests {
  #[test]
  fn it_works() {
  }
}




<div class="se-preview-section-delimiter"></div>

この時点でのcommunicatorの階層構造は

communicator
 ├── client
 ├── network
 |   └── client
 └── tests
 ```
 となっている.```tests::it_works```に```client::connect```を実行するコードを入れてみよう.

 *src/lib.rs*
 ```rust
 #[cfg(test)]
 mod tests {
   #[test]
   fn it_works() {
     client::connect();
   }
 }
 ```
```cargo test```を実行する.


 *shell*
 ```bash
 warning: function is never used: `connect`
 --> src/network/mod.rs:1:1
  |
1 |   pub fn connect() {
  |  _^ starting here...
2 | | }
  | |_^ ...ending here
  |
  = note: #[warn(dead_code)] on by default

  error[E0433]: failed to resolve. Use of undeclared type or module `client`
  --> src/lib.rs:8:5
   |
 8 |     client::connect();
   |     ^^^^^^^^^^^^^^^ Use of undeclared type or module `client`

 error: aborting due to previous error

 error: Could not compile `communicator`.
 Build failed, waiting for other jobs to finish...
 error: build failed




<div class="se-preview-section-delimiter"></div>

rustの中の名前空間は相対的で,testsの中からclient::connectを呼ぶには,一度上の階層に登ってからclient::connectを呼ばなければならない.ただし,usrで呼ぶ場合はルートから名前を指定する.
::を最初につけることで,絶対的に階層を指定できる.例えば::client::connect()とすれば,testsの中からclient::connectを呼べる.あるいは,superキーワードで一つ上の階層に登って,client::connectを呼ぶこともできる.このばあいsuper::client::connectとする.
bashで例えるなら,super.と,::/に対応する.

src/lib.rs

mod network;
pub mod client;

#[cfg(test)]
mod tests {
  #[test]
  fn it_works() {
    client::connect();
  }
}

としてcargo testすると,
shell

Running target/debug/deps/communicator-428d4111ad386458
 
running 1 test
test tests::it_works ... ok
 
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

と,無事テストが完了する.

Running target/debug/deps/communicator-428d4111ad386458

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

と,無事テストが完了する.

Basic Analysis (Jiri Lebl) 30日目

CC BY-NC-SA 3.0

8.2.2 Matrices

有限次元ベクトル空間があって,それぞれの基底をとする.を行列によって表現する.
の基底に対する像によって決まるから,

が成立するようにを定めると,

が成立する.(行列の基本用語の生命は省略する)

行列と線形写像は1体1に対応する.基底を変えれば,行列は異なった線形写像を表現することになる.
で,標準基底を用いるとき,Cauchy-Schwartzの不等式から
任意のに,

したがって,が成立する.この右辺をのnormと考えることもできて,Frobenius norm といい, と書く.

Proposition 8.2.7

が距離空間において連続関数として,の要素を行列として並べると,である連続写像である.逆に,が連続である時,それの行列表現の要素は連続である.

8.2.3 Determinants

行列式の議論だが,面倒なので大半は省略して重要な事実を列挙する.

  1. \det I = 1
  2. はそれぞれのに対して線形
  3. の2つの列を交換してとしたとき,
  4. が相等しい列をもつとき,
  5. が0ベクトルを列に持つとき,
  6. は連続写像
  7. で,可逆なとき,
  8. したがって
  9. 性質2~5は列を行と呼び変えても成立
  10. 行列式は基底によらない. を基底の変換行列として,は全単射で可逆.ゆえに

8.2.3 Exercises

Exercise 8.2.11

を多項式空間とし,を微分作用素とする.
をにノルムを入れる.
a) これが確かにノルムであることを示せ.
b) の作用素ノルムは非有界であると示せ.

答案.

a)
(i) は明らか.
(ii)
(iii) が成立.
以上より示せた.
b)
である.ゆえ,よって確かには非有界

8.2.12 (Prop 8.2.4の証明)

は有限次元ベクトル空間で,をそのノルムとする.
a) , は連続と示せ.
b) であるとき,を常に満たすが存在することを示せ.
c) ならばなるが存在すると示せ.
d) (c)を使って,が有限次元ベクトル空間でならを示せ.

答案.

b) は超球面で,閉集合である.さらに有界で有限次元だからコンパクトであって,a)で示した連続性より,最小値,最大値がには存在する.
c) であるが存在するとき,
は基底だから,.よっては非有界となり,に反する.

8.2.13

を基底とするベクトル空間で,ノルムが入っているとする.
のノルムは標準ユークリッドノルムとする.
a) 任意のであるがあると示せ.
b) のノルムとするとき,があって,任意のが成り立つことを示せ.
c) は,を距離として開集合なら,を距離としても開集合であることを示せ.

答案.

a)
とする.で,から,が成立する.とすれば,上で抑えられることもわかる.
b)
a)の証明の過程で,としたが,それぞれでノルムを使ったときのとするとき,


が成立する.任意のと書けて,と新たに定めると,
が成立する.したがって,

が成立する.
c)
とする.
a)より, なるが存在する.
で,仮定よりによって開集合であるとすると,ならばなるが存在する.ならばであるから,によってもは開集合である.

MIT OCW 6.0002 01日目

Eric Grimson, John Guttag, and Ana Bell. 6.0002 Introduction to Computational Thinking and Data Science. Fall 2016. Massachusetts Institute of Technology: MIT OpenCourseWare, https://ocw.mit.edu. License: Creative Commons BY-NC-SA.

Lecture 1. Introduction and Optimization Problems

Optimization Modelとは,対象となる関数(Objective function)をある制限によってもとで最大化したり最小化したりする引数を探し,現実の問題に応用するモデルのこと.例えばKnapsack problemがある.

Knapsack Problem :
ナップサックに入れられるものの重さの総和が決まっていて,重さの異なる複数種類のものをできるだけ価値が大きくなるようにナップサックに入れたいとき,どうやって入れるものを選ぶか. という問題.
- Continuous or fractional varinant: 入れるものが粉や液体のようないくらでも分割できるものではほとんど意味がない.
- 0/1 variant: それぞれの種類のものは1つしかない –その一つを持っていくか持っていかないか

0/1 Knapsack Problem, Formalized

  • それぞれの物品は

Brute Force Algorithm

  1. ありうる入れ方の組み合わせをすべて列挙する.(冪集合をとる操作)
  2. 組み合わせの重量の和がWを上回った組み合わせを取り除く
  3. 残った組み合わせそれぞれの価値の和を計算し,最大の価値の組み合わせを選ぶ

この方法だと組み合わせ爆発が生じる: 物品が100種類ある時,その冪集合の要素数は

より現実的な解法:

Greedy Algorithm(貪欲法) a Practical Alternative

while knapsack not full
  put "best" avalilable item in knapsack
  • “bset”とは何か?が問題となる.

Example

Food wine beer pizza burger fries coke apple donut
value 89 90 30 50 90 79 90 10
calories 123 152 258 354 365 150 95 195

このようなメニューがあるとき,摂取カロリーを750kcal以下にしつつ,valueの和を最大化する.(ここでvalueというのは値段でなく好みの定量)

class Food(object):
  def __init__(self, n, v, w,):
    self.name = n
    self.value = v
    self.weight = w

  def getValue(self):
    return self.value

  def getCost(self):
    return self.calories

  def density(self):
    return self.getValue()/self.getCost()

  def __str__(self):
    return self.name + ': < ' + set(self.value) + ', ' +str(self.calories) +">"

def buildMenu(names, values, calories):
  menu = []
  for i in range(len(values)):
    menu.append(Food(names[i], values[i], calories[i]))

  return menu

def greedy(items, maxCost, keyFunction):
  itemsCopy = sorted(items, key = keyFunction, reverse = True)
    # "best"の基準をkeyFunctionで指定する.
    # value, cost(calories), value/cost, cost^-1などを使う.

    # ソートの問題だから,クイックソートを使えば計算量は O(nlog(n))
    # (pythonはtimsort というクイックソートの一種を使っている)
  resut = []                                                   
  totalValue, totalCost = 0.0, 0.0                              

  for i in range(len(itemsCopy)): # 計算量はO(n)
    if (totalCost + itemsCopy[i].getCost()) <= maxCost:
      result.append(itemsCopy[i])
      totalCost += itemsCopy[i].getCost()
      totalValue += itemsCopy[i].getValue()

  return (result, totalvalue)
  # 全体の計算量はO(n log n)

貪欲法は簡単に実装できるが,
ナップサック問題での貪欲法は,必ずしも最適解を与えないし,どれほど最適解に近いかわからないという欠点がある.

Lecture 2. Optimization Problems,

Definition Tree

Treeはデータ構造の一つで,ただ一つのルートノードから発生する子ノードがあり,その子ノードから更に孫ノードが発生し・・・という具合に,ループと合流を許さない階層構造が定義されている.あるノードがからn個のノードを遡るとルートノードに至るとき,そのノードはdepth nであるという(ルートはdepth 0)またdepth n であるノードたちをlevel nに属するという.treeのノードのたちのうち,最大のdepthをtreeのlevelという.

Search Tree Implementation

rootから木を構成する.物品は最初にテーブルに並んでいて,その順序関係は変わらないとする.
1. 最も若い番号の物品をナップサックに入れても重量オーバーにならないとき,それをナップサックに入れるときのノードと入れないときのノードを作り,重量オーバーになるとき,ナップサックに入れないときのノードのみ作る.どちらにせよその物品はテーブルから削除する.
2. 子ノードを持っていない全てのノードに1の操作を行う
3. テーブルが空になったら,最大の価値があるような末端ノードを選ぶ.
enter image description here

Computational Complexity

  • 処理時間は生成されるノードの数に依存する
  • level i にあるノードの個数は最大
    最初にテーブルにn個の物品があるとき,ノードの総数は
    => すなわちO

Decision Tree Implementation

def maxVal(toConsider, avail): # これは効率の悪いアルゴリズムである.
  """
  assumes toConsider a list of items,
          avail a weight
  Returns a tuple of the total value of a solution to 0/1 knapsack problem
  and the items of that solution
  """
  # toConsiderの左端をナップサックに入れるかどうかを考えている.
  if toConsider == [] or avail == 0:                # ノードを追加できない       
    result = (0, ())
  elif toConsider[0].getUnits() > avail:            # ナップサックには空きがあるが,
                                                    # 今考えている物品は入らない
    result = maxVal(toConsider[1:], avail)
  else:                                             # ナップサックに今考えている物品が入る
    nextItem = toConsider[0]                        
    withVal, withToTake = maxVal(toConsider[1:], 
                            avail - nextItem.getUnits())
    withVal += nextItem.getValue()
    vithoutVal, withoutToTake = maxVal(toConsider[1:], avail)
  if withVal > withoutVal:
    result = (withVal, withToTake + (nextItem,))
  else:
    result = (withoutVal, withoutToTake)

  return result

Dynamic Programming(DP)

クロージャを使ったメモ化
でフィボナッチ数列の計算を高速化できたように,問題を解く仮定で同じサブ問題を何度も解く必要があるとき,最初に計算した答えを保存しておき,以下同じ引数が出てきたときには保存していた答えを取ってくるようにすると,高速に解けることがある.
メモ化が有効である条件は,
- Optimal substructure : 問題の最適な答えは,その問題をいくつかの小問題に分けたときの小問題の最適解たちに簡単な演算を施すことで得られて,小問題はもとの問題と同じ形をしている.
- Overlapping subproblems : 問題の最適解を得るために,同じ問題を複数書いといているとき

この基準からすると,fig.1の場合はDPはさほど有効でないが,fig.2の場合には計算量が半分以下になることが見て取れる.

enter image description here

def fastMaxVal(toConsider, avail, memo={}):  #  メモ化による高速版
  """
  assumes toConsider a list of items,
          avail a weight
  Returns a tuple of the total value of a solution to 0/1 knapsack problem
  and the items of that solution
  """
  if (len(topConsider), avail) in memo.keys():
    result = memo[(len(topConsider), avail)]

  # toConsiderの左端をナップサックに入れるかどうかを考えている.
  elif toConsider == [] or avail == 0:                # ノードを追加できない       
    result = (0, ())
  elif toConsider[0].getUnits() > avail:            # ナップサックには空きがあるが,
                                                    # 今考えている物品は入らない
    result = fastMaxVal(toConsider[1:], avail, memo)
  else:                                             # ナップサックに今考えている物品が入る
    nextItem = toConsider[0]                        
    withVal, withToTake = fastMaxVal(toConsider[1:], 
                            avail - nextItem.getUnits(), memo)
    withVal += nextItem.getValue()
    vithoutVal, withoutToTake = maxVal(toConsider[1:], avail, memo)
  if withVal > withoutVal:
    result = (withVal, withToTake + (nextItem,))
  else:
    result = (withoutVal, withoutToTake)

  memo[(len(toConsider), avail)] = result
  return result

Summary of Lectures 1-2

  • 多くの実践的な問題は最適化問題として定式化できる
  • 多くの場合Greedy algorithm(貪欲法)は十分良い解を与えるが,最適とは限らない.
  • 最適解を求めるのは指数的に困難
  • optimal substructure とoverlapping subproblemsの性質を持っているなら,Dynamic Programmingによって,高速に最適解を得られる場合がある

2017年6月27日火曜日

The Rust Programming Language 2nd 08日目

![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0

Chapter 7. Modules

main.rsの見通しが悪くならないように,Module機能を使って他のファイルにfunctionやstructureを定義して,適宜読み出すようにできる.moduleは関数や型の定義を含むネームスペースであって,それらの定義がmoduleの外から見える(public)か否(private)かはプログラマが指定できる.

mod and the Filesystem

今回は新しいプロジェクトでlibraryを作るようにする.これはcargo newのデフォルト.
$ cargo new communicator
communicatorプロジェクトが作成できるが,libraryの場合ソースはsrc/lib.rsに書く.
ライブラリ開発の場合,cargo runではなくcargo buildを使う.

Module Definitions

src/lib.rs

#[cfg(test)]
mod network {
  fn connect() {
  }
}

mod client {
  fn connect() {
  }
}

mod tests {
    #[test]
    fn it_works() {
    }
}

このように,moduleの定義にはmodキーワードのあとにmodule名を書き,{}に囲った中身がそのmoduleの名前空間に入る.network moduleのconnect functionを他のプログラムから呼び出したいときには,network::connectと書く.
実は,バイナリ開発のsrc/main.rsの中でmoduleを定義することができるし,moduleの中でさらにmoduleを定義することもできる.

mod network {
  fn connect() {
  }

  mod client {
    fn connect() {
    }
  }
}

は正しいrsutコードである.この場合,clientの中のconnectはnetwork::client::connectとして呼ぶ.moduleを書くファイルを複数用意することも可能だから,どのようにmoduleを構成していくかはプログラマやユーザーのセンス.

Movimng Modules to Other Files

src/lib.rs

mod client {
  fn connect() {

  }
}

mod network {
  fn connect() {
  }

  mod server {
    fn connect() {
    }
  }
}

このように散らかったmoduleたちは,複数のファイルに分けてしまうとよい.たとえばclientを別のファイルに置くには,mod client{ ...}mod client;と名前だけ残して,;をつけ,client.rssrc/に新たに作成し,src/client.rsにはもとのclientの定義の{}の中を書く.すなわち
src/client.rs

fn connect() {
}

のようにする.mod宣言を書かないことに注意する.さらにnetworkを他のファイルに移すなら,
src/lib.rs

mod client;
mod network;

src/network.rs

fn connect() {
}

mod server {
  fn connect() {
  }
}

とする.ルールは同じである.さらにnetwork::serverを他のファイルに移すなら,src以下にnetworkディレクトリを作成し,その下にmod.rsにリネームしたsrc/network.rsと新しく書いたserver.rsを置く.

Rules of Module File Systems

  • fooというmoduleがその内部にさらにmoduleを持っているとき,src/lib.rsmod foo;で宣言し,src/foo.rsに内容を書く.
  • fooというmoduleがその内部にbarというmoduleを持っているとき,lib.rsにおける宣言は同じだが,src/fooというディレクトリを作成し,その中のmod.rsfooの内容を,bar.rsbarの内容を書く.

Basic Analysis (Jiri Lebl) 29日目

CC BY-NC-SA 3.0

8.2 Analysis with vector spaces

8.2.1 Norms

Chapter 7では距離空間について論じた.距離空間を導く概念にnorm(ノルム)がある.集合にnormを定めると,そのnormから距離関数を導ける.

Definition 8.2.1

をベクトル空間とする.が次の(i)~(iii)を満たすとき,それをノルムという.
(i) が常に成立し,特に
(ii) として,
(iii)

における標準ノルムを定める前に,標準ドット積を定める.
について,

を標準ドット積という.標準ドット積の一般化が内積である.標準ドット積をにおける標準内積とも呼ぶ.Euclidean normを,

と定める.には様々なノルムが定義できるが,単にノルムというとこれのことを指す.

これがノルムの条件を満たすことを示す.(i),(ii)は明らかで,(iii)は前章で示したが,重要なのでもう一度証明することにする.

Theorem 8.2.2 (Cauchy-Schwartz inequality)

について,

が成立.
proof.

の少なくとも一方がなら明らか.とする.
と書けるとき,.そうでない場合,

は常に正になるから,この方程式は実数根を持たない.ゆえにに関して解の公式を使って,

が成立する.根をとれば,Cauchy-Scwartzの不等式になる. 以上より示せた.

さて,を示す.両辺を二乗し,

を言えばよい.整理すれば直ちにCauchy-Scwartzの不等式と同値になる.

Definition


における標準距離関数である.
一般に,ベクトル空間とノルムがあるとき,の差はとして関数を定めると上の距離関数となる

Definition 8.2.3

とする.

この演算子をのoperator norm(作用素ノルム)という.とも書く.
だから,

である.したがって

が成立する.また,である.

今はベクトル空間が有限次元であるときのみ考えるが,無限次元ではかなり話が違ってくるので注意する.

Proposition 8.2.4

: 有限次元ベクトル空間とする.
とすると,であり,かあつをLipschitz定数としてLipschits連続(一様連続)である.

proof.

で,ノルムは標準ノルムとする.
の標準基底とし,と書ける.
であって, が成立する.よって

が三角不等式とSchwartzの不等式より成立する.

さらに,について,とすると,

すなわちをLipschitz定数としてLipschitz連続である.

Proposition 8.2.5

をノルムを入れた有限次元ベクトル空間とする.
(i) とすと,

(ii) とすると,

proof.

(i)

よって. 後半もほとんど同様.

(ii)

よって成立.

Proposition 8.2.6

を有限次元なベクトル空間とする.を可逆な作用素の集合とする.
(i) で,ならばは可逆である.
(ii) は開集合で,上の連続関数である
proof.

(i)
は線形だから,

故に

であればであり,仮定より
が成立する.から任意のである.すなわちは単車であり,が単車で,hは有限次元だから,は全単射.よって可逆.
(ii)
を固定する.に近く可逆とする.つまり,であるとする.このときとすると

が成立する.したがって,である.

が成立するから,によって.たしかに逆関数をとる操作は連続である.

2017年6月26日月曜日

Basic Analysis (Jiri Lebl) 28日目

CC BY-NC-SA 3.0

8.1.4 Convexity

Definition (Convex set)

をベクトル空間とする.がconvex set(凸集合)である

Example 8.1.19

について,

を満たすの集合をとすると,はconvexである.
proof.

とする.
は明らかであり,

よって示せた.

Proposition 8.1.20

を凸集合の集合族とすると,

もまた凸集合.

proof. 略

Proposition 8.1.21

がベクトル空間間の線形写像とし,を凸集合とすると,もまた凸集合である.
proof.

を任意に取る.なるがある.は凸集合だから,任意のであって,
したがって確かには凸集合である.

Definition convex full(凸包)

について,convex full(凸包)を

と定める.つまり,のconvex fullとは,を含む最小の凸集合である.

The Rust Programming Language 2nd 07日目 EnumとPattern

6. Enums and Pattern Matching

enumenumerateの略で,取りうる値の集合が有限集合である変数の型である.

Defining an Enum

例えば,現在IPアドレスはIP4vかIP6vしかないので,IPアドレスを取り扱うプログラムのIPアドレスバージョンを入れる変数は

enum IpAddrKind {
  V4,
  V6,
}

のように定めたenum型を使える.

Enum Values

IpAddrKindのinstanceは

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

のようにして定義できる.IpAddrKindは一つの型だから,このinstanceを引数に取る関数を定義できる.
fn route(ip_type: IpAddrKind) {}
この関数は

route(IpAddrKind::V4);
route(IpAddrKind::V6);

のようにして呼び出せる.

enum IpAddrKind {
  V4,
  V6,
}

struct IpAddr {
  kind: IpAddrKind,
  address: String,
}

IpAddrというstructを定義するとき,

let home = IpAddr {
  kind: IpAddrKind::V4,
  address: String::from("127.0.0.1"),
};

let loopback = IpAddr {
  kind: IpAddrKind::V6,
  address: String::from("::1"),
};

としてそのinstanceを与えられる.しかし,より簡便な方法に

enum IpAddr {
  V4(String),
  V6(String),
}

とすれば,

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::01"));

によって同じ機能を実現できる.また,

struct Ipv4Addr {
  // details elided
}

struct Ipv6Addr {

}

enum IpAddr {
  V4(Ipv4Addr),
  V6(Ipv6Addr),
}

ともできる.つまり,enumの中の変数にはstruct型も入れられる.
さらに複雑な中身をもつenumの例を与える.

enum Message {
  Quit,
  Move {x: i32, y: i32},
  write(String),
  ChangeColor(i32, i32, i32),
}

これと同じ機能をstructで実現しようとすると,

struct QuitMessage;                       // unit struct
struct MoveMessage{
  x: i32,
  y: i32,
}
struct WriteMessage(String);              // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct

と煩雑になるし,これらのstructのinstanceを引数にする関数を定義するのは手間がかかる.

structと同様,enumでもimplを使ってmethodを定義できる.例えば,

enum Message {
  // details elided.
}

impl Message {
  fn call(&self) {
    // method body would be defined here.
  }
}

let m = Message::Write(String::from("Hello"));
m.call();

と,Messageにmethodを定義して呼び出すことができる.

The Option Enum and Its Advantages Over Null Values

RustはNull,すなわちその変数が何でもないということを表現する機能を安全性のために実装していないが,その危険性を回避しつつ似たようなことをOptionによって行える.Option

enum Option<T> {
  Some(T),
  None,
}

と実装されている.これは標準ライブラリに入っている特別なenumで,Option::<T>Option::NoneOption<T>のinstanceである.
<T>generic type parameterで,Chap.10で論じる.ここでは,<T>は,OptionのinstanceがSomeの値を取っているなら,Someにはさらに任意の方の変数を入れられるということ.例えば

let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;

として,OptionSomeは整数と文字列を保持している.
また,OptionNoneを取る場合には,Someを取った場合それが保持する変数の型を指定しなければならない.
Option<T><T>の型は異なるので,直接演算できない.例えば

let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;              // xとyは違う型

は異なる型の変数を足そうとしているからコンパイルエラーが生じる.演算をするにはOption<T>Tに変換しなければならず,したがって『Nullでないと考えて操作を行ったが,実はNullであった』というありがちなバグを取り除くことができる.
enumは取る値それぞれについて行われる処理を書くべきで,それにはmatch構文がよく使われる.

The match Control Flow Operator

matchは条件分岐の構文のひとつ.
match 変数名 { パターン1 => expression1, ..., }と定義する.{}の中を,armという.
例えば

enum Coin {
  Penny,
  Nickel,
  Dime,
  Quarter,
}

fn value_in_cents(coin: Coin) -> i32 {
  match coin {
    Coin::Penny => 1,
    Coin::Nickel => 5,
    Coin::Dime => 10,
    Coin::Quarter => 25,
  }
}

matchによって複数行の処理を行いたいときは{}によってその処理を囲む.そのとき,最終行のexpressionが最終的な返り値となる.

fn value_in_cents(coin: Coin) -> i32 {
  match coin {
    Coin::Penny => {
      println!("Lucky penny!");
      1
    },
    Coin::Nicked => 5,
    Coin::Dime => 10,
    Coin::Quarter => 25,
  }
}

Patterns that Binds to Values

matchはまた,enumの値の一部をその後の処理の中で利用できる.

#[derive(Debug)]
enum UsState{
  Alabama,
  Alaska,
  // ... etc.
}

enum Coin {
  Penny,
  Nickel,
  Dime,
  Quarter(UsState),
}

fn value_in_cents (coin: Coin) -> i32 {           // coinの型はenum
  match coin {
    Coin::Penny => 1,
    Coin::Nickel => 5,
    Coin::Dime => 10,
    Coin::Quarter(state) => {
      println!("State quarter from {:?}", state); // stateの型はUsState::hoge
      25
    }
  }
}

Matching with Option<T>

上の性質から,matchによってOption<T>からTを取り出せる.Option<i32>を扱う関数を定義してみよう.

fn plus_one(x: Option<i32>) -> Option<i32> {
  match x {
    None => None,
    Some(i) => Some(i + 1),
  }
}

let five = Some(5);
let six = plus_one(five);       // iはSomeの中の値に結びつき,したがってi=5,
let none = plus_one(None);      // None => Noneから,none = None

Matches Are Exhaustive

さらにmatchは変数の取りうる値それぞれにマッチした場合の処理を明示しなければコンパイルエラーが生じる._Placeholderを使うと,パターンを網羅させる処理が簡単に書ける._は最後のarmに書き,それ以前のすべての場合にマッチが起きなかったときにマッチする.例えば

let some_u8_value = 0u8;
match some_u8_value {
  1 => println!("one"),
  3 => println!("three"),
  5 => println!("five"),
  7 => println!("seven"),
  _ => (),
}

によって,some_u8_valueが1,3,5,7のどれでもなかった場合は何もしないという処理を実現している.
matchは実現したい分岐が少ない場合にはいちいち書くのは冗長なので,そういうときはif let構文を使う.

Concise Control Flow with if let

let some_u8_value = Some(0u8);
match some_u8_value {
  Some(3) => println!("three"),
  _ => (),
}

のかわりに

if let Some(3) = some_u8_value {
  println!("three");
}

でも同じ処理を実現できる.
if letは変数の取りうる値すべてを網羅する必要はないが,そのぶんバグが起きやすくなる.
if letにはelse節を加えられる._と同じ働きをする.たとえば

let mut count = 0;
match coin {
  Coin::Quarter(state) => println!("State quarter from {:?}", state),
  _ => count += 1
}

let mut count = 0;
if let Coin::Quarter(state) = coin {
  println!("State quarter from {:?}", state);
} else {
  count += 1;
}

と同じ働きをする.

2017年6月25日日曜日

Basic Analysis (Jiri Lebl) 27日目

CC BY-NC-SA 3.0

Chapter 8. Several variables and partial derivatives

8.1 Vector spaces, linear mappings, and convexity

8.1.1 Vector spaces

次元ベクトル
と書くことにする.本質的にはは縦ベクトルとする.
やその部分集合で定義された関数を考察するが,多くの場合定義域はベクトル空間である.

Definition 8.1.1

には2つの演算,加算とスカラー倍が定義されていて,どちらの演算にも閉じているとき,(real) vector spaceという.
vector spaceの元をvectorという.

以後,はすべてベクトル空間とする.

Example 8.1.4

は,
に,,
に,
と演算を定義すればベクトル空間となる.

Example 8.1.5

多項式のなす集合をとする.演算をEx. 8.1.4と同様に定義すると,はベクトル空間となる.は無限次元のベクトル空間である.

8.1.2 Linear combinations and dimension

Definition 8.1.6

をベクトル空間とする.
,があるとき,

linear combination(線型結合)という.
について,の有限な部分集合のつくるすべての線形結合の集合をspan(はる空間)といい,と書く.

Example 8.1.7

とすると,

であって,つまり原点とを通る直線である.

Proposition 8.1.9

任意のについて,はまたベクトル空間である.

Definition 8.1.10

linearlyl independent(線形独立)

線形独立でないとき,linealy dependentという.
が線形独立かつであるとき,basis(基底)という.
のうち本のベクトルを線形独立になるように選べるが,本のベクトルを線形独立となるように選べないとき,-dimension(次元)であるという.任意のに,かならず本の線形独立なベクトルが選べるとき,の次元は無限次元であるという.

Proposition 8.1.11

の基底である時,の任意のベクトルは,

と一意に書ける.
proof.


と書けるとすると,

線形独立性から,.よって示せた.

Definition

について,の基底である.この基底をstandard basis(標準基底)という.

8.1.3 Linear mappings

この節でははベクトル空間とする.
について,でないとき,function(関数)ではなくmapping(写像)とよぶことが多い.

Definition 8.1.13

linear(線形)である

と書くことにする.が線形であるとき,linear operator(線形作用素)であるという.からへの線形写像の集合をと書き,を特にと書く.

Proposition 8.1.14

をもつとき,もまた線形である.
proof.

は全単射だから,について,なるが必ず存在する.


よって示せた.

Proposition 8.1.15

の基底における値がわかれば完全に決定できる.また,の基底とするとき,に自然に拡張できる.

proof.

有限次元空間のみ考える.
の基底とする.を定めるとが一意に決まるというのが命題の主張である.
と書ける.線形性から

が成立して,たしかにによって一意に定まる.後半はもはや明らかだろう.

Proposition 8.1.16

は有限次元で,その上の線形作用素が単射である は全射
proof.

の基底とする.が単射であるとき,
の解を考えれば,が一次独立であることがわかる.の基底だから,の任意の点はの線型結合で書ける.よっては全射である.
を全射とする.を張る.
が成立するなら,.すなわちならば

Proposiiont 8.1.17

が有限次元なら,もまた有限次元のベクトル空間である.

proof. (Exercise 8.1.11)

Ex. 8.1.5と同じ演算を導入して,ベクトル空間であることは明らかなので,有限次元であることを示す.
の基底をそれぞれとする.

なる作用素たちを定める.を張ることを示せばよい.
とする.Prop.8.1.15から,によっては決定される.
はベクトル空間だから,はそれぞれの線型結合で書ける.

となるようにを定めると,任意のについて,

が成立する.すなわち,

と書ける.したがって確かにであり,以上より命題が示せた.