2012年5月29日火曜日

初めてのコンピュータサイエンス第9章解答

第9章の解答。以下ネタバレなので、読みたくない方は立ち去りましょう。問題文は載せませんので悪しからず。必要なら買いましょう。その前にこの章の個人的なメモ。

  • setオブジェクトで保持される順序にルールはない。
  • 集合はハッシュテーブルというデータ構造に格納される。
  • 集合の要素に対し、ハッシュコードが計算される。ハッシュコードはイミュータブル(書き換え不可能なデータ)のみ持つことが許される。
  • 辞書の初期化は{}で行う。追加はappendなどではなく、辞書名[キー] = 値 という形式で指定すればOK。
  • 185ページのdict_of_dicts.pyのサンプルコードに登場する辞書でJグドールに関する項目に'霊長類研究者'(英語では'primate researcher')とあるが、この後ろに「,」がないため、編集の必要がある。おそらくバグ。

1

# -*- encoding: sjis -*-

def find_dups(data):
    h_table = {}
    for d in data:
        if d in h_table.keys(): # getメソッドを利用する方法もある
            h_table[d] += 1
        else:
            h_table[d] = 1
    ret = []
    # iteritemsはオブジェクトのコピーを作らないので高速に動かすことができる
    for (key, value) in h_table.iteritems():
        if value >= 2:
            ret.append(key)
    return ret

if __name__ == '__main__':
    data = range(10) + range(10, 5, -1) + range(3, 7)
    ret = find_dups(data)
    print ret

2

def mating_pairs(males, females):
    ''' the size of males and that of females are the same. '''
    ret = []
    # while males: is OK. this answer is a bit copmlex.
    while len(males) > 0:
        ret.append((males.pop(), females.pop()))
    return set(ret)

if __name__ == '__main__':
    males = set(['A', 'B', 'C', 'D', 'E'])
    females = set(['1', '2', '3', '4', '5'])
    print mating_pairs(males, females)

3

PDB形式のAUTHORの行に関する情報はこちらのpdfを参考にするとよいでしょう。人物名はコンマ区切り、AUTHORと人名は空白で区切られているという条件を使います。また、最後にコンマがあり人名が続かないこともありますので、そのようなことも考慮しています。

# -*- encoding: sjis -*-

import sys

def read_author(r):
    ret = set()
    for line in r:
        if not line:
            continue
        if line[:6].upper() != 'AUTHOR': # AuthorなどもOKにするため、upper()を挟んでいる
            continue
        # 仕様の都合上8文字目から作者名が始まる可能性がある。
        # strip()でいったん両端の空白を削除してから","で区切る
        line = line[7:].strip().split(",")
        print line
        for i in range(len(line)):
            if not line[i]: # AUTHORとあるが、何も続かない場合
                continue
            ret.add(line[i])
    return ret # setを返しておく

if __name__ == '__main__':
    names = set()
    for filename in sys.argv[1:]: # 外部からファイルを読み込みたい
        infile = open(filename, 'r')
        name = read_author(infile)
        names = names.union(name) # 和集合はunionである
        infile.close()
    print names

4(自信薄)

setの中に中身が変更可能なsetが入れられないのでfrozensetにして入れている。vowelは要素にfrozensetのデータを持つが、すでに通常のsetとして扱って問題ない。中身が書き換え可能なメモリアドレスにvowelsが配置されるということになる。

5

def count_values(dic):
    values = {}
    for (key, value) in dic.iteritems():
        if not value in values:
            values[value] = 1
    return len(values)

if __name__ == '__main__':
    print count_values({'red':1, 'green':2, 'blue':3})
    print count_values({'red':1, 'green':2, 'blue':1})
    print count_values({'red':2, 'green':2, 'blue':2})

6

def min_elementary_particle(dic):
    minimum = 1.1 # probability is always smaller than 1
    elem = ''
    for (key, value) in dic.iteritems():
        if value < minimum:
            minimum = value
            elem = key
    return elem

if __name__ == '__main__':
    component = {'neutrino':0.14 , 'neutron':0.55,
                 'proton':0.21, 'meson':0.03, 'muon':0.07}
    print min_elementary_particle(component)

7

def count_duplicates(dic):
    dup = {}
    for (key, value) in dic.iteritems():
        if not value in dup:
            dup[value] = 1
        else:
            dup[value] += 1

    dup_list = []
    for (key, value) in dup.iteritems():
        if value >= 2:
            dup_list.append(key)
    return len(dup_list)

if __name__ == '__main__':
    color = {'red':1, 'green':4, 'blue':2, 'orange':3, 'green':2, 'orange':6}
    print count_duplicates(color) # 2 is duplicated number.

8

例外という先の内容を無理やり利用しろということなので、試しに使ってみた。

def fetch_and_set(dic, key, new_value):
    if key in dic:
        old_value = dic[key]
        dic[key] = new_value
        return old_value
    else:
        raise KeyError("Unable to replace value for nonexistent key")

if __name__ == '__main__':
    color = {'red':1, 'green':4, 'blue':2, 'orange':3}
    print fetch_and_set(color, 'green', 100) # 4
    try:
        fetch_and_set(color, 'black', 3) # KeyError exception
    except KeyError:
        print "KeyError occurred!"

9

問題の仕様にはないのですが、辞書には必ずしもキーがR、G、Bだけではないものと仮定します。RGB以外は無視するものとします。

def is_balanced(dic):
    total = 0
    for (key, value) in dic.iteritems():
        if key == 'R' or key == 'G' or key == 'B':
            total += value

    if abs(total - 1.0) < 10 ** -15: # almost equal to 1
        return True
    else:
        return False

if __name__ == '__main__':
    color = {'R':0.1, 'G':0.4, 'B':0.5}
    print is_balanced(color) # True
    color = {'R':0.1, 'G':0.4, 'X':0.2, 'B':0.5}
    print is_balanced(color) # True
    color = {'R':0.1, 'G':0.4, 'B':0.2}
    print is_balanced(color) # False

10

両方の辞書に同じキーと同じ値が含まれている対のみ返します。同じキーだが値が違うという場合はdict_intersectの返り値の辞書には含まないものとします。

def dict_intersect(dic1, dic2):
    k2 = dic2.keys()
    ret = {}
    for (k1, v1) in dic1.iteritems():
        if k1 in k2 and v1 == dic2[k1]:
            ret[k1] = v1
    return ret

if __name__ == '__main__':
    c1 = {'red':1, 'green':4, 'blue':2, 'orange':3, 'green':2, 'orange':6}
    c2 = {'red':1, 'green':5, 'blue':2, 'yellow ':3}
    print dict_intersect(c1, c2) # blue and red are extracted

11

scientists = {
  'jgoodall'  : {'surname'  : 'Goodall',
                 'forename' : 'Jane',
                 'born'     : 1934,
                 'died'     : None,
                 'notes'    : 'primate researcher', # distributed sample code lacks comma.
                 'author'   : ['In the Shadow of Man', 'The Chimpanzees of Gombe']},
  'rfranklin' : {'surname'  : 'Franklin',
                 'forename' : 'Rosalind',
                 'born'     : 1920,
                 'died'     : 1957,
                 'notes'    : 'contributed to discovery of DNA'},
  'rcarson'  : {'surname'  : 'Carson',
                 'forename' : 'Rachel',
                 'born'     : 1907,
                 'died'     : 1964,
                 'notes'    : 'raised awareness of effects of DDT',
                 'author'   : ['Silent Spring']}
}

def db_headings(dic):
    ret = []
    for values in dic.values():
        ret += values.keys()
    return set(ret)

if __name__ == '__main__':
    print db_headings(scientists)

12

scientistsは上のデータを利用することにします。rfranklinは他の二つとキーが異なるため内部の辞書は常に一定にはなりません。そのため、db_consistent関数にscientistsを渡すと、Falseになります。if elem.symmetric_difference(set(values)):は要素数が0でなければという意味になります。

def db_consistent(dic):
    first = dic.keys()[0]
    elem = set(dic[first].keys())
    for values in dic.values():
        if elem.symmetric_difference(set(values)):
            return False
    return True

if __name__ == '__main__':
    print db_consistent(scientists)

13

空行で機能が大まかに区切られているので、それを1つのまとまりとしてモジュールにするというだけですね。

import sys

def create_dic(files):
    count = {}
    for filename in sys.argv[1:]:
        infile = open(filename, 'r')
        for line in infile:
            name = line.strip()
            count[name] = count.get(name, 0) + 1
        infile.close()
    return count

def reverse_dict(dic):
    freq = {}
    for (name, times) in dic.iteritems():
        print name, times
        if times in freq:
            freq[times].append(name)
        else:
            freq[times] = [name]
    return freq

def print_dic(dic):
    for key in sorted(dic):
        print key
        for name in dic[key]:
            print ' ', name

if __name__ == '__main__':
    dic = create_dic(sys.argv[1:])
    r_dic = reverse_dict(dic)
    print_dic(r_dic)

14

a)、b)の解答は以下の通り。

def sparse_add(dic1, dic2):
    ret = dic1.copy()
    keys = set(dic1.keys() + dic2.keys())
    for key in keys:
        ret[key] = 0
        if key in dic1.keys():
            ret[key] += dic1[key]
        if key in dic2.keys():
            ret[key] += dic2[key]
    return ret

def sparse_dot(dic1, dic2):
    ret = 0
    for (key, value) in dic1.iteritems():
        if key in dic2:
            ret += value * dic2[key]
    return ret

if __name__ == '__main__':
    v1 = {0:1, 6:3, 7:-1}
    v2 = {1:1, 2:2, 6:1, 7:-1}
    print sparse_add(v1, v2) # {0: 1, 1: 1, 2: 2, 6: 4, 7: -2}
    print sparse_dot(v1, v2) # 4

c)に関しては、疎ベクトルの長さが、辞書の長さになるのか、それとも最後に現れた辞書のキーの値+1になるのかということが曖昧になっている。0を値に持つキーをどのように扱うかということが、「疎ベクトルの長さ」という言葉だけでははっきりしないということである。

0 件のコメント:

コメントを投稿

フォロワー

ブログ アーカイブ

ページビューの合計