2017年6月28日水曜日

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によって,高速に最適解を得られる場合がある

0 件のコメント:

コメントを投稿