アレイを拡大するには何枚のコピーが必要ですか?


8

動的配列の分析を読んでいます(Skienaのアルゴリズムマニュアルから)。
つまり、配列構造があり、スペースがなくなるたびに、元のサイズの2倍のサイズの新しい配列を割り当てます。

これは、アレイのサイズを変更する必要があるときに発生する無駄について説明しています。
これは、(n / 2)+1からnまでが最大で1回移動されるか、まったく移動されないことを示しています。これは明らかです。
次に、要素の半分が1回移動し、要素の4分の1が2回移動する、などと記述すると、移動の総数Mは次のようになります。

ここに画像の説明を入力してください

これは実際に起こるよりも多くのコピーを追加するように私には思えます。

例えば

以下の場合:

array of 1 element
+--+
|a |
+--+

double the array (2 elements)  
+--++--+  
|a ||b |  
+--++--+  

double the array (4 elements)  
+--++--++--++--+  
|a ||b ||c ||c |  
+--++--++--++--+  

double the array (8 elements)  
+--++--++--++--++--++--++--++--+    
|a ||b ||c ||c ||x ||x ||x ||x |  
+--++--++--++--++--++--++--++--+    

double the array (16 elements)  
+--++--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+    
|a ||b ||c ||c ||x ||x ||x ||x ||  ||  ||  ||  ||  ||  ||  ||  |   
+--++--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+   

x要素を4回コピーし、c要素を4回コピーし、b要素を4回コピーし、1つの要素を5回コピーしたため、合計は4 + 4 + 4 + 5 = 17コピー/移動です。

しかし、式によれば、1 *(16/2)+ 2 *(16/4)+ 3 *(16/8)+ 4 *(16/16)= 8 + 8 + 6 + 4 = 26のコピーが必要です配列を16要素に拡大するための要素。

これはいくつかの間違いですか、それとも式の目的はおおよその上限近似値を提供することですか?または私はここで何かを誤解していますか?


別の要因:現実の世界では、割り当てられた空の要素は(JavaやC#のような高級言語では)ゼロにされます。これには書き込みが必要ですが(読み取りではありません)、コピーの半分のコストがかかるようです。
dbkk

1
あなたの合計は正しくありません。bコピーは3回、各c2回、各x1回です。15部。
ドナルフェロー

回答:


5

最初に、bは3回移動され、aは4回移動されます。これにより、合計で4 + 4 + 3 + 4 = 15コピーになります。

数式はn = 8で埋める必要があると思います:1 *(8/2)(xは1回コピーされます)+ 2 *(8/4)(cは2回コピーされます)+ 3 *(8/8)(bは3回コピーされます)= 11.つまり、式には合計自体に加えて「+ log 2 n + 1」の項が欠落しているようです。

移動数をカウントするより自然な方法のように思えるのは、コピーごとに移動された要素の数をカウントすることです。

i = 1からi = ceiling(log 2 n)の合計:2 i-1

あなたの場合、n = 16なので、ceiling(log 2 16)= 4であり、上記の合計は2 0 +2 1 +2 2 +2 3 = 1 + 2 + 4 + 8 = 15です。

このスキーナのアルゴリズムマニュアルを見つけて正しいかどうかを確認します。

更新:スキーナのアルゴリズムマニュアルでその部分を見つけました。彼が使っている合計に欠けている用語があるようです。ただし、結論は正しいです。

M = i = 1からi =天井までの合計(log 2 n):2 i-1 = i = 0からi =天井までの合計(log 2 n)-1:2 i = 2 天井(log 2 n)- 1 + 1 <=(2 log 2 n + 1-1 + 1)= 2 * n

(私はあなたのためにこれらの数式をより良い方法でフォーマットできたらいいのにと思います)

この段落の要点は、償却分析の例を示すことです。可能性のあるメソッドなどのメソッドは、動的配列が非常にうまく機能する理由を(アドホックではなく)より良くしますが、このメソッドはやや高度です。

この本に誤りがあると確信している場合は、これについて著者に連絡することを検討してください(もちろん、建設的な方法で-この本には多くのページがあり、最後のすべてを正しいものにするのは難しく、常に本が正しいという可能性と私たちがそれを間違えた)。エラッタでこの特定のものを見つけていません。


私は式を少しフォーマットされた:-)
ペーテルTörök

ありがとう、これでずっと見栄えがよくなりました。私はLaTeXのフォーマットに慣れています。Programmers.SEではそれが可能だとは思いません。
Alex ten Brink

@Alex:+1ありがとうございます。OPでは、nが16ではなく8である必要があると思うのはなぜですか。私はそれを取得しませんでした。
user10326

それゆえ、i * n / 2 ^ iの項は意味があります。i= 1の場合、1 * n / 2について話します。これは、1回コピーされる入力の半分に相当します。彼の例では、1回コピーされる4つのx位置と8/2 = 4があるため、n = 8の方が理にかなっています。n = 16の場合、16/2 = 8要素はおそらく一度コピーされるだけで、例とは一致しません。
Alex ten Brink

2

低いブロックカウントレベルでは、メモリ割り当てが実際に発生することはほとんどありません。メモリマネージャーはメモリブロックを扱い、実際に要求された割り当て要求よりも大きなメモリブロックを定期的に割り当てます。

同様に、配列クラスの実装では、割り当てを切り上げて、いくつかの追加要素を許可する可能性があります。

編集:

さらに考察すると、実際のコピーは、説明したとおりに発生する可能性はほとんどありません。プロセッサには通常、ブロックコピーコマンドがあり、単一のassmbler命令を使用して、配列データを単一のメモリブロックとして新しいアドレスにコピーします。


1
これは私の質問とどのように関連していますか?
user10326

割り当てが発生する必要がない場合は、配列要素を新しいメモリ空間にコピーする必要はありません。
Michael Shaw

1
しかし、私は式について尋ねています。
user10326

十分に公正な、それは数学的な質問であり、プログラミングサイトでプログラミングの回答を提供しています...;)
Michael Shaw

0

本で与えられた式は単に間違っていると思います。i乗算器はそれを修正するための式から削除する必要があります。

アスカーの例を見て、1要素の配列array-1、2要素の配列を呼び出してみましょう- array-2、4要素の配列array-4

したがって、本に応じて、この特定の例では、コピーの数は次の式によって決まります。


M = 1⋅8 + 2⋅4 + 3⋅2 + 4⋅1

合計の最初の項 1⋅8array-8'sアイテムをにコピーするためのものarray-16です。

array-4'sアイテムを(a, b, c, c)2回コピーします。からarray-4までarray-8。そして、array-8'sアイテムをにコピーするときarray-16にコピー(a, b, c, c)に、アイテムを2回目にコピーします。本に応じて、したがって2番目の用語:2⋅4

ただし、この1⋅8用語では、(a, b, c, c)からarray-8へのアイテムのコピーがすでに考慮されていることに注意してください。array-16。したがって、この2⋅4用語には2乗数を含めないでください。

同じロジックが他のすべての用語に適用されます。だから、掛け算iは間違いです。


それが何をするのか、そしてあなたが尋ねられた質問に答えるのになぜそれを勧めるのかについてもっと説明してもらえますか?Stack Exchange では「リンクのみの回答」はまったく歓迎されません
gnat

承知しました。私はcs.stackexchangeから私の答えをコピーします。問題は、programmers.stackexchangeが適切な数式のフォーマットを許可しないことです。
Nik

私の読書によると、CSでの回答の式は、バックティックを使用したコード形式を使用して合理的に概算できます:M=1⋅8+2⋅4+3⋅2+4⋅1など
gnat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.