本当はむずかしいクイックソート(またクイズ)

アルゴリズム入門の代表的例題のような「クイックソート」ですが、意外と「罠」が少なくありません。例えば以下のような教科書的実装を考えます。

void quicksort(int *a, int n) {
  int tmp, pivot, left, right;

  if (n <= 1) return;

  pivot = a[0];
  left = 1;
  right = n - 1;
  while (left <= right) {
    if (a[left] < pivot) {
      left++;
    } else {
      tmp = a[left];
      a[left] = a[right];
      a[right] = tmp;
      right--;
    }
  }

  tmp = a[right];
  a[right] = a[0];
  a[0] = tmp;

  quicksort(&a[0], right);
  quicksort(&a[left], n - left);
}

この実装を用いて、例えば乱数をソートすると

> cat qsort-rand-100000000.c
#include <stdlib.h>
#define N 1000000
int main() {
  int i, a[N];
  srand(0);
  for (i = 0; i < N; i++) {
    a[i] = rand() % 100000000;
  }
  quicksort(&a[0], N);
  return 0;
}
> gcc -O3 qsort.c qsort-rand-100000000.c -o qsort-rand-100000000
> time ./qsort-rand-100000000
0.109u 0.001s 0:00.11 90.9%     0+0k 0+0io 0pf+0w

のようになりますし、典型的最悪ケースでは

> cat qsort-worst.c
#define N 1000000
int main() {
  int i, a[N];
  for (i = 0; i < N; i++) {
    a[i] = -i;
  }
  quicksort(&a[0], N);
  return 0;
}
> gcc -O3 qsort.c qsort-worst.c -o qsort-worst
> time ./qsort-worst
331.605u 0.019s 5:31.67 99.9%   0+0k 0+0io 0pf+0w

のようになります。
さて、では

#define N 1000000
int main() {
  int i, a[N];
  for (i = 0; i < N; i++) {
    a[i] = i;
  }
  quicksort(&a[0], N);
  return 0;
}

の実行時間はどれぐらいになるでしょうか?

#include <stdlib.h>
#define N 1000000
int main() {
  int i, a[N];
  srand(0);
  for (i = 0; i < N; i++) {
    a[i] = rand() % 100;
  }
  quicksort(&a[0], N);
  return 0;
}

はどうでしょうか。予想と結果およびその理由を考察してください。…などと書いてしまうと、またどこか他の方の授業のレポート課題と被るかもしれませんが。