8

Mathematicaでは、すべてのマシンサイズの整数または浮動小数点数を含むベクトル(または長方形の配列)をパックされた配列に格納できます。これらのオブジェクトはメモリをあまり消費せず、一部の操作ははるかに高速です。

RandomReal可能な場合はパック配列を生成します。Developerパックされた配列は、関数を使用してアンパックできますFromPackedArray

これらのタイミングを考慮してください

lst = RandomReal[1, 5000000];

Total[lst] // Timing
Plus @@ lst // Timing

lst = Developer`FromPackedArray[lst];

Total[lst] // Timing
Plus @@ lst // Timing

Out[1]= {0.016, 2.50056*10^6}

Out[2]= {0.859, 2.50056*10^6}

Out[3]= {0.625, 2.50056*10^6}

Out[4]= {0.64, 2.50056*10^6}

したがって、パックドアレイの場合は、非パックドアレイの場合Totalよりも何倍も高速ですPlus @@が、ほぼ同じです。Plus @@パックされた配列では実際には少し遅いことに注意してください。

今考えてみましょう

lst = RandomReal[100, 5000000];
Times @@ lst // Timing

lst = Developer`FromPackedArray[lst];
Times @@ lst // Timing

Out[1]= {0.875, 5.8324791357*10^7828854}

Out[1]= {0.625, 5.8324791357*10^7828854}

最後に、私の質問です。Mathematicaには、パックされた配列のリスト積に対して、類似した高速な方法がありTotalますか?

数値誤差が乗算と複合する方法のために、これは不可能かもしれないと私は思う。また、この関数は、マシン以外のフロートを返すことができる必要があります。

4

3 に答える 3

9

に相当する乗法があるかどうかも疑問に思いましたTotal

本当にそれほど悪くない解決策は

In[1]:= lst=RandomReal[2,5000000];
        Times@@lst//Timing
        Exp[Total[Log[lst]]]//Timing
Out[2]= {2.54,4.370467929041*10^-666614}
Out[3]= {0.47,4.370467940*10^-666614}

数値が正であり、大きすぎたり小さすぎたりしない限り、丸め誤差はそれほど悪くありません。(1) 数値が正の浮動小数点数である場合、Log演算はパックされた配列にすばやく適用できます。Total(2)のパック配列メソッドを使用して、数値をすばやく追加できます。(3) その後、機械サイズでないフロートが必要になるのは最後のステップだけです。

正と負のフロートの両方で機能するソリューションについては、この SO の回答を参照してください。

このソリューションが、非マシンサイズの回答を生成するフロートで機能することを簡単に確認してみましょう。アンドリューのものと比較してください(はるかに高速です)compiledListProduct

In[10]:= compiledListProduct = 
          Compile[{{l, _Real, 1}}, 
           Module[{tot = 1.}, Do[tot *= x, {x, l}]; tot], 
           CompilationTarget -> "C"]

In[11]:= lst=RandomReal[{0.05,.10},15000000];
         Times@@lst//Timing
         Exp[Total[Log[lst]]]//Timing
         compiledListProduct[lst]//Timing
Out[12]= {7.49,2.49105025389*10^-16998863}
Out[13]= {0.5,2.4910349*10^-16998863}
Out[14]= {0.07,0.}

>1より大きな ( ) 実数を選択するcompiledListProductと、警告が表示 CompiledFunction::cfne: Numerical error encountered; proceeding with uncompiled evaluation.され、結果が得られるまでに時間がかかります...


Sum興味深い点の 1 つは、との両方Productが任意のリストを取得できることです。Sum正常に動作します

In[4]:= lst=RandomReal[2,5000000];
         Sum[i,{i,lst}]//Timing
         Total[lst]//Timing
Out[5]= {0.58,5.00039*10^6}
Out[6]= {0.02,5.00039*10^6}

PackedArrayしかし、ここのテスト例のような長いs のProduct場合、自動コンパイルされたコード (バージョン 8.0) がアンダーフロー/オーバーフローを適切にキャッチしないため、失敗します。

In[7]:= lst=RandomReal[2,5000000];
         Product[i,{i,lst}]//Timing
         Times@@lst//Timing
Out[8]= {0.,Compile`AutoVar12!}
Out[9]= {2.52,1.781498881673*10^-666005}

有用な WRI テクニカル サポートが提供する回避策は、 を使用して製品のコンパイルをオフにすることですSetSystemOptions["CompileOptions" -> {"ProductCompileLength" -> Infinity}]。別のオプションは、を使用することlst=Developer`FromPackedArray[lst]です。

于 2011-03-14T05:05:09.350 に答える
4

まず、混乱を避けるために、結果がすべてハードウェアマシンの精度の数値として表現できる例を見てください。これは、すべて以下でなければなりません。

In[1]:= $MaxMachineNumber

Out[1]= 1.79769*10^308

Totalの例には、すでにこの優れた(そして高速な)プロパティがあります。マシン番号を使用したTimesの例のバリエーションを次に示します。

In[2]:= lst = RandomReal[{0.99, 1.01}, 5000000];
Times @@ lst // Timing

Out[3]= {1.435, 1.38851*10^-38}

これで、コンパイルを使用して、この操作を効率的に実行するためのコンパイル済み関数を作成できます。

In[4]:= listproduct = 
 Compile[{{l, _Real, 1}}, 
  Module[{tot = 1.}, Do[tot *= x, {x, l}]; tot]]

Out[4]= CompiledFunction[{l},Module[{tot=1.},Do[tot*=x,{x,l}];tot],-CompiledCode-]

それははるかに高速です:

In[5]:= listproduct[lst] // Timing

Out[5]= {0.141, 1.38851*10^-38}

CコンパイラとMathematica8があれば、Cコードまで自動的にコンパイルすることもできます。一時DLLが作成され、実行時にMathematicaにリンクされます。

In[6]:= compiledlistproduct = 
 Compile[{{l, _Real, 1}}, 
  Module[{tot = 1.}, Do[tot *= x, {x, l}]; tot], 
  CompilationTarget -> "C"]

Out[6]= CompiledFunction[{l},Module[{tot=1.},Do[tot*=x,{x,l}];tot],-CompiledCode-]

これにより、組み込みのMathematica関数とそれほど変わらないパフォーマンスが得られます。

In[7]:= compiledlistproduct[lst] // Timing

Out[7]= {0.015, 1.38851*10^-38}

製品が実際に$ MaxMachineNumber(または$ MinMachineNumber)を超える場合は、に固執する方がよいことに注意してくださいApply[Times, list]。結果がこれほど大きくなる可能性がある場合は、同じコメントがTotalにも当てはまります。

In[11]:= lst = RandomReal[10^305, 5000000];
Plus @@ lst // Timing

Out[12]= {1.435, 2.499873364498981*10^311}

In[13]:= lst = RandomReal[10^305, 5000000];
Total[lst] // Timing

Out[14]= {1.576, 2.500061580905602*10^311}
于 2011-03-14T05:30:15.617 に答える
3

Simon の方法は高速ですが、負の値では失敗します。それを私の他の質問に対する彼の答えと組み合わせると、ネガを処理する高速なソリューションがここにあります。ありがとう、サイモン。

関数

f = (-1)^(-1 /. Rule @@@ Tally@Sign@# /. -1 -> 0) * Exp@Total@Log@Abs@# &;

テスト

lst = RandomReal[{-50, 50}, 5000000];

Times @@ lst // Timing
f@lst // Timing

lst = Developer`FromPackedArray[lst];

Times @@ lst // Timing
f@lst // Timing

{0.844, -4.42943661963*10^6323240}

{0.062, -4.4294366*10^6323240}

{0.64, -4.42943661963*10^6323240}

{0.203, -4.4294366*10^6323240}
于 2011-03-15T06:53:20.407 に答える