ティムソート

Harshit Jindal 2023年10月12日
  1. ティムソートアルゴリズム
  2. ティムソートの例
  3. ティムソートアルゴリズムの実装
  4. ティムソートアルゴリズムの複雑さ
ティムソート

ティムソートはハイブリッド安定ソートアルゴリズムです。挿入ソートとマージソートから派生したハイブリッドアルゴリズムです。最初に挿入ソートを用いて部分配列を作成し、この小さなソートされた部分配列をナチュラルランと呼びます。これらの小配列は自然実行と呼ばれ、その後、マージソートを用いて小配列をマージし、最終的にソートされた配列を生成します。このアルゴリズムは、実世界のデータに対する最適な性能を考慮して設計されています。このアルゴリズムは、挿入ソートが小さなサイズの部分配列に対して非常に優れた性能を発揮するという事実を利用しています。これは、Java の Array.sort() や Python の sorted()sort() で使われている標準的なソートアルゴリズムです。

ティムソートアルゴリズム

ここでは、n 要素を含むソートされていない配列 A[] があると仮定します。ここでは、実行サイズを 32 とします。これは任意の 2 の累乗でもよい。

TimSort())

  • 配列を n/32 の実行に分割します。
  • 挿入ソートを用いて、個々のランを 1つずつソートします。
  • マージソートの merge 関数を使用して、ソートされたランを 1つずつマージします。

Merge()

  • ソートされた出力を格納するための補助配列 output を初期化します。
  • 3つのポインタ ijk を初期化します。

    i は最初の部分配列 beg の先頭を指します。
    j は 2 番目の部分配列 mid+1 の先頭を指します。
    beg で初期化された k は、ソートされた配列 output の現在のインデックスを保持します。

  • arr[beg, .... ,mid] または arr[mid+1, .... ,end] 部分配列の終わりに到達するまでは、インデックス i &j の要素の中から小さい方の値をピックして output に挿入します。
  • 残りの要素をピックして、配列を使い切ったら output に挿入します。
  • outputarr[beg, ... , end] にコピーします。

ティムソートの例

配列 (3、5、2、8、1、7、6、4) があるとします。これをティムソートアルゴリズムを用いてソートします。説明を簡単にするために、実行のサイズを 4 と考えてみましょう。

配列を 2つのサブ配列 (3、5、2、8)(1、7、6、4) に分割します。

最初のサブアレイ: (3, 5, 2, 8)

ソートされた部分配列 アンソートされていない部分配列 配列
(3) (5, 2, 8) (3,5,2,8)
  • 最初のイテレーション

キー : A[1] = 5

ソートされた部分配列 アンソートされていない部分配列 配列
( 3 , 5) (2, 8) (3, 5, 2, 8)
  • 2 回目のイテレーション

キー : A[2] = 4

ソートされた部分配列 アンソートされていない部分配列 配列
( 2, 3, 5) (8) (2, 3, 5, 8)
  • 3 回目のイテレーション

キー: A[3] = 2

ソートされた部分配列 アンソートされていない部分配列 配列
( 2, 3, 5, 8) () (2, 3, 5, 8)

2 番目の部分配列:(1,7,6,4)

ソートされた部分配列 アンソートされていない部分配列 配列
(1) (7, 6, 4) (1, 7, 6, 4)
  • 最初のイテレーション

キー : A[1] = 7

ソートされた部分配列 アンソートされていない部分配列 配列
( 1 , 7) (6, 4) (1, 7, 6, 4)
  • 2 回目のイテレーション

キー : A[2] = 6

ソートされた部分配列 アンソートされていない部分配列 配列
( 1, 6, 7) (4) (1, 6, 7, 4)
  • 3 回目のイテレーション

キー : A[3] = 4

ソートされた部分配列 アンソートされていない部分配列 配列
( 1, 4, 6, 7) () (1, 4, 6, 7)

ソートされた 2つの部分配列をマージして、最終的なサブ配列を次のようにします:(1, 2, 3, 4, 5, 6, 7, 8)

ティムソートアルゴリズムの実装

#include <bits/stdc++.h>
using namespace std;
const int RUN = 32;

void insertionSort(int arr[], int left, int right) {
  for (int i = left + 1; i <= right; i++) {
    int temp = arr[i];
    int j = i - 1;
    while (j >= left && arr[j] > temp) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = temp;
  }
}

void merge(int arr[], int beg, int mid, int end) {
  int output[end - beg + 1];
  int i = beg, j = mid + 1, k = 0;
  // add smaller of both elements to the output
  while (i <= mid && j <= end) {
    if (arr[i] <= arr[j]) {
      output[k] = arr[i];
      k += 1;
      i += 1;
    } else {
      output[k] = arr[j];
      k += 1;
      j += 1;
    }
  }
  // remaining elements from first array
  while (i <= mid) {
    output[k] = arr[i];
    k += 1;
    i += 1;
  }
  // remaining elements from the second array
  while (j <= end) {
    output[k] = arr[j];
    k += 1;
    j += 1;
  }
  // copy output to the original array
  for (i = beg; i <= end; i += 1) {
    arr[i] = output[i - beg];
  }
}
void timSort(int arr[], int n) {
  for (int i = 0; i < n; i += RUN)
    insertionSort(arr, i, min((i + 31), (n - 1)));

  for (int size = RUN; size < n; size = 2 * size) {
    for (int left = 0; left < n; left += 2 * size) {
      int mid = left + size - 1;
      int right = min((left + 2 * size - 1), (n - 1));

      merge(arr, left, mid, right);
    }
  }
}

int main() {
  int n = 6;
  int arr[6] = {5, 3, 4, 2, 1, 6};
  cout << "Input array: ";
  for (int i = 0; i < n; i++) {
    cout << arr[i] << " ";
  }
  cout << "\n";
  timSort(arr, n);  // Sort elements in ascending order
  cout << "Output array: ";
  for (int i = 0; i < n; i++) {
    cout << arr[i] << " ";
  }
  cout << "\n";
}

ティムソートアルゴリズムの複雑さ

時間計算量

  • 平均のケース

このアルゴリズムは、n 要素の配列をソートするために O(nlogn) 比較を必要とします。したがって、時間の複雑さは[ビッグシータ]:O(nlogn) のオーダーです。

  • 最悪のケース

最悪の場合は nlogn 比較が必要です。最悪の時間複雑度は [Big O]:O(nlogn) です。これは平均ケースの時間複雑度と同じです。

  • 最良のケース

ベストケースは、配列が既にソートされており、スワップが不要な場合に発生します。最良の時間的複雑さは [Big Omega]:O(n) です。

空間計算量

このアルゴリズムの空間複雑度は O(n) ですが、マージソートでは O(n) の余分な補助空間が必要となるため、このアルゴリズムの空間複雑度は O(n) です。

著者: Harshit Jindal
Harshit Jindal avatar Harshit Jindal avatar

Harshit Jindal has done his Bachelors in Computer Science Engineering(2021) from DTU. He has always been a problem solver and now turned that into his profession. Currently working at M365 Cloud Security team(Torus) on Cloud Security Services and Datacenter Buildout Automation.

LinkedIn

関連記事 - Sort Algorithm