PythonでCounting Sortした

たまには真面目に記事を書きます.

今大学でやっているアルゴリズムの授業で書いたものです.

実行速度で差をつけろ!というときは,CやC++などで書くのがメジャーで,Pythonスクリプト言語で,逐次実行というところからすると,やはり遅くなってしまいます.

僕も大体の部分ではCを使って書いていたのですが,今回始めてPythonでじっくり書きました.

この大学のアルゴリズムの講義では会津大学のAOJというサイトを使用し,そこに用意されている問題をACさせることが,授業の課題という感じです.

今回の問題はこちら

問題を要約すると,入力された数字を計数ソートにより昇順に並べ換えるという問題です.

これをするためには Counting sort という手法で数字を並べ換える必要があります.

やり方としては以下の通り

  1. キー(配列Aのデータ)を数え上げるための配列Cとソートのための作業用配列Bを用意する
  2. 配列Cを利用して,配列Aのデータの出現頻度を数える
  3. 配列Cが保持したキーの累積度数分布を求める
  4. (配列Cの)累積度数分布に従って配列Aから配列Bにデータをコピーする
  5. 必要な場合,配列Bから元の配列Aにデータをコピーする

累積度数分布って何ですか?

累積度数分布とは,その値がいくつあるか?という分布です. 例として,以下のような数列が与えられたとき.

1 5 2 3 1 1 3 3

この場合,要素数は8,分布が均等であれば,1~8がそれぞれ1つずつ存在することになります.

実際は重複がいくつかあり,この場合の累積度数分布は f:id:flying_hato_bus:20170602002701p:plain

こうなる,あとはこのリストを分布に沿ってコピーすればいいという感じです.

実際のコードはこんな感じ,sort内のmはカウントをする配列の数になる.count = [0]*(m)の表記は,これだけで要素を0で初期化が出来ます.

ここまで言って何なんですが,このコードの中では累積度数分布を使用していません.

count[a] += 1 の部分で出現回数の計算をし,

for c in range(count[a]):
            array[i] = a
            i += 1

で値の出現回数だけ繰り返して元のリストに値をコピーしています.

サンプルにするとこんな感じ,print部分でごちゃごちゃしているのは出力に制限があり,それに当てはめた結果がこのようになりました.

ただただ出力したい場合は print(li)で出力が可能です.

def counting_sort(array, maxval):
    m = maxval+100
    count = [0] * (m)
    for a in array:
        count[a] += 1
    i = 0
    for a in range(m):
        for c in range(count[a]):
            array[i] = a
            i += 1
    return array
 
if __name__ == '__main__':
    N = int(input())
    l = list(map(int, input().split()))
    a = 0
    li = counting_sort(l, N)
    for a in range(N-1):
        print(li[a], end=" ") 
    print(li[N-1])

実行例

入力1

6 1 1 4 5 1 4

出力1

1 1 1 4 4 5

入力2

10 2 2 2 1 1 5 5 9 1 1

出力2

1 1 1 1 2 2 2 5 5 9

入力3

100 0 33 43 62 29 0 8 52 56 56 19 11 51 43 5 8 93 30 66 69 32 17 47 72 68 80 23 49 92 64 69 51 27 90 24 35 20 44 10 62 84 63 1 10 36 76 31 29 97 75 91 90 44 34 25 29 30 27 26 43 34 4 60 49 20 56 32 72 13 90 9 19 5 95 49 27 19 97 24 96 49 56 84 93 45 7 6 9 54 52 65 83 38 1 90 30 37 95 56 63

出力3

0 0 1 1 4 5 5 6 7 8 8 9 9 10 10 11 13 17 19 19 19 20 20 23 24 24 25 26 27 27 27 29 29 29 30 30 30 31 32 32 33 34 34 35 36 37 38 43 43 43 44 44 45 47 49 49 49 49 51 51 52 52 54 56 56 56 56 56 60 62 62 63 63 64 65 66 68 69 69 72 72 75 76 80 83 84 84 90 90 90 90 91 92 93 93 95 95 96 97 97

このように間違いはなく,実際にソートがされているということが分かる.