質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

6回答

4074閲覧

クイックソートのソースコードについて

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/12/15 08:57

編集2018/12/15 09:15

クイックソートのプログラムなのですが、

#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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

変わるのは left > right の時ですが、それが起こり得るのは最初の一回だけです。再起呼び出しではその条件は除外してあるので、最初の一回さえ気をつければ変わりません。

投稿2018/12/15 09:35

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2018/12/15 09:37

ちなみに left > right で呼び出すと do の方はまずいことになります。
guest

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

cateye

総合スコア6851

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/12/15 09:16

どちらの処理でもソート内容自体に変わりはありますか?
cateye

2018/12/15 09:19

余談:千〜2千ぐらいならシェルソートを使いましょう(シェル大好きw)・・・どっちにしても安定なソートではないd^^
cateye

2018/12/15 09:26 編集

実行環境を作ってプリントデバッグd^^ plとprを見てみれば?・・・追いかけるほど頭回らないσ(゚∀゚ )
guest

0

while は処理の前に条件判断となり、条件次第では処理が全く行われません。
do while だと、処理が終わってから条件判断となり、必ず1回は処理を行うことになるってことで、そこの違いになりますんで、そこらへんで処理が変わるかですね

投稿2018/12/15 09:09

y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/12/15 09:16

どちらの処理でもソート内容自体に変わりはありますか?
guest

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
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bull

2018/12/16 07:12

quickB の '}' の位置が quickA とは違います。 それを直すと呼び出し回数と、swap の回数は同じになります。
katoy

2018/12/16 08:43

あ、間違えた! コードを paste して来て { } を追加編集したのですが、そこで間違えてました。 修正したコードと実行結果を追記します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問