クイックソートのプログラムなのですが、
#define swap(type, x, y) do { type t = x; x = y; y = t; } while (0) /*--- クイックソート ---*/ void quick(int a[], int left, int right) { int pl = left; /* 左カーソル */ int pr = right; /* 右カーソル */ int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ do { while (a[pl] < x) pl++; while (a[pr] > x) pr--; if (pl <= pr) { swap(int, a[pl], a[pr]); pl++; pr--; } } while (pl <= pr); if (left < pr) quick(a, left, pr); if (pl < right) quick(a, pl, right); }
を
#define swap(type, x, y) do { type t = x; x = y; y = t; } while (0) /*--- クイックソート ---*/ void quick(int a[], int left, int right) { int pl = left; /* 左カーソル */ int pr = right; /* 右カーソル */ int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ while (pl <= pr) { while (a[pl] < x) pl++; while (a[pr] > x) pr--; if (pl <= pr) { swap(int, a[pl], a[pr]); pl++; pr--; }; if (left < pr) quick(a, left, pr); if (pl < right) quick(a, pl, right); }
に変更しても処理結果は変わるでしょうか?
do-whileをwhileに変更しています。
do-whileとwhileの処理の違いはわかるのですが、それで実際にソートに差が出るのかわかりません。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
変わるのは left > right の時ですが、それが起こり得るのは最初の一回だけです。再起呼び出しではその条件は除外してあるので、最初の一回さえ気をつければ変わりません。
投稿2018/12/15 09:35
総合スコア28660
0
do-whileとwhileの処理の違いはわかるのですが、それで実際にソートに差が出るのかわかりません。
- do-while: left > rightのときでも実行する。インデックスが範囲外に出ることがある。
- while: left > rightのとき実行しない。ソートしない。
インデックスが範囲外に出る可能性があるのは、以下の文です。
C
1 while (a[pl] < x) pl++; 2 while (a[pr] > x) pr--;
left > rightのとき、do-whileは不具合を起こすことがありますが、whileは不具合を起こさないので防禦的なコードだといえます。だからといって優劣が決まるわけではない。ソートの仕様が提示されていないからです。たとえば次のような仕様が考えられます。
- 事前条件 配列の要素数は2以上、left <= right であること、0 <= left,right < 要素数
- 事後条件 配列の要素が昇順にならんでいること。
- 不変条件 配列の要素が欠けたり追加されないこと。
クイックソートの呼び出し側は事前条件を守るべきで、left > rightで呼び出すなら、結果は保証されない。つまり、仕様次第では、do-whileに欠陥があるといえません。
ちょうど今、「契約による設計」を勉強しているので、思ったことを書いてみました。
投稿2018/12/18 10:38
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ちょっと見&インデントが分かりづらいので修正・・・
#define swap(type, x, y) \ do { \ type t = x; \ x = y; \ y = t; \ } while (0) /*--- クイックソート ---*/ void quick(int a[], int left, int right) { int pl = left; /* 左カーソル */ int pr = right; /* 右カーソル */ int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ while (pl <= pr) { while (a[pl] < x) pl++; while (a[pr] > x) pr--; if (pl <= pr) { swap(int, a[pl], a[pr]); pl++; pr--; }; if (left < pr) quick(a, left, pr); if (pl < right) quick(a, pl, right); }
変わるとしたらpl == prの時ですかね?
・・・do{…}while;の方はそれでも動いちゃいますが?
投稿2018/12/15 09:10
総合スコア6851
0
原理上は起こり得ないが(そもそも初期でleft>rightの場合は値渡しがおかしいので)、万が一ミスった際の対策でdo-whileになっていると解釈しました。
皆様のおかげで理解しました、ありがとうございました。
投稿2018/12/26 10:22
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
どんな差が出るかを、
ソート結果、 sawp が実行される回数、quick メソッドが呼びし回数
で比べてみました。
x.c
c
1#include <stdio.h> 2 3#define swap(type, x, y) do { type t = x; x = y; y = t; swap_count++;} while (0) 4 5int swap_count; 6int call_count; 7 8/*--- クイックソート ---*/ 9void quickA(int a[], int left, int right) 10{ 11 int pl = left; /* 左カーソル */ 12 int pr = right; /* 右カーソル */ 13 int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ 14 15 call_count++; 16 do { 17 while (a[pl] < x) pl++; 18 while (a[pr] > x) pr--; 19 if (pl <= pr) { 20 swap(int, a[pl], a[pr]); 21 pl++; 22 pr--; 23 } 24 } while (pl <= pr); 25 26 if (left < pr) quickA(a, left, pr); 27 if (pl < right) quickA(a, pl, right); 28} 29 30void quickB(int a[], int left, int right) 31{ 32 int pl = left; /* 左カーソル */ 33 int pr = right; /* 右カーソル */ 34 int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ 35 36 call_count++; 37 while (pl <= pr) { 38 while (a[pl] < x) pl++; 39 while (a[pr] > x) pr--; 40 if (pl <= pr) { 41 swap(int, a[pl], a[pr]); 42 pl++; 43 pr--; 44 }; 45 if (left < pr) quickB(a, left, pr); 46 if (pl < right) quickB(a, pl, right); 47 } 48} 49 50void copy_to_a(int src[], int a[], int size) { 51 for (int i = 0; i < size; i++) { 52 a[i] = src[i]; 53 } 54} 55 56void show_a(int a[], int size) { 57 for (int i = 0; i < size; i++) { 58 printf("%d ", a[i]); 59 } 60 printf(" swap_count(%d), call_count(%d)\n", swap_count, call_count); 61} 62 63int main(void) { 64 65 int test [][3] = { 66 {1, 2, 3}, 67 {1, 3, 2}, 68 {2, 1, 3}, 69 {2, 3, 1}, 70 {3, 1, 2}, 71 {3, 2, 1} 72 }; 73 74 for (int i = 0; i < 6; i++) { 75 int a[3]; 76 77 printf("----------\n"); 78 swap_count = call_count = 0; 79 show_a(test[i], 3); 80 81 copy_to_a(test[i], a, 3); 82 swap_count = call_count = 0; 83 quickA(a, 0, 2); 84 show_a(a, 3); 85 86 copy_to_a(test[i], a, 3); 87 swap_count = call_count = 0; 88 quickB(a, 0, 2); 89 show_a(a, 3); 90 } 91 return 0; 92}
[3, 2, 1] をソートしたときに、ソート結果は同じですが、メソッドの呼び出し回数、swap の実行回数に差異があります。
3要素の配列でためしていますが、ご自身で、もっと要素数が多いときに常にソート結果は同じなのか、計算量の差はどのくらい異なるのかなどを調べてみることをお勧めします。
[100, 99, ... 1] の配列をソートして比較すると、きっと驚くだろうと予想してます。(私は驚きました)
もし実際に比較してみていただけたなら結果をコメントにでも書いてもらえれば幸いです。
追記
上のコードの quickB は質問文のものと違ってました。
修正したコードと実行結果を示します。
c
1#include <stdio.h> 2 3#define swap(type, x, y) do { type t = x; x = y; y = t; swap_count++;} while (0) 4 5int swap_count; 6int call_count; 7 8/*--- クイックソート ---*/ 9void quickA(int a[], int left, int right) 10{ 11 int pl = left; /* 左カーソル */ 12 int pr = right; /* 右カーソル */ 13 int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ 14 15 call_count++; 16 do { 17 while (a[pl] < x) pl++; 18 while (a[pr] > x) pr--; 19 if (pl <= pr) { 20 swap(int, a[pl], a[pr]); 21 pl++; 22 pr--; 23 } 24 } while (pl <= pr); 25 26 if (left < pr) quickA(a, left, pr); 27 if (pl < right) quickA(a, pl, right); 28} 29 30void quickB(int a[], int left, int right) 31{ 32 int pl = left; /* 左カーソル */ 33 int pr = right; /* 右カーソル */ 34 int x = a[(pl + pr) / 2]; /* 枢軸は中央の要素 */ 35 36 call_count++; 37 while (pl <= pr) { 38 while (a[pl] < x) pl++; 39 while (a[pr] > x) pr--; 40 if (pl <= pr) { 41 swap(int, a[pl], a[pr]); 42 pl++; 43 pr--; 44 }; 45 } 46 if (left < pr) quickB(a, left, pr); 47 if (pl < right) quickB(a, pl, right); 48} 49 50void copy_to_a(int src[], int a[], int size) { 51 for (int i = 0; i < size; i++) { 52 a[i] = src[i]; 53 } 54} 55 56void show_a(int a[], int size) { 57 for (int i = 0; i < size; i++) { 58 printf("%d ", a[i]); 59 } 60 printf(" swap_count(%d), call_count(%d)\n", swap_count, call_count); 61} 62 63int main(void) { 64 65 int test [][3] = { 66 {1, 2, 3}, 67 {1, 3, 2}, 68 {2, 1, 3}, 69 {2, 3, 1}, 70 {3, 1, 2}, 71 {3, 2, 1} 72 }; 73 74 for (int i = 0; i < 6; i++) { 75 int a[3]; 76 77 printf("----------\n"); 78 swap_count = call_count = 0; 79 show_a(test[i], 3); 80 81 copy_to_a(test[i], a, 3); 82 swap_count = call_count = 0; 83 quickA(a, 0, 2); 84 show_a(a, 3); 85 86 copy_to_a(test[i], a, 3); 87 swap_count = call_count = 0; 88 quickB(a, 0, 2); 89 show_a(a, 3); 90 } 91 92 int aa[100]; 93 for (int i = 0; i < 100; i++) { 94 aa[i] = 100 - i; 95 } 96 swap_count = call_count = 0; 97 quickB(aa, 0, 99); 98 show_a(aa, 100); 99 100 for (int i = 0; i < 100; i++) { 101 aa[i] = 100 - i; 102 } 103 swap_count = call_count = 0; 104 quickA(aa, 0, 99); 105 show_a(aa, 100); 106 107 108 return 0; 109}
投稿2018/12/16 05:29
編集2018/12/16 08:47総合スコア22324
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/12/15 09:37