web-dev-qa-db-ja.com

基数ソートvsカウントソートvsバケットソート。違いは何ですか?

私は基数、カウント、バケットソートの定義を読んでいますが、それらはすべて以下のコードのようです:

public static void sort(int[] a, int maxVal){
    int [] bucket=new int[maxVal+1];

    for (int i=0; i<bucket.length; i++){
        bucket[i]=0;
    }

    for (int i=0; i<a.length; i++){
        bucket[a[i]]++;
    }

    int outPos=0;
    for (int i=0; i<bucket.length; i++){
        for (int j=0; j<bucket[i]; j++){
            a[outPos++]=i;
        }
    }
}

私は正しいことができないことを知っているので、何が欠けていますか? JavaまたはCでの説明に役立つと思われる場合は、コードを表示します。

52
good_evening

Cの方が説明しやすいので、Cでコードを書き直すことから始めましょう。それでは、コメントを付けてコードを思い出してみましょう。

int
counting_sort(int a[], int a_len, int maxVal)
{
  int i, j, outPos = 0;
  int bucket_len = maxVal+1;
  int bucket[bucket_len]; /* simple bucket structure */

  memset(bucket, 0, sizeof(int) * bucket_len);

  /* one loop bucket processing */
  for (i = 0; i < a_len; i++)
    {
      bucket[a[i]]++; /* simple work with buckets */
    }

  for (i=0; i < bucket_len; i++)
    {
      for (j = 0; j < bucket[i]; j++)
        {
          a[outPos++] = i;
        }
    }

  return 0;
}

次に、この男に現実的なデータを提供しましょう。

[126、348、343、432、316、171、556、223、670、201]

出力には

[126、171、201、223、316、343、348、432、556、670]

それはすべて大丈夫だと思われますか?未だに。 maxValを見てみましょう。 670(!)です。ここでは、10個の要素の配列をソートするために、670個の要素の配列(主にゼロ)を使用しました。ひどく。このカウントソートの問題を処理するために、2つの一般化方法があります。

1)最初の方法-数字ごとにソートする。これは基数ソートと呼ばれます。可能な限りカウントソートコードに近づけるようにして、いくつかのコードを示しましょう。もう一度コメントを見てください:

int
radix_sort(int a[], int a_len, int ndigits)
{
  int i;
  int b[a_len];
  int expn = 1;

  /* additional loop for digits */
  for (i = 0; i != ndigits; ++i)
    {
      int j;
      int bucket[10] = {0}; /* still simple buckets */

      /* bucket processing becomes tricky */
      for (j = 0; j != a_len; ++j)
        bucket[ a[j] / expn % 10 ]++;

      for (j = 1; j != 10; ++j)
        bucket[j] += bucket[j - 1];

      for (j = a_len - 1; j >= 0; --j)
        b[--bucket[a[j] / expn % 10]] = a[j];

      for (j = 0; j != a_len; ++j)
        a[j] = b[j];

      expn *= 10;
    }
}

Nに近い乗数をメモリと交換しています。利益?多分。ただし、場合によっては、Nに近い乗数が非常に重要です。プログラム、1日作業、1週間作業は、どちらもそれぞれ1 * O(N)と7 * O(N)で動作する場合でも、ユーザーの表示とは大きく異なります。そして、2番目の一般化に向かっています。

2)2番目の方法-バケットをより洗練させる。これはバケットソートと呼ばれます。

もう一度いくつかのコードから始めましょう。哲学的な議論よりも前に、より多くのコードを好む。まだコメントを見てください、彼らは不可欠です。

int
bucket_sort(int a[], int a_len, int maxVal)
{
  int i, aidx;

  typedef struct tag_list {
    int elem;
    struct tag_list *next;
  } list_t, *list_p;

  list_p bucket[10] = {0}; /* sophisticated buckets */

  /* one loop simple processing with one more inner loop 
    to get sorted buckets (insert-sort on lists, Cormen-style) */
  for (i = 0; i != a_len; ++i)
    {
      int bnum = (10 * a[i]) / maxVal;
      list_p bptr = bucket[bnum];
      list_p belem = malloc(sizeof(list_t));
      belem->elem = a[i];
      if (bptr == 0)
        {
          bucket[bnum] = belem;
          belem->next = 0;
          continue;
        }
      else if (a[i] <= bptr->elem)
        {
          belem->next = bptr;
          bucket[bnum] = belem;
          continue;
        }
      else
        {
          while (bptr != 0)
            {
              if ((bptr->elem <= a[i]) && ((bptr->next == 0) || (bptr->next->elem > a[i])))
                {
                  belem->next = bptr->next;
                  bptr->next = belem;
                  break;
                }
               bptr = bptr->next;
            }
         }
    }

  /* one loop (looks as two) to get all back */
  aidx = 0;

  for (i = 0; i != 10; ++i)
    {
      list_p bptr = bucket[i];
      while (bptr)
        {
          list_p optr = bptr;
          a[aidx] = bptr->elem;
          aidx += 1;
          bptr = bptr->next;
          free(optr);
        }
    }

  return 0;
}

ここに何がありますか?いくつかの洗練されたバケット構造と動的に割り当てられたメモリの要件を交換していますが、静的メモリを獲得し、平均でNに近い乗数です。

次に、コードで何が表示されたかを思い出してみましょう。

  1. ソートのカウント-シンプルなバケット、シンプルな処理、メモリオーバーヘッド
  2. 基数ソート-単純なバケット、高度な処理、速度のオーバーヘッド(さらに追加の静的メモリが必要)
  3. バケットの並べ替え-洗練されたバケット、単純な処理、動的メモリが必要で、平均的には良い

したがって、基数およびバケットソートは、カウントソートの2つの有用な一般化です。それらは、並べ替えのカウントとお互いに多くの共通点を持っていますが、いずれの場合も、何かを失い、何かを獲得しています。ソフトウェアエンジニアリングは、これらの機会のバランスについてです。

61

基数ソートvsカウントソートvsバケットソート。違いは何ですか?

バケットソートでは、ソートするキーまたは要素をバケットに配置します。それらがバケット内でどのように配置されるかは任意であり、複合キーおよび任意の配布の一部になります。個々のバケットをさらにソートする必要がある場合があります。

メモリでのソートは、ディスクでのソートよりも高速です。ただし、メモリに収まるよりも多くのデータがある場合は、別のオプションが必要です。できるのは、バケットがメモリに収まるほど小さいバケットソートです。つまり、各バケットに多数のエントリがあります。これらは、個別にクイックソートできます。

基数ソートは、バケットソートの特定のタイプです。上位nビットまたはn桁で始まり、すべてのエントリがソートされるまで、基数ソートなどを使用してこれらのバケットをソートできます。

ソートのカウントは、値全体を使用することを除いて、基数ソートの使用に似ています。各オブジェクトを記録する代わりに、各オブジェクトのバケットがあり、発生回数をカウントするだけです。これは、可能なキーの数が限られており、多くの重複がある場合にうまく機能します。

14
Peter Lawrey

Geekviewpointによると:

基数: http://www.geekviewpoint.com/Java/sorting/radixsort

基数ソートは、カウントソートやバケットソートと同様に、整数ベースのアルゴリズムです(つまり、入力配列の値は整数であると想定されます)。したがって、基数ソートは、理論上、最速のソートアルゴリズムの1つです。基数ソートの特別な違いは、各暗号(つまり、数字)のバケットを作成することです。そのため、バケットソートと同様に、基数ソートの各バケットは、異なるキーを許可する拡張可能なリストである必要があります。

バケット: http://www.geekviewpoint.com/Java/sorting/bucketsort

バケットの並べ替えは、カウントの並べ替えがその上限を合理的に示していることを考えると、実際には非常に優れています。そして、カウントの並べ替えは非常に高速です。バケットソートの特別な違いは、ハッシュ関数を使用して入力配列のキーを分割し、複数のキーが同じバケットにハッシュできるようにすることです。したがって、各バケットは実質的に拡張可能なリストである必要があります。基数ソートに似ています。

カウント: http://www.geekviewpoint.com/Java/sorting/countingsort

カウントソートの特定の違いは、値ごとにバケットを作成し、各バケットにカウンターを保持することです。その後、入力コレクションで値が検出されるたびに、適切なカウンターが増分されます。並べ替えのカウントでは各値のバケットが作成されるため、入力配列の最大値が事前にわかっているという制限が課せられます。

彼らは彼らのサイトでそれをより詳細に説明します。

編集:

  • 基数ソートを使用していて、数値が10進数の場合、0〜9の各桁に1つずつ、10個のバケットが必要です。

  • カウントソートを使用している場合は、入力の一意の値ごとにバケットが必要です(実際には、0〜maxの値ごとにバケットが必要です)。

  • Bucketsortを使用している場合、使用するバケットの数はわかりません。使用しているハッシュ関数に関係なく、バケットの数が決まります。

7
kasavbere

あなたのコードは、データなしのキーによるソートの単純なバリアントです。

基数ソートは、このメソッドに基づいたソートです。ソートのカウントに関する問題は、メモリ要件:_int [] bucket=new int[maxVal+1];_です。基数ソートはこの問題を解決します。考え方は、最初に下位の桁に、次に上位の桁に数回のソートを使用することです。たとえば、32ビット整数をソートするには、次を使用します。

_sort(a, 65535) using lower half as key
sort(a, 65535) using higher half as key
_

ソートのカウントは安定しているため、機能します。同じキーでデータの順序を保持します。これは、スプレッドシートでの並べ替えのようなものです。_sort by B; sort by A_は、Aで並べ替えられ、Asが等しい場合はBで並べ替えられた要素を提供します。

バケットソートは、カウントソートの一般化です。これを使用して、予測可能な確率分布から実数を並べ替えることができます(たとえば、均一な_(0,1)_)。 (floor(x*N_BUCKETS)をキーとして使用して)カウントソートを使用し、各バケットを個別にソートするだけです。

6
zch

まず、基数ソ​​ートとバケットソートの違いを見てみましょう。これは、考え方が同じように見えるため、一般に混乱を招くものだからです。次に、これら2つのプライマリバージョンのようなCounting Sortを調べます。また、Counting Sortの問題により他の2つが使用されます

RadixソートとBucketソートの両方の初期パスは同じです。要素は、最大のnoの桁数に応じて、「バケット」つまり0-10、11-20などに配置されます。基数。ただし、次のパスでは、バケットの並べ替えでこれらの「バケット」を並べ替え、1つの配列に追加します。ただし、基数の並べ替え方法は、バケットを追加の並べ替えなしで追加し、数値の2桁目(10の位)に基づいてバケットを「再バケット化」します。したがって、バケットの並べ替えは「密」配列に対してより効率的ですが、基数並べ替えはまばらな配列をうまく処理できます。バケットの並べ替えをこれと考えてください

1からkの数字のキーを持つn個のレコードのリストがあるとします(kが必ずしもnと等しくないように問題を少し一般化します)。

これは、リンクリストの配列を作成することで解決できます。各入力レコードを配列の適切な位置のリストに移動し、すべてのリストを順番に連結します。

 bucket sort(L)
    {
    list Y[k+1]
    for (i = 0; i <= k; i++) Y[i] = empty
    while L nonempty
    {
        let X = first record in L
        move X to Y[key(X)]
    }
    for (i = 0; i <= k; i++)
    concatenate Y[i] onto end of L
    }

Kが大きい場合の対処方法数値の10進表現x = a + 10 b + 100 c + 1000 d + ...について考えてください。ここで、a、b、cなどはすべて範囲0..9です。これらの数字は、バケットのソートを行うのに十分なほど簡単に小さいです。

   radix sort(L):
    {
    bucket sort by a
    bucket sort by b
    bucket sort by c
    ...
    }

またはもっと簡単に

radix sort(L):
{
while (some key is nonzero)
{
    bucket sort(keys mod 10)
    keys = keys / 10
}
}

なぜ最も重要でない数字を最初にソートするのですか?さらに言えば、最後のバケットがすべてを適切に配置するバケットであるため、なぜ複数のバケットソートを行うのでしょうか。回答:手で物事をソートしようとする場合、何か別のことをする傾向があります。最初にバケットソートを行い、次に共通の最初の数字を共有する値を再帰的にソートします。これは機能しますが、問題を多くのサブ問題に分割するため、効率が低下します。対照的に、基数ソートはリストを分割しません。同じリストにバケットのソートを数回適用するだけです。基数ソートでは、バケットソートの最後のパスが、全体の順序に最も影響するものです。そのため、最も重要な数字を使用するものにする必要があります。以前のバケットソートパスは、最後のパスで2つのアイテムが同じキー(mod 10)を持っている場合にのみ使用されます。

これで、すべてのCountingソートの方法から外れたので、すべて0に初期化されたk個の要素を持つ補助配列Cを保持します。

入力配列Aを1回通過し、表示されるAの各要素iについて、C [i]を1ずつ増分します。Aのn個の要素を反復処理してCを更新すると、Cのインデックスjの値は対応しますjがAに出現した回数。このステップは、Aを反復するのにO(n)時間を要します。Cを取得したら、Cを反復して挿入してAのソート済みバージョンを作成できます各要素は、合計C [j]回の新しいリスト(またはA自体)になります。Cを反復処理するには、O(k)時間を要します。最終結果は、ソートされたAで、合計でそうするのにO(n + k)時間かかりました。

カウントソートの欠点は、要素の範囲が大きすぎると実用的ではない可能性があることです。たとえば、ソートする必要があるn個の要素の範囲が1〜n 3の場合、補助配列Cを作成するだけでO(n ^ 3)時間かかり、ソートのカウントは挿入ソートよりも漸近的に悪化します。これには、これまでに学習した他のソートアルゴリズムで使用されるスペースよりもかなり大きいO(n ^ 3)スペースも必要です。基数ソートは、要素を桁ごとにソートすることにより、この問題の解決に役立ちます

注:回答と詳細情報のソース:

http://htmltolatex.sourceforge.net/samples/sample4.html

最初の答え: バケットソートと基数ソートの違いは何ですか?

3
Slartibartfast

Radixソートは、サブルーチンとしてカウントソートの形式を使用します(OK、使用できますが、ほとんどの場合、ソートをカウントします)。

Kasavbereが答えたように、Countingsortはバケットソートの特別な形式です。

Bucketsortはキーをバケットに分割し、バケットを個別にソートします。

2
kutschkem

Count sortを使用して配列をソートするには:

_#define MAX_INPUT 1000

void sort(int arr[100], int n)
{
    static int hash[MAX_INPUT], i, j;

    memset(hash, 0, sizeof hash);

    for (i = 0; i < n; ++i) ++hash[arr[i]];

    j = 0;
    for (i = 0; i < MAX_INPUT; ++i)
        while (hash[i]--)
           arr[j++] = i;
}
_

これはO(MAX_INPUT)であるため、線形時間でソートされます。バケットの並べ替えでは、非常に異なります。 実装

1
user586399