データ構造における基数ソートアルゴリズム

⚡ スマートサマリー

基数ソートは、比較を用いない線形ソートアルゴリズムであり、計数ソートなどの安定サブルーチンを用いて、整数を桁の位置ごとにグループ化します。多くの入力に対して、数値、文字列、固定幅キーのソートを、比較ベースのソートよりも高速に実行できます。

  • 🎯 コアアイデア: 基数ソートは、各要素の各桁を最下位から最上位へと順に処理し、値をバケットに分配して、各パスで配列を再構築します。
  • ⚙️ 安定サブルーチン: 計数ソートのような安定内部ソートは、同じ数字の前の順序を保持します。これは、最終結果が完全にソートされているために不可欠です。
  • 🧭 実例: 配列 {162, 623, 835, 415, 248} に対して、一の位、十の位、百の位の列を 3 回反復処理すると、ソートされた出力 {162, 248, 415, 623, 835} が得られます。
  • 💻 言語: C++ (NAIST) と Python 実装では、安定した内部パスとして計数ソートを使用する。
  • 📊 複雑: 時間計算量はO(d*(n + b))、空間計算量はO(n + b)です。ここで、nは配列のサイズ、bは基数、dは桁数です。
  • 🏭 用途: DC3アルゴリズムを用いた接尾辞配列の構築、広範囲の値における位置特定、ランダムアクセスマシンにおけるキーベースのソートなどが一般的な用途です。

データ構造における基数ソートアルゴリズム

基数ソート アルゴリズムとは何ですか?

基数ソートは比較を用いないソートアルゴリズムです。ping ソート対象となる要素の各桁の数字を指定します。次に、安定ソート法を用いて、基数に基づいて要素を並べ替えます。これは線形ソートアルゴリズムです。

ソートプロセスには次のプロパティが関係します。

  • 最大要素を見つけ、その要素の桁数を取得します。これにより、ソート処理が実行する反復回数がわかります。
  • グロウping 各反復処理において、同じ有効桁数にある要素の個々の桁。
  • グループping この処理は最下位桁から始まり、最上位桁で終了します。
  • その重要な位置にある数字に基づいて要素を並べ替える。
  • 同じキー値を持つ要素の相対的な順序を維持する。この特性により、基数ソートは安定ソートとなる。

最終反復では、完全にソートされたリストが返されます。

基数ソートアルゴリズムの仕組み

基数ソートアルゴリズムの仕組み

ソートする整数のリスト

上の図の整数のリストを基数ソートを使って昇順に並べ替えてみましょう。

基数ソートを実行する手順は以下のとおりです。

ステップ1) リストの中で最大の要素を特定してください。ここでは835です。

ステップ2) 桁数を数えてください。835は3桁なので、繰り返し回数は3回です。

ステップ3) 基数を決定します。これは十進数なので、基数は10です。

ステップ4) 最初の反復を開始します。

a) 最初の反復

基数ソートアルゴリズムの動作:最後の桁によるソート

最後の桁で並べ替える

最初の反復では、各要素の単位位の値を考慮します。

ステップ1) 整数を10で割った余りを求めると、要素の一の位が得られます。例えば、623を10で割ると3になり、248を10で割ると8になります。

ステップ2) 最下位桁に基づいて整数を整理するには、計数ソートまたは他の安定ソートを使用します。図から、248は8番目のバケットに、623は3番目のバケットに、といった具合に分類されます。

最初の反復の後、リストは次のようになります。

最初の反復後のリスト

最初の反復後のリスト

リストはまだソートされておらず、さらに反復処理が必要です。

b) XNUMX 回目の反復

XNUMXの位の数字に基づくソート

XNUMXの位の数字に基づくソート

今回の反復処理では、ソート処理において十の位の数字を考慮します。

ステップ1) 整数を10で割ります。例えば、248を10で割ると24になります。

ステップ2) ステップ1の出力を10で割った余りを求めます。24 mod 10は4になります。

ステップ3) 前回の手順2に従ってください。

2回目の反復処理後、リストは次のようになります。

XNUMX 回目の反復後のリスト

XNUMX 回目の反復後のリスト

リストはまだ昇順になっていないため、完全にソートされているとは言えません。

c) XNUMX 回目の反復

百の位の数字に基づいてソートする

百の位の数字に基づいてソートする

最後の反復処理では、最上位桁を取得します。この場合、リスト内の各整数の百の位が最上位桁となります。

ステップ1) 整数を100で割ります。例えば、415を100で割ると4になります。

ステップ2) ステップ1の結果を10で割った余りを求めます。4 mod 10は4になります。

ステップ3) 前回の手順3に従ってください。

XNUMX回目の反復後のリスト

XNUMX回目の反復後のリスト

リストは昇順にソートされました。最終反復処理が完了し、ソート処理は終了しました。

基数ソートアルゴリズムの擬似コード

基数ソートアルゴリズムの擬似コードを以下に示します。

radixSortAlgo(arr as an array)
    Find the largest element in arr
    maximum = the element in arr that is the largest
    Find the number of digits in maximum
    k = the number of digits in maximum
    Create buckets of size 0-9 k times
    for j -> 0 to k
        Acquire the jth place of each element in arr. Here j = 0 represents the least significant digit.
        Use a stable sorting algorithm like counting sort to sort the elements in arr according to the digits in the jth place
    arr = sorted elements

C++ 基数ソートを実装するプログラム

#include <iostream>
using namespace std;
// Function to get the largest element in an array
int getMaximum(int arr[], int n) {
    int maximum = arr[0];
    for (int i = 1; i < n; i++) {
        if (maximum < arr[i]) maximum = arr[i];
    }
    return maximum;
}
// We are using counting sort to sort the elements digit by digit
void countingSortAlgo(int arr[], int size, int position) {
    const int limit = 10;
    int result[size];
    int count[limit] = {0};
    // Calculating the count of each integer
    for (int j = 0; j < size; j++) count[(arr[j] / position) % 10]++;
    // Calculating the cumulative count
    for (int j = 1; j < limit; j++) {
        count[j] += count[j - 1];
    }
    // Sort the integers
    for (int j = size - 1; j >= 0; j--) {
        result[count[(arr[j] / position) % 10] - 1] = arr[j];
        count[(arr[j] / position) % 10]--;
    }
    for (int i = 0; i < size; i++) arr[i] = result[i];
}
// The radixSort algorithm
void radixSortAlgo(int arr[], int size) {
    // Get the largest element in the array
    int maximum = getMaximum(arr, size);
    for (int position = 1; maximum / position > 0; position *= 10)
        countingSortAlgo(arr, size, position);
}
// Printing final result
void printResult(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}
int main() {
    int arr[] = {162, 623, 835, 415, 248};
    int size = sizeof(arr) / sizeof(arr[0]);
    radixSortAlgo(arr, size);
    printResult(arr, size);
}

出力:

162 248 415 623 835

Python 基数ソートアルゴリズムのプログラム

# Radix Sort in Python
def countingSortAlgo(arr, position):
    n = len(arr)
    result = [0] * n
    count = [0] * 10
    # Calculating the count of elements in the array arr
    for j in range(0, n):
        element = arr[j] // position
        count[element % 10] += 1
    # Calculating the cumulative count
    for j in range(1, 10):
        count[j] += count[j - 1]
    # Sorting the elements
    i = n - 1
    while i >= 0:
        element = arr[i] // position
        result[count[element % 10] - 1] = arr[i]
        count[element % 10] -= 1
        i -= 1
    for j in range(0, n):
        arr[j] = result[j]

def radixSortAlgo(arr):
    # Acquiring the largest element in the array
    maximum = max(arr)
    # Using counting sort to sort digit by digit
    position = 1
    while maximum // position > 0:
        countingSortAlgo(arr, position)
        position *= 10

data = [162, 623, 835, 415, 248]
radixSortAlgo(data)
print(data)

出力:

[162, 248, 415, 623, 835]

基数ソートの複雑性分析

考慮すべき複雑さには、空間計算量と時間計算量の2種類がある。

  • 空間計算量: O(n + b) であり、n は配列のサイズ、b は考慮される基数です。
  • 時間計算量: O(d * (n + b))。ここで、d は配列内の最大要素の桁数です。

基数ソートの空間計算量

空間計算量に関して注目すべき2つの特徴:

  • 配列内の要素の数、 n.
  • 要素を表すために使用されるベース、 b.

場合によっては、この基数が配列のサイズよりも大きくなることがあります。したがって、全体の計算量はO(n + b)となります。

リスト内の要素の以下の特性により、基数ソートのスペース効率が悪くなる可能性があります。

  • 桁数の多い要素。
  • 要素の基数は 64 ビットの数値のように大きくなります。

基数ソートの時間計算量

計数ソートをサブルーチンとして使用すると、各イテレーションには O(n + b) 時間。 d 回の反復が存在する場合、合計実行時間は次のようになります。 O(d * (n + b))ここで、「O」は複雑度関数を表します。

基数ソートの直線性

基数ソートが線形になるのは、次の場合です。

  • d は定数で、d は最大要素の桁数です。
  • b は、より著しく大きくはない。 n.

基数ソートと他のソート方法の比較 Algorithms

基数ソートの計算量は数値のサイズに依存します。最良の場合と平均的な場合では、いずれもO(d * (n + b))です。パフォーマンスは内部のソート方法によって異なります。計数ソートが一般的ですが、安定ソートであればどれでも使用できます。

基数ソートアルゴリズムの応用

基数ソートの重要な応用例は以下のとおりです。

  • 基数ソートは、広範囲の値を扱う場合の位置情報探索アルゴリズムとして使用できます。
  • これは、DC3アルゴリズムにおいて接尾辞配列を構築するために使用されます。
  • これは、レコードが固定幅の識別子によってキー付けされるシーケンシャルランダムアクセスマシンで使用されます。

よくあるご質問

基数ソートは、AIデータの前処理とGPUに適した整数キーのソートを高速化します。ベクトルデータベースや埋め込みパイプラインでも、最近傍バケットに基数スタイルのパーティショニングが使用されます。

はい。GitHub CopilotとGPTは、基数ソートを生成できます。 Python, C++, JavaまたはRust(LSDおよびMSDの派生版、文字列または固定幅バイナリキーをソートするバージョンを含む)。

基数ソートは、桁数の少ない大きな整数配列では、比較処理を回避できるため、クイックソートよりも高速です。ただし、一般的なデータや浮動小数点値では、クイックソートよりも遅くなる場合が多いです。

基数ソートは、内部ソートが安定ソート(例えば計数ソート)である場合に安定ソートとなります。ただし、入力配列に加えてO(n + b)サイズのバケット配列が必要となるため、インプレースソートではありません。

LSD基数ソートは、最下位桁から最上位桁の順に処理を行い、固定長の整数に適しています。MSD基数ソートは、最上位桁から処理を開始し、可変長の文字列に適しています。

標準基数ソートは非負整数を前提としています。負の値は、配列の最小値分だけオフセットするか、正負を別々のパスでソートすることで処理されます。

基数ソートは、接尾辞配列の構築、IPルーティングテーブル、データベースインデックス、GPUソートカーネル、郵便番号によるメールルーティング、コンパイラにおける辞書式文字列ソートなどに利用されています。

カウントソートは安定しており、O(n + b) の時間で実行されます。ping 基数ソートの総コストは線形である。その安定性により、複数パス戦略に必要な、同じ桁の順序が維持される。