B07 [宿題]

B07_1 (スクリプト)

ねらい: 辞書型の基本的な操作を,繰り返し処理と組み合わせる.

この課題は A07_2 の続きである.手元の課題 A07_2 のソースコードをコピーしてから開始せよ.

課題A07_2のプログラムでは,1回の問い合わせを行うとプログラムが終了していた.連続した問い合わせに対応できるように,プログラムを修正しよう(世の中の各種サーバは,要求を待ち受けて処理をして結果を返す処理を繰り返すことが一般的である).

  1. 【繰り返し実行】 課題A07_2で実装された処理,すなわち「登録されている件数の表示,国番号の入力,結果の表示」を,繰り返し実行するようにせよ.国番号に0が入力されると終了するようにせよ.

    意欲のある人は,教科書5.5節のエラー処理を実装して,空文字列や整数に変換できない文字列の入力でも終了するようにせよ.

    復習: 繰り返しには for 文と while 文があるが,本課題の場合は繰り返し対象や回数が指定されていないことを考えて,いずれが適しているか判断せよ.ある条件を満たした場合に繰り返しから抜ける方法についても復習せよ.

  2. 【新規登録】 課題A07_2で作成したelse節の処理を改良する.国番号が country_code のキーとして存在していない場合は,続いて国名の入力をうながし,新たな国番号と国名を辞書 country_code に登録して,国番号の入力待ちに戻るようにする.具体的には,「国番号 xx は登録されていません」に続いて,「国名を入力してください」と表示して国名を得て,入力された国名を辞書 country_code に追加せよ.

    なおプログラムをテストする際には,country_code に登録されていない国番号と国名の組が必要であるが,https://countrycode.org/ を参照して好みで選択せよ.

  3. 【動作確認】 ステップ2.が完成したら,新しく登録した国番号を入力し,適切に登録されていることを確認せよ.

なお,このようにして更新された country_code の内容は,一度プログラムを終了すると失われる. そのため,本プログラムは実用的とは言えない. 再度実行した際にも利用できる実用的なプログラムを作成するには,ファイル等に辞書 country_code の内容を保存しておく必要がある. ファイルの読み書きについては,後日学習する.


B07_2 (スクリプト)

ねらい: 辞書のキーと値の組に対する処理を行う.

この課題は A07_2 の関連課題である.

課題 A07_2 で使用した,国番号をキーとし,国名を値として格納している辞書 country_code を元に,国名をキーとして国番号を値として格納する辞書 country_name を作成せよ.

次に,作成した辞書 country_name に登録されている項目を,国名順に並べ替え,国名と国番号として表示せよ. ただし,与えられている情報のみで完全に読み方の順序にすることは困難である. これは,日本語文字列のソートはUnicodeによって割り振られている文字コードを基準として行われるためである. 本課題では,カタカナの国名が50音順で並んだ後に,漢字の国名が続けばよく,漢字の国名は読み方の50音順になっている必要はない.

本課題のプログラムの作成にあたっては,以下の知識が必要になると思われる.

辞書型の変数を list() を用いてリスト型にキャストすると,辞書のキーを要素とするリストが得られる.

インタラクティブシェルで挙動を確かめて使用せよ.

>>> d = {3: 'three', 2: 'two', 1: 'one'}
>>> d
{3: 'three', 2: 'two', 1: 'one'}
>>> l = list(d)
>>> l
[3, 2, 1]
>>>

B07_3 (スクリプト)

ねらい: プログラムの挙動をソースコードから推測し,適切な動作を行うように修正する.

この課題は A07_4 と同じデータを使用する.

以下のプログラムは,西暦を入力すると,対応する和暦を表示するプログラムである.仕様は以下の通りである.

しかしながら,このプログラムには誤りが数件ある.これらを全て修正し,正しく変換と表示がなされるようにせよ.

import sys

japanese_era = {
    '明治': (1868, 1912),
    '大正': (1912, 1926),
    '昭和': (1926, 1989),
    '平成': (1989, 2019),
    '令和': (2019, 2023)
}

year = input('西暦 = ')
if year < 1868 and 2023 < year:
    print('範囲外です.')
    sys.exit() # sys.exit() はプログラムをその時点で終了する

for key in japanese_era:
    period = japanese_era(key)
    if year == period[0]:
        print(f'{key}元年')
    elif year < period[1]:
        print(f'{key}{year - period[0]}年')

なお,sys.exit() はプログラムをその時点で終了する命令であり,この命令は本実習では初出かもしれないが,誤りではない.

実行例を以下に示す.ただし,以下の入力に対して正しい結果が出されたとしても,全ての誤りが修正できたとは限らないので,十分なテストを実施せよ.

> python japanese-era-name02.py
西暦 = 2021
令和3年

> python japanese-era-name02.py
西暦 = 1800
範囲外です.

> python japanese-era-name02.py
西暦 = 2019
平成31年
令和元年

B07_4 (スクリプト)

ねらい: 辞書型を用いて,複数の対象の個数を数える処理を行う.

観光に行きたい国についてのアンケートを実施し,以下の回答群を得たとする. 回答は国名を要素とするリスト answer で与えられている(※アンケート結果は架空のものです).

answer = ['エジプト', 'クロアチア', 'オーストラリア', 'エジプト', '中国', 'オーストラリア',
    'インド', '中国', 'エジプト', 'イタリア', 'チェコ', '中国', 'チェコ', 'スイス',
    'オーストラリア', '中国', 'タイ', 'インド', 'イギリス', 'スイス', 'チェコ', '中国',
    'タイ', 'スペイン', 'オーストラリア', 'イタリア', 'オーストラリア', 'スイス', 'イタリア',
    'アメリカ', 'スイス', 'スペイン', '中国', 'イタリア', 'イタリア', 'イギリス', 'チェコ',
    'フランス', 'フランス', '中国', 'スペイン', 'スペイン', '中国', 'スイス', 'イタリア',
    'エジプト', '中国', 'フランス', 'インド', 'スペイン']

このアンケート結果について,国名をキー,出現回数を値とする辞書を用いて集計し,各国の回答者数を表示するプログラムを作成せよ.


B07_5 (スクリプト,オプション)

ねらい: 実用的な処理の例として,ハフマン符号のエンコードとデコードを行う.

ハフマン符号化 Huffman coding とは,出現頻度に応じて異なる長さの符号を割り当てる符号化方式である. 符号化された記号列に,前から順番にあてはまる符号を探すと,必ず一意に復号する(平文に直す)ことができるようになっている. ハフマン符号の詳細についてはこれ以上説明しないので,情報理論の教科書等で各自確認すること.

英語のアルファベットおよび空白の出現頻度について調査した結果から,以下の符号表が作成されているとする.

huffman_table = {
    ' ': '111',
    'E': '010',
    'T': '1101',
    'A': '1011',
    'O': '1001',
    'I': '1000',
    'N': '0111',
    'S': '0011',
    'H': '0010',
    'R': '0001',
    'L': '10101',
    'D': '01101',
    'C': '00001',
    'U': '00000',
    'F': '110011',
    'M': '110010',
    'W': '110001',
    'Y': '101001',
    'P': '101000',
    'G': '011001',
    'B': '011000',
    'V': '1100000',
    'K': '11000011',
    'X': '110000100',
    'J': '1100001011',
    'Q': '11000010101',
    'Z': '11000010100'
}

この割り当て表を用いて,以下の文を暗号化せよ.

MASTERY FOR SERVICE

また,以下の暗号文を平文に直せ.

11010010010111110000101010000010000000111000011111011000000110011100010111111110011100111000010011111000010110000011001010100000111111001110000001000011111101001001011110101101111000010100101001111011011001011001

プログラムは2つの処理を順次行い,それぞれの結果を出力するようにせよ.


B07_6 (スクリプト,オプション)

ねらい: 実用的な処理の例として,クラスタリング法の一つである k-means 法を試す.

2次元空間に30個の点が分布しており,点の座標が以下のようにタプルのリストとして与えられている.

points = [
    ( 25.71, -17.76),
    ( 31.54, -37.34),
    (  1.71,   6.69),
    ( 22.86, -24.20),
    (-56.94,  54.55),
    (-62.99,  33.33),
    (-32.99, -19.10),
    (-60.18,  30.81),
    (-41.02,  41.90),
    (  3.82, -50.54),
    (-36.74,  32.06),
    (-51.59,  56.53),
    (-53.78, -14.58),
    ( 39.57,  57.17),
    ( 64.28,  47.35),
    (-24.36,  37.20),
    (-44.88,  69.37),
    ( -1.88, -52.88),
    ( 18.44, -12.62),
    ( 75.38,  42.73),
    ( 17.94,  -6.43),
    ( 56.68,  62.96),
    (-82.40,  33.01),
    ( 46.75,  64.75),
    ( 73.11,  18.29),
    ( -1.45, -23.62),
    ( 86.82,  19.18),
    ( 40.62,  53.85),
    ( 37.37,  47.17),
    ( 61.55,  32.42)
]

点の分布

これを3つのクラスタに分類したい.クラスタの番号を0,1,2とする.

以下の手順に従って,各点がどのクラスタに属しているかを決定せよ.

  1. 各点に対して,ランダムなクラスタ(0 あるいは 1 あるいは 2)を割り振る.
  2. 各クラスタの中心点の座標を計算する.中心点として,クラスタに属する点の重心(座標の平均)を用いる.
  3. 各点に対して,得られた3つのクラスタの中心点との距離を計算し,最も近い中心点を持つクラスタに属しているとして,新たなクラスタを割り振る.
  4. ステップ3の処理によって,属しているクラスタに変化があった点を数える.
  5. 変化した点があればステップ2に戻り処理を続ける.無ければクラスタへの分類が終了したとして,各点の属しているクラスタを出力する.

結果は以下のように出力せよ.先頭はクラスタ番号であるが,本手法ではクラスタ番号が何番になるかは特定できない.

2: ( 25.71, -17.76)
2: ( 31.54, -37.34)
  :

なお,出力結果が妥当かどうかは,2次元平面上へのプロットを行えば判断できる. Python ではグラフ描画のためのモジュールとして Matplotlib が広く使用されている. Matplotlib の使用方法に関する情報は豊富にあるため,試してみよ. あるいは,出力された結果を元に,別途グラフを描画するアプリケーションで描画してもよい.