クイックソート.

Slides:



Advertisements
Similar presentations
1 高速フーリエ変換 (fast Fourier transform). 2 高速フーリエ変換とは? – 簡単に言うとフーリエ変換を効率よく計算 する方法 – アルゴリズムの設計技法は分割統治法に基 づいている 今回の目的は? – 多項式の積を求める問題を取り上げ、高速 フーリエ変換のアルゴリズムを用いた解法.
Advertisements

離散システム特論 整列(sorting)アルゴリズム 2.
アルゴリズムイントロダクション第2章 主にソートに関して
4.ソート 4-1.ソート問題について 4-2.簡単なソートアルゴリズム 4-3.高度なソートアルゴリズム
アルゴリズムとデータ構造 2012年6月27日
第12回 ソート(3): シェルソート、クイックソート
第10回 整列 ~ バブルソート,挿入ソート,選択ソート~
近似アルゴリズム 第10章 終了時刻最小化スケジューリング
第11回 整列 ~ シェルソート,クイックソート ~
アルゴリズムとデータ構造1 2005年7月8日
2009/10/30 整列アルゴリズム (2) 第5講: 平成21年10月30日 (金) 4限 E252教室 コンピュータアルゴリズム.
算法数理工学 第1回 定兼 邦彦.
4-3:高度なソートアルゴリズム① (分割統治法にもとづくソート)
情報知能学科「アルゴリズムとデータ構造」
2009/10/9 良いアルゴリズムとは 第2講: 平成21年10月9日 (金) 4限 E252教室 コンピュータアルゴリズム.
第10回 ソート(1):単純なソートアルゴリズム
C言語 配列 2016年 吉田研究室.
情報工学概論 (アルゴリズムとデータ構造)
情報工学概論 (アルゴリズムとデータ構造)
データ構造と アルゴリズム 第二回 知能情報学部 新田直也.
情報処理Ⅱ 2005年12月9日(金).
 Combinations(2)        古川 勇輔.
データ構造とアルゴリズム 分割統治 ~ マージソート~.
岩井 儀雄 コンピュータ基礎演習  ー探索、整列ー 岩井 儀雄
データ構造と アルゴリズム 第八回 知能情報学部 新田直也.
データ構造と アルゴリズム 第十一回 理工学部 情報システム工学科 新田直也.
第11回 整列 ~ シェルソート,クイックソート ~
プログラミング基礎a 第8回 プログラムの設計 アルゴリズムとデータ構造
算法数理工学 第1回 定兼 邦彦.
二分探索木によるサーチ.
ハッシュテーブル.
アルゴリズムとデータ構造 2012年6月28日
アルゴリズムとデータ構造 2011年6月27日
定兼邦彦 今井浩 東京大学理学系研究科 情報科学専攻
2009/11/6 整列アルゴリズムの復習 第6講: 平成21年11月6日 (金) 4限 E252教室 コンピュータアルゴリズム.
アルゴリズムとデータ構造1 2005年7月15日
ソートアルゴリズムの種類 選択ソート (selection sort) バブルソート (bubble sort)
最小自乗法.
情報工学概論 (アルゴリズムとデータ構造)
わんくま同盟茶藝部顧問 Microsoft MVP for VC episthmh
アルゴリズムとデータ構造1 2005年7月1日
大岩 元 慶応大学環境情報学部 再帰 データ構造とプログラミング(8) 大岩 元 慶応大学環境情報学部
25. Randomized Algorithms
Cプログラミング演習 第10回 二分探索木.
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング基礎a 第8回 プログラムの設計 アルゴリズムとデータ構造
プログラミング 4 整列アルゴリズム.
2009/10/23 整列アルゴリズム (1) 第4講: 平成21年10月23日 (金) 4限 E252教室 コンピュータアルゴリズム.
プログラミング 4 探索と計算量.
アルゴリズムとデータ構造1 2006年7月11日
アルゴリズムとデータ構造 2011年6月23日
プログラミング演習Ⅲ- Sorting Quick Sort and Merge Sort
ナップサック問題 クマさん人形をめぐる熱いドラマの結末.
15.cons と種々のデータ構造.
アルゴリズムとデータ構造 第3章 ヒープ 5月31日分
補講:アルゴリズムと漸近的評価.
データ構造とアルゴリズム (第5回) 静岡大学工学部 安藤和敏
Data Clustering: A Review
アルゴリズムとデータ構造 2013年7月1日
情報工学概論 (アルゴリズムとデータ構造)
PROGRAMMING IN HASKELL
ソートのプログラムの流れ 配列の中身を小さい順に並び替える a[1],a[2],…a[n]の値を順に出力する
アルゴリズムとデータ構造 2012年6月25日
PROGRAMMING IN HASKELL
ヒープソート.
Cプログラミング演習 ニュートン法による方程式の求解.
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
参考:大きい要素の処理.
情報処理Ⅱ 第8回:2003年12月9日(火).
プログラミング論 バイナリーサーチ 1.
Presentation transcript:

クイックソート

計算量とは アルゴリズムのよしあしを評価する基準 領域計算量(space complexity) 計算機のメモリーをどれだけ必要とするか 時間計算量(time complexity) アルゴリズムが答を出すまでにどの程度の計算時間を必要とするか プログラム中のそれぞれの文が実行される回数を数えて計算量の目安をつける

計算量の評価 47 12 33 ・・・ 5 67 ループを回る回数がk回のとき、つまり x=table[k-1]のときの計算時間(概算)は 1: 2: 3: 4: 5: found = false; for ( i=0 ; i<n ; i++ ){ if ( x == table[i] ){ found = true; break; } ループを回る回数がk回のとき、つまり x=table[k-1]のときの計算時間(概算)は T(k)=t1+k(t2+t3)+t4+t5 ループを回る回数の平均は約n/2回 最悪の場合(k=n)の計算時間は T= t1+t4+t5+n(t2+t3) nが大きくなれば定数t1+t4+t5は無視でき、 Tはおよそnに比例する T=cn (c=t2+t3) 線形探索アルゴリズム O記法ではO(n)とあらわす

クイックソートの手順 pivot の選択 pivot による要素の分割 分割された2つの部分に,再びクイックソートを適用 要素を1つずつ調べて、基準値より小さい要素と,より大きい要素を分ける 分割された2つの部分に,再びクイックソートを適用 「基準値より大きい要素」と「基準値より小さい要素」のそれぞれを独立にソート 最後に、2つの部分と pivot をつなげる.全体がソートできたことになる

pivot の選択 8 10 6 3 5 8 リスト pivot ソートする範囲の中から pivot を選ぶ 8 10 6 3 5 8 リスト pivot ソートする範囲の中から pivot を選ぶ (ここでは,リストの先頭要素を pivot として  選んでいる)

pivot による要素の分割 10 8 10 6 3 5 8 6 3 5 リスト pivot 分割されたリスト 8 10 6 3 5 8 6 3 5 リスト pivot 分割されたリスト 要素を1つずつ調べて、基準値より 小さい要素と,より大きい要素を分ける

リストが空になるまで,pivot の選択と, pivot による要素の分割を続ける 10 10 8 10 6 3 5 8 リスト pivot 6 3 5 6 リスト pivot 3 5 3 リストが空になるまで,pivot の選択と, pivot による要素の分割を続ける

部分問題の例 休暇旅行の旅程(家から旅先のホテルまで) 家→空港 空港→旅先の空港 旅先の空港→ホテル

クイックソートの部分問題 「基準値より大きい要素」のソート 「基準値より小さい要素」のソート

分割統治法(divide and conquer) サイズNの問題を解くのに、サイズが約N/2の部分問題2つに分けて、それぞれを再帰的に解き、その後でその2つの解を合わせて目的の解を得る

クイックソート 分割統治法に分類されるソート法 平均して O(nlog n) の計算量でソートを行なうアルゴリズム 多くの問題に対して、最高速のソートアルゴリズムであると言われているそうですが、 最悪の場合には,O(n2) の計算量を要することが知られている

クイックソートの手順 ソートする範囲の中から適当な値を1つ選ぶ この値を基準値と呼ぶ 次に配列中の要素を1つずつ調べて、基準値より小さいデータを配列の左側、大きなデータを右側に集める この操作を分割と呼ぶ 分割が終わったら、基準値より小さい部分と大きい部分に,それぞれに再びクイックソートを適用 これは再帰呼び出しで実現 分割された2つの部分を独立にソートすれば全体もソートされたことになる 基準値より小さい部分に含まれる値は基準値より大きい部分に含まれるどの値よりも小さいから

基本的な分割法 ソートすべき範囲を left~right とする まず基準値Tを選び、この値を left に移しておく ? m m+1 i ある時点 i まで分割が終わった状態 left+1~m の範囲に T 未満の値、 m+1~i までの範囲に T 以上の値が残る

i を1増やした後の i 番目の要素は,次の2通り 1) i 番目の値が T以上: 何もしない 2) i 番目の値が T より小さい m を 1 増やし、小さい要素のための新たな場所を指すようにして、次にそこにある要素と i 番目の要素を交換する これを i が right にいたるまで繰り返す 交換 T < T >= T <T ? m i T < T >= T ? m i

クイックソートの平均計算量 ソートする範囲の要素数をnとする 1回の分割での比較の回数: n-1 長さnのクイックソートに要する計算量: Qn = n-1+ Qa+Qb ここで a, b は分割によって生じた左右の部分の長さ a+b = n-1 が成立 a と b の組は分割の回数によって決まる 平均をとると,Qn = n-1+ 2/n(Q0+…+Qn-1 ) これを計算すると Qn = 2(n+1)( 1+1/2+…+1/(n+1) - 2 ) +2 Qn ≒ 2nlog n

n=4のときの計算量 Q2 =1 Q1 =0 pivot Q3 =2+ 2Q2 /3 Q4 = 3+(2Q3 +2Q2 )/4

最悪の場合 Qn=(n-1)+(n-2)+…+1 = (n(n-1))/2 基準値として最大または最小の値をとった場合に起こる 分割を行うたびにこのような状態になったとすると、比較の回数は Qn=(n-1)+(n-2)+…+1 = (n(n-1))/2

基準値の選び方 「最悪の場合」から分かるように、基準値の選び方が,性能の鍵 最悪の計算量になる場合とは逆に,分割させる範囲をなるべく同じ長さの2つの部分に分けるような値を選ぶ しかし、分割のたびに中央値を求めるのでは、そのための手間がかかりすぎる 実用的な方法は,ソートする範囲の中からいくつかの値をサンプルとして選び、それらの中央値を基準とする方法です。 サンプルとしてとる値の個数は多いほど分割を均等化するためにはよい 実際には3個とれば十分だとされている

簡単なソート法の併用 ソートすべき範囲が短い場合には、クイックソートよりも単純な方法(挿入法など)と差が無い 高級なアルゴリズムは、単純な方法より計算量のオーダーが低い 対象とするデータが多い場合には明らかに速い データが少ない場合にはオーダーの問題とはならないことがありえる 10~20の範囲内であれば,どれを選んでも大差はないといわれている

例題 クイックソート 次の2つのクイックソートプログラムを作成し,性能を比較する(平均の比較回数は6 / 7程度に減り、実際の実行時間は5%ほど減ると言われている) 基準値を適当な値にとるプログラム ソートする範囲から3つのサンプルを選び,その中央値を基準値として使うプログラム ソート対象のデータは rand関数等を用いて生成すること

#include<stdio.h> FILE *infile,*outfile; int data[10000]; main() { int i, n, in[20], out[20]; printf("Input InFilename :"); /*入力データのファイル名を入力*/ scanf("%s", in); if ((infile=fopen(in,"r"))==NULL) { printf("can't open file %s\n", in); exit(); } printf("Input OutFilename :"); scanf("%s", out);

if ((outfile=fopen(out,"w"))==NULL) { printf("can't open file %s\n",out); exit(); } n=0; while(fscanf(infile, "%d", &(data[n])) != EOF) { n++; Quick(0,n-1); /*0からn-1の範囲でクイックソート*/ for (i=0; i<n; i++) { fprintf(outfile,"%d\n",data[i]); fclose(outfile); fclose(infile);

Quick(int left,int right) { /*クイックソートを行う*/ int i,t,m; t=Choice(left, right); /*基準値を選ぶ*/ swap(&data[left], &data[t]); m=left; for (i=left; i<right; i++){ if (data[i+1]<data[left]) { /*基準値よりも小さい場合*/ swap(&data[i+1], &data[m+1]); m++; } swap(&data[left], &data[m]); /*基準値をmに挿入*/ if (m-left>1) { Quick(left, m-1); if (right-m>1) { Quick(m+1, right);

int Choice(int left,int right) { /*基準値を選ぶ*/ if ((right-left)<2) { return(left); /*要素数が3つないとき*/ } else { /*要素数が3つ以上のとき*/ if((data[left]<=data[left+1] & data[left+1]<=data[left+2]) | (data[left+2]<=data[left+1] & data[left+1]<=data[left+2])) { return(left+1); /*中央値を返す*/ else if((data[left+1]<=data[left] & data[left]<=data[left+2]) | (data[left+2]<=data[left] & data[left]<=data[left+1])) { return(left); else { return(left+2);

swap(int *a, int *b){ /*値の交換*/ int temp; temp=*a; *a=*b; *b=temp; }