バケットの在庫リストから、浮動小数点数の順に表示

Bucket ソートは、入力データを有限数の「バケット」に分割し、各 Bucket の内容を個別にソートする分布ベースのソートアルゴリズムです。 既知の間隔で均一に分散されている浮動小数点数に適用される場合、通常は ] — バケットソートは、線形平均値の時刻の複雑さを達成し、高性能なソートタスクの強力な候補となることができます。

コアのアイデアは簡単です: 要素のペアを補う代わりに(Quicksortやmergesortのような比較ソートで)、バケットソートは最初に、値に基づいて Bucket を渡る要素を配布します。各 Bucket は、値の狭い範囲をグループ化します。その後、単純なソートアルゴリズム - 多くの場合、インサートソートまたはバケットソートへの再帰呼び出しでさえ、作業を終了します。最後に、バケットはソート配列を生成するために連結されます。

この記事では、Pythonの浮動小数点数のバケットのソートを実装し、その機械力、複雑さ、強度、下降、および現実世界のアプリケーションをカバーしています。

どのようにバケットソート作品

Bucket ソートは、入力が既知の範囲内で均一に分散されていると仮定します。通常 。アルゴリズムは 3 つのフェーズで進行します。

  1. []初期化]:の配列を作成します。]n空の Bucket、n]は要素の数です。
  2. []Distribution]]:各要素[]]の場合、バケットインデックス[を計算します(]])。その要素をバケットに置きます。
  3. []] ソートとコンカテーション[: それぞれ各バケットを個別にソート(安定したまたは効率的な内部ソートを使用)、その後、最終ソート配列を生成するためにバケットを連結します。

重要な洞察は、データが均一に分散しているため、各バケットは大体 []n / n = 1]] の要素を平均で受け取ります。これにより、個々のバケットをソートするコストが極めて低く抑えられます。

エッジケースの処理

浮動小数が1.0と正確に等しい場合、計算されたインデックスはで、境界線から抜け出されます。 一般的な修正は、そのような値のにインデックスをクランプすることです。 実際には、あなたのデータが厳密にである場合、このエッジケースは発生しませんが、それはそれに対して監視する賢明です。

Pythonでバケットのソートを実行

以下は、範囲内の浮動小数点数のバケットの種目()のクリーンで生産準備が整っています。

def bucket_sort(arr):
 """Sort an array of floats uniformly distributed in [0, 1)."""
 n = len(arr)
 if n <= 1:
 return arr

 # Create empty buckets
 buckets = [[] for _ in range(n)]

 # Distribute elements into buckets
 for num in arr:
 index = int(num * n)
 # Guard against floating-point index = n (e.g., when num == 1.0)
 if index == n:
 index = n - 1
 buckets[index].append(num)

 # Sort each bucket and concatenate
 sorted_arr = []
 for bucket in buckets:
 sorted_arr.extend(sorted(bucket)) # Python's Timsort is efficient

 return sorted_arr

関数は、各バケットをソートするために、Pythonの組み込みを使用します。 小さい(典型的に0〜2要素)のバケットでは、これは非常に高速です。 生産用途では、小さなバケットのオーバーヘッドを下げるインサートソートでを置き換える可能性があります。

バケット 任意の範囲のソート

浮動小数点のデータが以外の範囲に及ぶ場合、分布の前に値を正規化できます。次のバリエーションは、任意の[]]範囲を[にマップします。

def bucket_sort_scaled(arr, min_val=None, max_val=None):
 if not arr:
 return arr
 if min_val is None:
 min_val = min(arr)
 if max_val is None:
 max_val = max(arr)

 # Guard against identical values
 if max_val == min_val:
 return arr

 n = len(arr)
 buckets = [[] for _ in range(n)]

 for num in arr:
 # Normalize to [0, 1)
 normalized = (num - min_val) / (max_val - min_val)
 index = int(normalized * n)
 if index == n:
 index = n - 1
 buckets[index].append(num)

 sorted_arr = []
 for bucket in buckets:
 sorted_arr.extend(sorted(bucket))
 return sorted_arr

こちらのバージョンは一般ですが、範囲を知るか、または計算する必要があります。その範囲内でデータ分布がほぼ均一な場合にうまく機能します。

複雑化解析

バケットのソートの計算コストを理解することは、使用時に決定する上で不可欠です。

時間の複雑さ

  • []ベストケース(単体分散データ):[]O(n + k)], k]は、バケットの数は(通常n])です。 配布はO(n)K[FLT:]K]]が、(通常、[FLT:]は、各バケットの合計が[FLT]、[FLT]、[FLT]、[FLT]、[FLT:[FLT:[FLT:])])]は、および[FLTは、各バケットの合計が、各回、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[FLTは、[F]は、[FLTは
  • []Average case:[[]]]O(n + n2/k)]) バケットのインサートソートを使用する場合。 []]k = n[]] で、これはO(n)[になります。
  • []Worst case]:[[]O(n2)]すべての要素が同じバケットに落ちるとき。 これは、データが均一に分散されていない場合、または範囲が要素の数に非常に小さい場合に発生します。

宇宙の複雑さ

バケットのソートには、[]O(n + k)[[の余分スペースが必要です。 ]k = n]で、これは[]]]O(n)[[です。 使用されるスペースは、マージのそれと、クイックソートのようなインソートのそれよりも高いに匹敵する。

利点および使用場合

バケットソートは、その仮定が保持する特定のシナリオで輝きます。

  • []均一に分散浮動小数点データ[ — 例、センサー読み取り、モンテカルロシミュレーション出力、または正規化の確率。
  • []大データセット — []O(n)平均的なケースのパフォーマンスは、比較ソートが少ない効率的なフロートの数百万をソートするために魅力的になります。
  • []外部ソート[]]] — ディスクにデータが存在する場合、バケットは独立して処理し、ファイルを分離するために書き込むことができます。
  • []並列とGPU計算 - 各バケットは、大規模な並列性を可能にする、独立してソートすることができます。

注目すべき強みは、バケットソートがのstableのことです。(バックレットソートが安定している場合)、等しい要素の相対的な順序が保存されることを意味します。

制限事項と留意事項

エレガンスにもかかわらず、バケットソートには、汎用的なソートに適さないレンダリングが可能ないくつかの制限があります。

  • []: データのスキュード(例: 一緒にクラスターされた多くの値)の場合、ほとんどの要素はいくつかのバケットに落ち、 ]O(n2)にソートコストを増加させます。
  • ]範囲の事前知識を必要とします:最小値と最大値を知ることなく、効果的にバケットを作成することはできません。 これを緩和する上でスケールされたバージョンが、範囲を計算すると、余分なパスを追加します。
  • [Memory overhead]: []n]]] を作成すると、特に非常に大きな配列で重要なメモリを消費できます。 配列の一覧または配列はオーバーヘッドを減らすことができますが、Pythonのリストは簡単です。
  • は、パーバックのソート[のオーバーヘッド: Pythonの[]で多くの小さなバケツを並べ替える]。 非常に小さなバケットの場合、明示的なインサートソートがより速くなる可能性があります。

バケットのソートを使用しないとき

範囲が要素の数に非常に大きく、またはメモリが非常に制約されるとき、データが均一に分散されていないとき、バケットソートを避けてください。この場合、比較ベースのソート()キルクスワートまたは[]]heapsort)はより安全な選択です。

他のソートアルゴリズムとの比較

Bucket ソートは、ソートアルゴリズム間でユニークなニッチを占めています。 一般的な代替方法は次のとおりです。

Algorithm Average Time Space Stable Best For
Bucket Sort (with k = n) O(n) O(n) Yes (if per-bucket sort is stable) Uniform floats in known range
Quicksort O(n log n) O(log n) No (typical) General-purpose, in-place
Mergesort O(n log n) O(n) Yes Stable sorting, linked lists
Counting Sort O(n + k) O(k) Yes Integer data with limited range
Radix Sort O(n × w) O(n + 2^w) Yes (LSD) Integers or strings of fixed length

浮動小数点数の場合、バケットソートは、多くの場合、大幅な小数(ビットマニピュレーションが必要な)を出力し、]よりも高速にすることができます。] データの均一な場合の比較ソート。

実用的なPythonのヒントと最適化

バケットの数を選択する

要素の数(]]]k = n)に等しいバケットの数を設定することは、標準の境界線です。 フィールバケットは平均バケットサイズと劣化性能を増加させます。 速度を改善することなく、より多くのバケット廃棄物メモリ。

小さなバケツのインサートソートを使用して

細かい管理が必要な場合は、カスタムインサートソートで[を置き換えてください。

def insertion_sort(arr):
 for i in range(1, len(arr)):
 key = arr[i]
 j = i - 1
 while j >= 0 and arr[j] > key:
 arr[j + 1] = arr[j]
 j -= 1
 arr[j + 1] = key

def bucket_sort_insertion(arr):
 n = len(arr)
 if n <= 1:
 return arr
 buckets = [[] for _ in range(n)]
 for num in arr:
 index = int(num * n)
 if index == n:
 index = n - 1
 buckets[index].append(num)
 sorted_arr = []
 for bucket in buckets:
 insertion_sort(bucket)
 sorted_arr.extend(bucket)
 return sorted_arr

Pythonのが0-または1要素リストにオーバーキルされている関数コールのオーバーヘッドと汎用動作を持っているため、オーバーヘッドを減らすことができます。

非均一分布の取り扱い

データの分布が均一でないが、まだバケットのソートを使用するのを知っている場合は、バケットの境界線を適応させることができます。例えば、データが通常の分布に続くと、負荷のバランスを取るための非等幅のバケットを作成できます。しかし、これはデータの事前の分析を必要とし、実際には行われません。

外部リソース

さらなる読書については、次の権限の参照を検討してください。

コンテンツ

バケットソートは、特にデータが均一に分散され、範囲が知られています。その線形平均ケース時間の複雑さは、データ科学者やエンジニアのツールキットに貴重なツールになります。しかし、その感度は、分布と追加のメモリ要件を入力し、それが盲目的に使用すべきではありません。バケットソートを適用する方法と、適切なエッジケースでPythonで慎重に実装することにより、一般的な比較結果を得ることができます。

数千万のセンサー測定や、ストキャスシミュレーションによる正規化出力をソートしている場合でも、バケットソートは、データがルールによって再生される限り、高速で安定した、並列可能なソリューションを提供します。