4.整列のアルゴリズム.

Slides:



Advertisements
Similar presentations
4.3 マージソート.
Advertisements

4.ソート問題とアルゴリズム 4-1.ソート問題について 4-2.簡単なソートアルゴリズム 4-3.高度なソートアルゴリズム
離散システム特論 整列(sorting)アルゴリズム 2.
ヒープソートの演習 第13回.
アルゴリズムイントロダクション第2章 主にソートに関して
4.ソート 4-1.ソート問題について 4-2.簡単なソートアルゴリズム 4-3.高度なソートアルゴリズム
アルゴリズムとデータ構造 2012年6月27日
第12回 ソート(3): シェルソート、クイックソート
アルゴリズムとデータ構造1 2008年7月22日
第10回 整列 ~ バブルソート,挿入ソート,選択ソート~
近似アルゴリズム 第10章 終了時刻最小化スケジューリング
第11回 整列 ~ シェルソート,クイックソート ~
データ構造とアルゴリズム論 第5章 整列(ソート)のアルゴリズム
第11回 整列 ~ バケットソート,基数ソート,ヒープソート~
データ構造とアルゴリズム論 第6章 探索のアルゴリズム
4-3:高度なソートアルゴリズム① (分割統治法にもとづくソート)
アルゴリズムとデータ構造 第6回演習解答 2015/11/18実施 アルゴリズムとデータ構造 2015.
2009/10/9 良いアルゴリズムとは 第2講: 平成21年10月9日 (金) 4限 E252教室 コンピュータアルゴリズム.
第10回 ソート(1):単純なソートアルゴリズム
データ構造とアルゴリズム 分割統治 ~ マージソート~.
岩井 儀雄 コンピュータ基礎演習  ー探索、整列ー 岩井 儀雄
データ構造と アルゴリズム 第八回 知能情報学部 新田直也.
データ構造と アルゴリズム 第十一回 理工学部 情報システム工学科 新田直也.
第11回 整列 ~ シェルソート,クイックソート ~
7-3.高度な木 (平衡木) AVL木 平衡2分木。回転操作に基づくバランス回復機構により平衡を保つ。 B木
2009/10/16 いろいろなデータ構造 第3講: 平成21年10月16日 (金) 4限 E252教室 コンピュータアルゴリズム.
定兼邦彦 今井浩 東京大学理学系研究科 情報科学専攻
アルゴリズムとデータ構造1 2006年6月16日
情報工学概論 (アルゴリズムとデータ構造)
アルゴリズムとデータ構造1 2005年7月15日
ソートアルゴリズムの種類 選択ソート (selection sort) バブルソート (bubble sort)
情報工学概論 (アルゴリズムとデータ構造)
わんくま同盟茶藝部顧問 Microsoft MVP for VC episthmh
データ構造とアルゴリズム論 第6章 整列(ソート)のアルゴリズム
アルゴリズムとデータ構造 第3章 ヒープ 6月10日分
第9回 優先度つき待ち行列,ヒープ,二分探索木
アルゴリズムとデータ構造1 2005年7月1日
クイックソート.
大岩 元 慶応大学環境情報学部 再帰 データ構造とプログラミング(8) 大岩 元 慶応大学環境情報学部
基本情報技術概論(第6回) 埼玉大学 理工学研究科 堀山 貴史
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング 4 整列アルゴリズム.
2009/10/23 整列アルゴリズム (1) 第4講: 平成21年10月23日 (金) 4限 E252教室 コンピュータアルゴリズム.
プログラミング 4 探索と計算量.
データ構造とアルゴリズム論 第5章 整列(ソート)のアルゴリズム
プログラミング 4 木構造とヒープ.
アルゴリズムとデータ構造1 2006年7月11日
アルゴリズムとデータ構造 2011年6月23日
プログラミング演習Ⅲ- Sorting Quick Sort and Merge Sort
6.データ構造入門 6-1.連結リスト(Linked List) 6-2.スタック(Stack) 6-3.キュー(Queue)
5.サーチ 5-1.線形探索 5-2.2分探索 5-3.ハッシュ.
データ構造とアルゴリズム (第5回) 静岡大学工学部 安藤和敏
第9回 優先度つき待ち行列,ヒープ,二分探索木
アルゴリズムとデータ構造 2012年7月2日
アルゴリズムとデータ構造 2011年6月28日
基本情報技術概論(第6回) 埼玉大学 理工学研究科 堀山 貴史
情報工学概論 (アルゴリズムとデータ構造)
アルゴリズムとデータ構造 2013年7月2日
ソートのプログラムの流れ 配列の中身を小さい順に並び替える a[1],a[2],…a[n]の値を順に出力する
平面走査法を使った 一般線分の 交点列挙アルゴリズム
4.プッシュダウンオートマトンと 文脈自由文法の等価性
アルゴリズムとデータ構造 2012年6月25日
ヒープソート.
ヒープソート.
参考:大きい要素の処理.
関数と再帰 教科書13章 電子1(木曜クラス) 2005/06/22(Thu.).
第10回 関数と再帰.
プログラミング入門2 第5回 配列 変数宣言、初期化について
7.木構造 7-1.データ構造としての木 7-2.2分探索木 7-3.高度な木.
プログラミング論 バイナリーサーチ 1.
Presentation transcript:

4.整列のアルゴリズム

整列(ソート) データ データ k,a,l,c,d,s 5,3,8,1,6,21,11 ソートアルゴリズム ソートアルゴリズム 1,3,5,6,8,11,21 a,c,d,k,l.s

内部整列と外部整列 CPU CPU 高速アクセス 高速アクセス データの一部 全データ メモリ メモリ 低速アクセス 全データ ディスク

仮定と要求 内部整列 どのデータにも均等な時間でアクセスできる。 できるだけ高速に整列したい。 (理想的なRAMモデル上のアルゴリズムではこっち) 外部整列 CPU-メモリ間のデータ転送速度より、 ディスク-メモリ間のデータ転送速度が極端に遅い。 全体の整列をできるだけ高速にしたい。 (ディスクーメモリ間のデータ転送をあまり行わないように する。)

ソートアルゴリズムの種類 バブルソート 挿入ソート 選択ソート ヒープソート :教科書に掲載 クイックソート マージソート バケットソート 基数ソート

ソートアルゴリズムの分類 原理 比較による 比較によらない バブルソート O(n^2) 選択ソート バケットソート 時間量(速度) 挿入ソート 基数ソート クイックソート 計算量はO(n) ヒープソート O(n log n) だけど条件付き マージソート

入出力形態 5 3 8 1 4 13 9 2 入力: 配列A A[0] A[1] A[i] A[n-1] n 個 2 3 4 5 8 9 (終了状態): 配列A A[0] A[1] A[n-1] n 個 連結リストで入力されるとしてもいいのですが、 配列に入力データがあるとして説明します。

バブルソート 方針 隣同士比べて、小さいほうを上(添字の小さい方)に 順にもっていく。 先頭の方は、ソートされている状態にしておく。 これらを繰り返して、全体をソートする。

バブルソートの動き1 A 5 1 5 5 5 5 5 5 5 3 3 3 3 3 1 3 1 3 3 2 8 8 8 8 1 8 8 8 1 1 1 1 8 1 3 2 2 4 4 2 2 2 4 4 非交換 4 4 4 4 4 5 13 2 13 13 13 2 13 13 13 13 6 9 交換 9 9 9 9 9 9 9 7 2

バブルソートの動き2 ソート済み A 1 1 1 1 1 1 1 1 5 2 2 2 2 2 1 2 2 繰り返し交換適用 5 2 3 3 1 1 1 5 2 2 2 2 2 1 2 2 繰り返し交換適用 5 2 3 3 3 3 3 3 3 3 5 4 4 4 8 4 4 3 8 4 5 5 5 4 2 5 5 4 5 4 8 8 8 8 8 8 9 9 9 9 9 9 9 6 13 13 13 13 13 13 7 9 13 13

バブルソートの実現1 /*交換用の関数 A[i]とA[j]を交換する。*/ void swap(int i ,int j, datatype *A) { datatype temp; /* データの一次保存用*/ temp=A[i]; A[i]=A[j]; A[j]=temp; return; }

バブルソートの実現2 /* バブルソート*/ void bubble(datatype *A) { int i,j; /* カウンタ*/ /* バブルソート*/ void bubble(datatype *A) { int i,j; /* カウンタ*/ for(i=0;i<n;i++) for(j=n-1;j>i;j--) if(A[j-1]>A[j]) swap(j-1,j,A); } return;

バブルソートの計算量 1回目の外側のループで、n-1回の比較と交換 2回目 n-2 ・ n-1回目の外側のループで、1回の比較と交換 よって、 時間量 のアルゴリズム

選択ソート 方針 先頭から順に、その位置に入るデータを決める。 (最小値を求める方法で選択する。) その位置のデータと選択されたデータを交換する。 これらを繰り返して、全体をソートする。 ソート済み 残りのデータで最小値を選択

選択ソートの動き1(最小値発見) 探索済み A 5 5 5 5 5 5 5 5 1 3 1 3 3 3 3 3 3 3 3 2 8 8 8 5 5 5 5 5 5 5 5 1 3 1 3 3 3 3 3 3 3 3 2 8 8 8 8 8 8 8 8 8 未探索 1 3 1 1 1 1 1 1 1 5 4 4 4 4 4 4 4 4 4 4 5 13 13 13 13 13 13 13 13 13 6 9 9 9 9 9 9 9 9 9 7 2 2 2 2 2 2 2 2 2 min=0 min=1 min=1 min=3 min=3 min=3 min=3 min=3

選択ソートの動き2 A 済 1 1 1 1 1 1 1 1 2 2 2 2 2 3 2 2 1 2 3 3 3 3 3 8 8 3 最小値発見 4 4 4 4 4 3 5 5 5 5 5 5 5 5 4 4 4 4 5 13 13 8 8 8 13 13 13 9 9 9 9 9 6 9 9 9 8 8 3 8 13 13 13 7 2 min=7 min=7 min=4 min=4 min=7 min=6 min=7

選択ソートの実現1 (最小値を求めるアルゴリズム) /*選択用の関数、A[i]からA[j]をまでの最小値を求める*/ int select_min(int i ,int j, datatype *A) { int k; /* カウンタ */ int min; /* 仮の最小値の添字*/ min=i; for(k=i;k<=j;k++) if(a[min]>a[k]){min=k;} } return k;

選択ソートの実現2 /* 選択ソート*/ void select(datatype *A) { int i; /* カウンタ*/ /* 選択ソート*/ void select(datatype *A) { int i; /* カウンタ*/ int min; /* 最小値の添字*/ for(i=0;i<n;i++) min=select_min(i,n-1,A); swap(i,min,A); } return; なお、説明の都合上、関数select_minを作ったが、 関数呼び出しで余分に時間がとられるので、 実際は2重ループにするほうが速いと思われる。 (でも、オーダーでは、同じ。)

選択ソートの計算量 1回目のselect_minで、n-1回の比較 2回目 n-2 ・ n-1回目のselect_minで、1回の比較 よって、 回の比較 交換は、n回 時間量 のアルゴリズム

挿入ソート 方針 先頭の方は、ソート済みの状態にしておく。 未ソートのデータを、ソート済みの列に挿入し、 ソート済みの列を1つ長くする。 これらを繰り返して、全体をソートする。 未ソートデータ ソート済み

挿入ソートの動き ソート済 A 5 3 3 1 1 1 1 1 3 5 5 3 1 3 3 3 2 2 5 4 8 8 8 4 4 3 未ソート 1 1 1 8 5 3 5 5 4 8 8 8 4 4 4 4 4 5 5 9 8 13 13 13 13 13 13 13 6 9 9 9 9 9 9 9 2 7 2 2 2 2 2 2 13

挿入ソートの実現1 (挿入位置を求める) /*挿入位置を見つける関数、 A[0]からA[i-1]までソート済みのとき、 int find_pos(int i ,datatype *A) { int j; /* カウンタ */ for(j=0;j<i;j++) if(A[j]>A[i]){break;} } return j;

挿入ソートの実現2 /* 挿入ソート*/ void insert(datatype *A) { int i,j; /* カウンタ*/ /* 挿入ソート*/ void insert(datatype *A) { int i,j; /* カウンタ*/ int pos; /* 挿入位置の添字*/ for(i=0;i<n;i++) pos=find_pos(i,A); for(j=n-1;j<pos;j--) swap(j-1,j,A); } return;

挿入ソートの計算量 n-1回目の外側のループで、n-1回の比較あるいは交換 n-2回目 n-2回の ・ 1回目 1回の比較あるいは交換 1回目 1回の比較あるいは交換 よって、 比較と交換回数の合計は、 時間量 のアルゴリズム (挿入ソートを元に高速化した、シェルソートっていうものもあるが省略。)

ヒープソート 方針 ヒープを使ってソートする。 先頭から順にヒープに挿入し、データ全体をヒープ化する。 最大値を取り出して、最後のデータにする。 13 2 1 4 9 4 5 3 2 6 3 8 5 7 1

ヒープソートの動き前半1 配列 ヒープ 1 2 3 4 5 6 7 5 13 A 3 8 1 4 9 2 5 5 13 3 8 1 4 9 2 ヒープ 未ヒープ 5 5 13 3 8 1 4 9 2 1 ヒープ 未ヒープ 3

ヒープソートの動き前半2 配列 ヒープ 1 2 3 4 5 6 7 8 13 A 8 3 5 1 4 9 2 2 1 3 5 ヒープ 8 13 A 8 3 5 1 4 9 2 2 1 3 5 ヒープ 未ヒープ ちょっと省略 1 2 5 4 2 3 13 8 9 4 6 1 2 3 4 5 6 7 A 13 4 9 2 3 5 8 1 7 ヒープ

ヒープソートの動き後半1 13 1 2 3 4 5 6 7 2 1 9 4 A 13 4 9 2 3 5 8 1 4 5 3 2 6 3 ヒープ 5 8 7 最大要素を、ヒープから削除し 後ろに挿入 1 9 2 1 2 3 4 5 6 7 1 4 8 9 A 4 8 2 3 5 1 13 4 5 3 2 6 3 5 1 ヒープ ソート済

ヒープソートの動き後半2 1 2 3 4 5 6 7 8 8 2 A 4 5 2 3 1 9 13 1 4 5 ヒープ ソート済 4 5 3 2 3 1 ちょっと省略 3 8 A 1 2 4 5 9 13 ソート済

ヒープソートの実現 void heap_sort(datatype *A) { int i; /* カウンタ */ datatype max; /*最大値*/ /* ヒープ化 */ for(i=0;i<n;i++){insert_heap(A[i],A);} /* 最大値を順に後ろに挿入*/ for(i=n-1;i>=0;i--) max=delete_max(A); A[i]=max; } return ;

ヒープソートの計算量 ヒープ化の部分 操作insert_heap(A) 1回あたり、時間量 n回行うので、時間量 最大値発見と移動の部分 操作delete_max(A) 1回あたり、時間量 n回行うので、時間量 これらは、1づつ行われるので、 時間量 のアルゴリズム (実は、ヒープ化の部分は、 の時間量で実現する方法もあるが、省略)

マージソート 方針 問題を小分けにして、あとで組み合わせる。(分割統治法) 小分けした問題は、再帰的にソートする。 もしソートされた2つの配列があれば、 それらのそれらを組み合わせて、 大きいソートの列をつくる。(マージ操作) B 1 3 5 8 3 8 A 1 2 4 5 9 13 C 2 4 9 13

マージの動き B 1 3 5 8 A C 2 4 9 13 B 1 3 5 8 A 1 C 2 4 9 13 ソート済み B 1 3 5 8 A 1 2 C 2 4 9 13

分割 もし2つのソート列があったら、 マージ操作によって、長いソート列がえられることが わかった。 どうやって、2つのソート列を作るのか? おなじ問題で、 問題のサイズが小さくなっていることに 注意する。 列を二等分にして、再帰的にソートする。

マージソート動き前半(分割) 3 1 2 4 5 6 7 13 A 5 3 8 1 4 9 2 A[0]からA[3]まで ソートして。 13 A 5 3 8 1 4 9 2 A[0]からA[3]まで ソートして。 A[4]からA[7]まで ソートして。 1 2 3 4 5 6 7 13 5 3 8 1 4 9 2 m_sort(0,1,A) m_sort(2,3,A) 1 2 3 6 7 4 5 5 3 13 9 2 8 1 4 6 1 3 2 5 7 4 13 5 3 8 1 4 9 2

マージソート動き後半(マージ) 1 2 3 4 5 7 6 13 5 3 8 1 4 9 2 marge 6 4 5 7 2 3 1 6 7 5 7 6 13 5 3 8 1 4 9 2 marge 6 4 5 7 2 3 1 6 7 13 2 9 4 1 8 3 5 1 2 3 4 5 6 7 8 1 3 5 2 4 9 13 5 1 2 3 4 6 7 A 9 1 2 3 4 5 8 13

マージの実現 /* 配列Bと配列Cを配列Aにマージする。概略です。細かい部分は省略*/ void marge(int posA,datatype *A,datatype *B ,int numB,datatype *C,int numC) { int iA=0,iB=0,iC=0; /* カウンタ*/ while(iA<=numB+numC-1) if(B[iB]<C[iC]) A[posA+iA]=B[iB]; iB++; } else A[posA+iA]=C[iC]; iC++ iA++; return;

マージソートの実現 /*概略です。細かい部分は省略*/ void marge_sort(int left,int right,datatype *A) { int mid; /* 配列の中央 */ datatype B[n],C[n]; /*作業用の配列*/ if(left==right)return; mid=(left+right)/2; marge_sort(left,mid,A); marge_sort(mid,right,A); /*ここに、A[left]からA[mid]を配列Bに、 A[mid]からA[right]を配列Cにコピーする処理を書く。 */ marge(left,A,mid-left,B,right-mid,C); return; }

マージソートの計算量 解析を簡単にするため、データを 個あると仮定します。 まず、マージの計算量 を考えます。 解析を簡単にするため、データを 個あると仮定します。 まず、マージの計算量 を考えます。 明らかに、出来上がるソート列の長さに比例した時間量です。 マージソートの時間量を とします。 以下の再帰式を満たします。 これを解いて、

クイックソート 方針 問題を小分けにして、あとで組み合わせる。(分割統治法) 前半部分は特定要素(ピボット)より小さく、 後半部分はピボットより大きく分割する。 ピボットの位置を確定し、 小分けした問題は、再帰的にソートする。 1 2 3 4 5 6 7 5 13 A 3 8 1 4 9 2 ピボット 4 A 3 2 1 5 13 9 8 小さい 大きい

説明上の注意 全てのデータが異なるとして、 説明します。 クイックソートのアルゴリズムでは、 ピボットの選び方にあいまいさがあります。 (自由度といったほうがいいかも。) ここでは、ソート範囲の最初の要素をピボットとして 説明します。 実際に、 プログラミングするときは、 もっといろんな状況を考えましょう。

クイックソートの動き前半(分割) 5 13 A 3 8 1 4 9 2 ピボットより 大きい値を探す ピボットより小さい 値を探す 5 13 探索が交差したら終了。 ピボットと前半最後の要素を交換し、あとは再帰呼び出し。 5 13 A 4 3 2 1 9 8

クイックソートの動き後半(再帰) 1 2 3 4 5 6 7 5 13 A 4 3 2 1 9 8 A[0]からA[4]までを ソートして 5 13 A 4 3 2 1 9 8 A[0]からA[4]までを ソートして A[5]からA[7]までを ソートして q_sort(0,3,A) 1 2 3 4 5 6 7 5 A 4 3 2 1 13 9 8 位置確定 3 1 2 5 6 7 A 1 3 2 4 13 9 8 以下省略

クイックソートの実現1(分割) /*概略です。細かい部分は省略*/ int partition(int left,int right,datatype *A) { int i,j; /*カウンタ*/ i=left+1; j=right; for(;;) while(A[i]<A[left]){i++;} while(A[j]>A[left]){j--;} if(i>=j){break;} swap(i,j,A); } return(i);

クイックソートの実現2(再帰) /*概略です。細かい部分は省略*/ void quick_sort(int left,int right,datatype *A) { int pos; /*分割位置 */ pos=partition(left,right,A); swap(left,pos,A); quick_sort(left,pos-1,A); quick_sort(pos+1,right,A); return; }

クイックソートの計算量 クイックソートは、 最悪時の計算量と平均の計算量が異なります。 これらは、 ピボットの選び方にもよりますが、 どんな選び方によっても最悪のデータ初期配置があります。 ここでは、最悪計算量と、平均計算量の両方を考えます。

クイックソートの最悪計算量 まず、関数partition(i,j,A)の1回の時間量は、 j-i+1に比例した時間量です。 再帰の同じ深さで、parttition()の時間量を 総計すると になります。 いつも0個、ピボット、残りのように 分割されるのが最悪の場合です。 つまり、ピボットとしていつも最小値が選択されたりするのが最悪です。 (他にも最悪の場合はあります。) このときでも、partition(i,j,A)の実行には、j-i+1回の演算が必要です。 これは、結局選択ソートの実行と同じようになり、 最悪時間量 のアルゴリズムです。

クイックソートの平均計算量 初期状態として、 通りの並びが すべて等確率だとしましょう。 クイックソートの時間量 を とします。 初期状態として、 通りの並びが すべて等確率だとしましょう。 クイックソートの時間量 を とします。 ピボットが 番目のときには、 以下の漸化式を満たす。 小さい方の分割を 再帰的にソートする分 大きい方の分割を 再帰的にソートする分 partition()分 ピボットの順位は、n通り全て均等におこるので、 それらを総和して、nで割ったものが平均時間量 2分探索木のときの解析と同様にして、

ちょっと寄り道 (一個一個が大きいデータを処理する工夫) 配列A A[0] A[1] A[i] A[n-1] A[j] 名前、 生年月日、 住所、 経歴、 趣味 A[j] A[i] 交換は大変

大きいデータを処理する工夫2 工夫:大きいデータは動かさずに、 たどり方だけがわかればいい。 data1 data2 配列A A[0] A[1] A[i] A[n-1] 1 i n-1 配列B B[0] B[1] B[i] B[n-1] 添字の配列Bだけを操作して、配列Aは動かさない。 swap(0,i); (data1が最小値のとき。) data2 data1 配列A A[0] A[1] A[i] A[n-1] i 1 n-1 配列B B[0] B[1] B[i] B[n-1]

大きいデータを処理する工夫3 イメージ data1 data2 配列A A[0] A[1] A[i] A[n-1] data1 data2 配列A A[0] A[1] A[i] A[n-1] ソート順は、下の情報からえられる。 (配列の添字の利用だけでなく、ポインタでも同様のことができる。)

問題とアルゴリズム (復習p.18参照) 具体的なアルゴリズムを作ることは、 問題の難しさ(問題固有の計算量)の上界を与えています。 最適なアルゴリズムの存在範囲 アルゴリズムがない問題は、難しさがわからない。 ソート問題 の難しさ バブルソート発見 ソート問題 の難しさ マージソート発見 ソート問題 の難しさ

問題と下界 一方、問題の難しさの範囲を下の方から狭めるには、 問題を解くアルゴリズムが無いことを示さないといけない。 実は、1つのアルゴリズムを作ることより、アルゴリズムが存在 しないことを示すことの方が難しい。 最適なアルゴリズムの存在範囲 ソート問題 の難しさ ? ソート問題 の難しさ ? アルゴリズムが 存在しない。 ソート問題の場合は、なんとか示せます。

アルゴリズムと決定木 (比較によるソートの下界証明の準備) 決定木の根:初期状態 決定木の節点:それまでの計算の状態 決定木の枝 :アルゴリズムの進行による状態の遷移 決定木の葉:終了状態 初期状態 状態遷移 いままでの、データ構造の木ではなくて、 概念的、抽象的なもの。 根からの道が、 アルゴリズムの実行順に対応し、 根から葉までの道の長さが時間量に対応 します。 終了状態

3要素バブルソートの決定木 A:a b c 1:true 0:false A:a c b A:a b c 1 1 1 1 結果の可能性 b a c b c a c a b c b a 結果の可能性 A:a b c 配列Aの内容 1:true 0:false A[1]<A[2]? (b<c?) a c b c a b c b a A:a c b a b c b a c b c a A:a b c 1 1 A[0]<A[1]? (b<c?) A[0]<A[1]? (a<c?) a c b c a b c b a a b c b a c b c a A:c a b A:b a c A:a c b A:a b c 1 1 A[1]<A[2]? (a<c?) A[1]<A[2]? (a<c?) b c a b a c c a b c b a A:b c a A:b a c A:c a b A:c b a

ソート問題の下界 どんな入力でもきちんとソートするには、 決定木にn!個以上の葉がなければならない。 それで、アルゴリズムの比較回数は、 決定木の高さで定まる。 最悪時間が良いアルゴリズムは高さが低く、 悪いアルゴリズムは高さが高い。 高さがhの決定木では、高々 個の葉 しかない。 よって、 よって、 ソートアルゴリズムでは少なくとも の時間量が必要である。

ソート問題の難しさ ソート問題 の難しさ 決定木による証明 アルゴリズム開発 (マージソート ヒープソート等) こんなことを考えるのが、 計算量理論の分野です。

比較によらないソート バケットソート データが上限のある整数のような場合に用いる。 データの種類が定数種類しかない場合には、 ハッシュ関数で整数に変えてから用いてもよい。 基数ソート 大きい桁の数に対して、 桁毎にバケットソートをしてソートする。

バケットソート とりあえず、簡単のために、 データは、 1.重複がなく、 2. 0からm-1の整数 という性質を満足するとしましょう。 (例えば、学籍番号の下2桁とか。) 方針 m個のバケット(配列)を用意して、 データを振り分けていく。 データそのものを配列の添字として使う。

バケットソートの動き1 2 4 3 6 1 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 -1 -1 -1 -1 3 6 1 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 -1 -1 -1 -1 -1 配列B B[0] B[1] B[2] B[3] B[m-1] 6 1 2 4 3 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 3 -1 -1 -1 -1 配列B B[0] B[1] B[2] B[3] B[m-1] 6 1 2 4 3 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 3 -1 -1 6 -1 配列B B[0] B[1] B[2] B[3] B[6] B[m-1]

バケットソートの実現 /*概略です。細かい部分は省略 入力データの配列の型がintであることに注意*/ void bucket_sort(int *A,int *B) { int i; /*カウンタ*/ for(i=0;i<n;i++) B[A[i]]=A[i]; } return;

バケットソートの動き2(添字を用いた場合) 2 4 3 6 1 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 -1 -1 -1 -1 -1 配列B B[0] B[1] B[2] B[3] B[m-1] 3 6 1 2 4 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 -1 -1 -1 -1 配列B B[0] B[1] B[2] B[3] B[m-1] 3 1 6 2 4 配列A A[0] A[1] A[i] A[n-1] -1 -1 -1 3 -1 -1 -1 1 配列B B[0] B[1] B[2] B[3] B[6] B[m-1]

バケットソートの実現2 /* 配列の添字を用いて、間接的にソート*/ void bucket_sort(int *A,int *B) { int i; /*カウンタ*/ for(i=0;i<n;i++) B[A[i]]=i; } return; i番目のデータの参照は、A[B[i]]で行う。

バケットソート(重複あり) 方針 重複した分を、リストとして繋ぐ。 (外部ハッシュ法参照。) 後の基数ソートへの利用では、 重複データのリストへの挿入順序が大事。 (外部ハッシュでは、余り重要ではなかったが。)

バケットソートの動き3(重複あり) A B C 2 6 3 1 4 1 2 1 2 1 2 5 2 4 1 3 3 3 2 4 2 4 1 4 2 5 2 7 5 2 5 6 6 4 7 5 7 5 先入れ先出しのキューに しておいたほうがいいです。

バケットソートの実現3(重複あり) /* リストの細かい実現は省略*/ void bucket_sort(int *A,struct cell **B,int *C) { int i,j; /*カウンタ*/ /*配列Bへ挿入*/ for(i=0;i<n;i++){enque(B[A[i]],=i);} /*ソート順に配列Cへ挿入*/ i=0; for(j=0;j<m;j++){ while(B[j]が空でない){ C[i]=A[deque(B[j])]; i++; } return;

バケットソートの計算量 配列1回のアクセスには、定数時間で実行できる。 (RAMモデルの能力を最大限に使っていることに 注意しましょう。) 繰り返し回数は、明らかにデータ数n回です。 また、 配列Bの準備や、走査のために、 の時間量必要です。 最悪時間量 のアルゴリズムです。

基数ソート 方針 大きい桁の数に対して、 桁毎にバケットソートをしてソートする。 下位桁から順にバケットソートする。

基数ソートの動き(3桁) A A A A 650 2 2 221 250 650 106 23 1 1 1 1 215 2 23 2 221 2 2 33 3 2 3 2 3 221 3 47 0桁で ソート 23 4 106 4 372 4 4 106 1桁で ソート 226 2桁で ソート 5 226 5 23 5 5 126 6 250 6 33 6 126 6 215 7 126 7 215 7 33 7 221 372 106 47 226 8 8 8 8 47 226 650 250 9 9 9 9 215 126 250 372 10 10 10 10 372 33 47 650 11 11 11 11

基数ソートの実現 /*細かい実現は省略*/ void radix_sort(int *A) { int i,j; /*カウンタ*/ for(k=0;k<max_k;k++) bbucket_sort(A,k); /*バケットソートを第k桁でソートして、 もとの配列に戻すように拡張する。*/ } return;

基数ソートの計算量 バケットソートを桁数分行えばよいので、 k桁数を基数ソートするには、 最悪時間量 のアルゴリズムです。 また、 桁必要です。 種類のデータを区別するには、 のときには、 結局 の時間量を持つアルゴリズムです。 だから、バケットソートや基数ソートは、 データ範囲mや、桁数kに注意しましょう。