どのようなアルゴリズムのコンテキストで 'アレイアクセス'を構成する?

以下は、JavaのLSD基数ソートの実装で、各文字列に正確に W 文字が含まれる文字列の配列をソートするためのテキストブックです。

実行時に配列アクセスの数を数えたいと思います。私はLSDソートが n * c 配列へのアクセスを必要としていると読んでいる。 n は文字列の数であり、 c 各文字列にしかし、以下のアルゴリズムは、複数の配列に複数回アクセスします。これらのそれぞれでカウンタを増やすと、重要な要素 nc になります。

では、アルゴリズムのコンテキストでは、正確には「配列アクセス」を構成するものは何ですか?ここで数える必要があると考えられる配列アクセスのインスタンスは1つだけですか、実際はこの例は、必要以上に多くの配列アクセスを使用する非効率的な実装ですか?

 public int lsdSort(String[] array, int W) {
  int access = 0;
 //Sort a[] on leading W characters.
  int N = array.length;
  String[] aux = new String[N];
  for (int d = W-1; d >= 0; d--)
  {//Sort by key-indexed counting on dth char.
    int[] count = new int[R+1];//Compute frequency counts.
    for (int i = 0; i < N; i++) {
        count[array[i].charAt(d) + 1]++;
    }
    for (int r = 0; r < R; r++) {
       //Transform counts to indices.
        count[r+1] += count[r];
    }
    for (int i = 0; i < N; i++) {
       //Distribute.
        aux[count[array[i].charAt(d)]++] = array[i];

    }  
    for (int i = 0; i < N; i++)//Copy back.
        array[i] = aux[i];
  }

  return access;
  }
4
読みやすさを向上させた小さな編集のためにYuvalに感謝します!
追加された 著者 Lawyerson,

4 答え

LSDソートは、n回の文字列の数と各文字列の文字数のn回のc回の配列アクセスを必要とすると読みました。

あなたはそれが O(nc)であると読んでいないでしょうか?それはまったく同じことではありません。 big-O表記です。重要な点は、配列アクセスの正確な数を判断することではなく、 n としてどのように成長するか(あるいは、成長する方法の制限 c が増加します。この場合、それは線形に増加します - もしあなたが n を1000倍に増やすと、合計コストは1000倍も増加すると予想します。 2 )アルゴリズムではなく、1,000,000倍になる可能性があります。 (厳密に言えば、O(nc)アルゴリズムもそれが限界であるために O(n 2 )です。

7
追加された
@パルサ:それは本当にそれが意味するものであり、本当に可能です。それはビッグオービットよりも興味深いのように見えます。
追加された 著者 Jon Skeet,
あなたの答えをありがとう。これは理にかなっているようです。私は最近、アルゴリズムの研究を始めました。私はbig-O表記に精通しています。しかし、私の教科書は、この実装例を提供しているにもかかわらず、Radixソートは各文字列 n に対して正確に c
追加された 著者 Lawyerson,
@JonSkeet私は、この本は単純に(混乱しているにもかかわらず)私に、Radixソートが線形時間でソートできることを教えてくれると思っていますし、実際にはこれを厳密にcnで行うことができますが、実装に依存します。あなたの時間をありがとう!
追加された 著者 Lawyerson,
nc が実行できない理由は実際にはわかりませんが。 c ループで配列を反復処理し、ヘルパー配列に値を入れるだけです。さて、文字を書き戻す必要があるので、 2nc と主張できますが、別の配列;-)
追加された 著者 Voo,

最初のforループ内のすべての配列アクセスは、基本的に配列アクセスの合計数としてカウントされます。 nは、これらの組み合わせられた配列アクセスを何回行うのかです。これは、アクセスの実際の数ではなく、関数の成長のおおよその考え方を示します。

1
追加された
public int lsdSort(String[] array, int W) {
  int access = 0;
 //Sort a[] on leading W characters.
  int N = array.length;
  String[] aux = new String[N];
  for (int d = W-1; d >= 0; d--)
  {//Sort by key-indexed counting on dth char.
    int[] count = new int[R+1];//Compute frequency counts.
    for (int i = 0; i < N; i++) {
        count[array[i].charAt(d) + 1]++;
        access++;
        access++;
    }
    for (int r = 0; r < R; r++) {
       //Transform counts to indices.
        count[r+1] += count[r];
        access++;
    }
    for (int i = 0; i < N; i++) {
       //Distribute.
        aux[count[array[i].charAt(d)]++] = array[i];
        access++; 
        access++;
        access++;   
    }  
    for (int i = 0; i < N; i++)//Copy back.
        array[i] = aux[i];
        access++;
        access++;
  }

  return access;

  }

配列 'アクセス'は、読み取りまたは書き込みのいずれかです...

1
追加された
+1の例です。私は同じループ内で何度も増分を考慮していませんでした。あなたの答えをありがとう!
追加された 著者 Lawyerson,

Big-O 漸近表記では、アクセス数は定数に比例します。コードを分析すると、すべての定数が破棄されます。

基数ソートの場合、Big Oは O(cn)です。しかし、実際に何回配列がアクセスされたのかを数えたい場合は、定数 k にその数を掛けなければなりません。 k は、

たとえば、この関数は O(n)ですが、配列にアクセスする回数は 2n です(値を読み取るためのものと更新するためのもの)。番号 2 は破棄されます。

for (i=0; i
1
追加された