42

このマージソートの実装を例として取り上げましょう

void mergesort(Item a[], int l, int r) {
if (r <= l) return;
int m = (r+l)/2;
mergesort(a, l, m);   ------------(1)
mergesort(a, m+1, r); ------------(2)
merge(a, l, m, r);

a)このマージソートの時間計算量はですO(n lg(n))。(1)と(2)を並列化すると、実用的な利益が得られますか?理論的には、それらを並列化した後も、で終わるように見えますO(n lg(n))。しかし、実際に私たちは何か利益を得ることができますか?

b)このマージソートのスペースの複雑さはここにありO(n)ます。ただし、リンクリストを使用してインプレースマージソートを実行することを選択した場合(配列で合理的に実行できるかどうかはわかりません)O(lg(n))、再帰スタックのフレームサイズを考慮する必要があるため、スペースが複雑になりますか?O(lg(n))64を超えることはできないので、定数として扱うことはできますか?私はこれをいくつかの場所で誤解したかもしれません。64の重要性は正確には何ですか?

c)ソートアルゴリズムの比較-Cprogramming.comによると、マージソートにはリンクリストを使用した一定のスペースが必要です。どのように?O(lg(n))彼らは一定に扱いましたか?

d)より明確にするために追加されました。スペースの複雑さの計算では、入力配列またはリストがすでにメモリにあると想定するのは公平ですか?複雑さの計算を行うときは、入力によってすでに使用されているスペースに加えて、必要になる「余分な」スペースを常に計算します。そうしないと、スペースの複雑さが常にO(n)悪化します。

4

7 に答える 7

104

MergeSortの時間計算量は基本的な知識であるO(nlgn)です。マージソートスペースの複雑さは、配列を含めて常にO(n)になります。スペースツリーを描くと、スペースの複雑さはO(nlgn)のように見えます。ただし、コードは深さ優先コードであるため、常にツリーの1つのブランチに沿って拡張するだけです。したがって、必要な合計スペース使用量は常にO(3n)= O(n)によって制限されます。

たとえば、スペースツリーを引き出すと、O(nlgn)のように見えます。

                             16                                 | 16
                            /  \                              
                           /    \
                          /      \
                         /        \
                        8          8                            | 16
                       / \        / \
                      /   \      /   \
                     4     4    4     4                         | 16
                    / \   / \  / \   / \
                   2   2 2   2.....................             | 16
                  / \  /\ ........................
                 1  1  1 1 1 1 1 1 1 1 1 1 1 1 1 1              | 16

ここで、木の高さはO(logn)=>スペースの複雑さはO(nlogn + n)= O(nlogn)です。ただし、これは実際のコードには当てはまりません。並列で実行されないためです。たとえば、N = 16の場合、これがマージソートのコードの実行方法です。N=16。

                           16
                          /
                         8
                        /
                       4
                     /
                    2
                   / \
                  1   1

使用されるスペースの数が32=2n = 2 *16<3nであることに注意してください

それからそれは上向きに合流します

                           16
                          /
                         8
                        /
                       4
                     /  \
                    2    2
                        / \                
                       1   1

これは34<3nです。それからそれは上向きに合流します

                           16
                          /
                         8
                        / \
                       4   4
                          /
                         2
                        / \ 
                       1   1

36 <16 * 3 = 48

その後、上向きにマージします

                           16
                          / \
                         8  8
                           / \
                          4   4
                             / \
                            2   2
                                /\
                               1  1

16 + 16 + 14 = 46 <3 * n = 48

より大きなケースでは、n = 64

                     64
                    /  \
                   32  32
                       / \
                      16  16
                          / \
                         8  8
                           / \
                          4   4
                             / \
                            2   2
                                /\
                               1  1

これは64*3 <= 3 * n = 3*64です

これは、一般的な場合の誘導によって証明できます。

したがって、配列を使用して実装する場合でも、マージ後に使用済みスペースをクリーンアップし、コードを並列ではなく順次実行する限り、スペースの複雑さは常にO(3n)= O(n)によって制限されます。

私の実装例を以下に示します。

templace<class X> 
void mergesort(X a[], int n) // X is a type using templates
{
    if (n==1)
    {
        return;
    }
    int q, p;
    q = n/2;
    p = n/2;
    //if(n % 2 == 1) p++; // increment by 1
    if(n & 0x1) p++; // increment by 1
        // note: doing and operator is much faster in hardware than calculating the mod (%)
    X b[q];

    int i = 0;
    for (i = 0; i < q; i++)
    {
        b[i] = a[i];
    }
    mergesort(b, i);
    // do mergesort here to save space
    // http://stackoverflow.com/questions/10342890/merge-sort-time-and-space-complexity/28641693#28641693
    // After returning from previous mergesort only do you create the next array.
    X c[p];
    int k = 0;
    for (int j = q; j < n; j++)
    {
        c[k] = a[j];
        k++;
    }
    mergesort(c, k);
    int r, s, t;
    t = 0; r = 0; s = 0;
    while( (r!= q) && (s != p))
    {
        if (b[r] <= c[s])
        {
            a[t] = b[r];
            r++;
        }
        else
        {
            a[t] = c[s];
            s++;
        }
        t++;
    }
    if (r==q)
    {
        while(s!=p)
        {
            a[t] = c[s];
            s++;
            t++;
        }
    }
    else
    {
        while(r != q)
        {
            a[t] = b[r];
            r++;
            t++;
        }
    }
    return;
}

これがお役に立てば幸いです!=)

すぐにチーロン、

トロント大学

于 2015-02-21T03:17:05.773 に答える
33

a)はい-完璧な世界では、サイズn、n / 2、n / 4 ...の対数nマージを実行する必要があります(または、1、2、3 ... n / 4、n / 2と言います) 、n-並列化できません)。これにより、O(n)が得られます。それでもO(n log n)です。それほど完璧ではない世界では、プロセッサの数は無限ではなく、コンテキストの切り替えと同期によって潜在的な利益が相殺されます。

b)要素をどこかに格納する必要があるため、スペースの複雑さは常にΩ(n)です。追加のスペースの複雑さは、配列を使用する実装ではO(n)になり、リンクリストの実装ではO(1)になります。実際には、リストを使用する実装では、リストポインタ用に追加のスペースが必要になるため、リストがすでにメモリにある場合を除いて、問題はありません。

スタックフレームを数える場合は編集します。O(n)+ O(log n)なので、配列の場合はO(n)のままです。リストの場合、それはO(log n)追加メモリです。

c)リストは、マージプロセス中に変更されるいくつかのポインターのみを必要とします。それには一定の追加メモリが必要です。

d)マージソートの複雑さの分析で、人々が「追加のスペース要件」などに言及するのはそのためです。要素をどこかに保存する必要があることは明らかですが、純粋主義者を寄せ付けないために「追加のメモリ」について言及することをお勧めします。

于 2012-04-27T00:06:52.297 に答える
1

a)はい、もちろん、マージソートの並列化は非常に有益です。それはnlognのままですが、定数は大幅に低くなるはずです。

b)リンクリストのスペースの複雑さは、O(n)、より具体的にはO(n)+ O(logn)である必要があります。これは*ではなく+であることに注意してください。漸近解析を行うときは、定数についてあまり気にしないでください。

c)漸近解析では、方程式の主要な項のみが重要であるため、*ではなく+があるという事実により、O(n)になります。サブリスト全体を複製する場合、それはO(nlogn)スペースになると思いますが、スマートリンクリストベースのマージソートでは、リストの領域を共有できます。

于 2012-04-27T00:01:02.397 に答える
1

マージソートの最悪の場合のパフォーマンス: O(n log n)、マージソートのベストケースのパフォーマンス:通常はO(n log n)、O(n)自然バリアント、マージソートの平均パフォーマンス:O(n log n)、マージソートの最悪の場合のスペースの複雑さ:О(n)合計、O(n)補助

于 2017-07-19T06:53:16.883 に答える
1

シンプルでスマートな思考。

合計レベル(L)= log2(N)。最後のレベルでは、ノードの数=Nです。

ステップ1:ノード= x(i)を持つすべてのレベル(i)について仮定しましょう。

ステップ2:時間計算量= x1 + x2 + x3 + x4 + .... + x(L-1)+ N(for i = L);

ステップ3:私たちが知っている事実、x1、x2、x3、x4 ...、x(L-1)<N

ステップ4:では、x1 = x2 = x3 = ... = x(L-1)=Nについて考えてみましょう。

ステップ5: 時間計算量=(N + N + N + ..(L)times)

時間計算量=O(N * L); L = log(N);を置きます。

時間計算量=O(N * log(N))

マージ中に余分な配列を使用するので、

スペースの複雑さ:O(N)。

ヒント:大きなO(x)時間とは、xが、平均的な場合にxを超えることは決してないという証拠で確実に言える最小の時間であることを意味します。

于 2020-10-13T04:22:03.267 に答える
0

最良の場合と最悪の場合の両方で、複雑さはO(nlog(n))です。ただし、各ステップで追加のNサイズの配列が必要になるため、複雑さを計算するための定数値を削除してO(n)になるため、スペースの複雑さはO(n + n)はO(2n)になります。

于 2019-08-21T13:34:08.853 に答える
-1

マージソートスペースの複雑さはです。これは、最大で再帰に進むことができ、再帰ごとに、再割り当てが必要なマージされた配列を格納するための追加のスペースがO(nlogn)あることを考えると、非常に明白です。リーチスタックフレームの深さのためであることを忘れないでくださいと言っている人のために。O(logn)O(n)O(n)O(n)

于 2019-07-18T08:17:05.917 に答える