arrays - tablas - resolver matrices en wolfram mathematica
Uso de funciones de matriz y tabla en Mathematica. Que es mejor cuando (5)
He sido principalmente un usuario de funciones de tabla en matemática. Sin embargo, he notado que en varios ejemplos en los que usé Array en lugar de Tabla para expresar el mismo resultado, se ejecutó notablemente más rápido, especialmente a medida que la dimensión de la tabla creció.
Entonces mi pregunta es esta: cuando la velocidad de ejecución es la principal preocupación, ¿cuándo es más apropiado usar la tabla?
¿Qué explica esta diferencia?
Mi conjetura es que dado que Arrays asume una relación funcional entre los elementos de la lista, los almacena de manera más eficiente, por lo tanto, utiliza menos memoria, lo que facilita el almacenamiento y el procesamiento posterior.
¿Es lo que está pasando?
Michael Trott en Programación (pp 707 - 710) aborda el problema de las diferencias entre Array
y Table
y argumenta que como Table
tiene el atributo HoldAll
, calcula su argumento para cada llamada, mientras que Array
"en la medida de lo posible" calcula su argumento solo en el principio. Esto puede conducir a diferencias en el comportamiento, así como la velocidad.
Attributes[Table]
{HoldAll, Protegido}
Attributes[Array]
{Protegido}
Michael Trott usa los siguientes ejemplos para ilustrar la diferencia en velocidad y comportamiento. Los tomo textualmente de su libro (disco). Espero no estar rompiendo ninguna regla al hacerlo.
Remove[a, i, j];
a = 0;
Table[a = a + 1; ToExpression[StringJoin["a" <> ToString[a]]][i, j],
{i, 3}, {j, 3}]
{{a1 [1, 1], a2 [1, 2], a3 [1, 3]}, {a4 [2, 1], a5 [2, 2], a6 [2, 3]}, {a7 [ 3, 1], a8 [3, 2], a9 [3, 3]}}
a = 0;
Array[a = a + 1;
ToExpression[StringJoin["a" <> ToString[a]]], {3, 3}]
{{a1 [1, 1], a1 [1, 2], a1 [1, 3]}, {a1 [2, 1], a1 [2, 2], a1 [2, 3]}, {a1 [ 3, 1], a1 [3, 2], a1 [3, 3]}}
(Note la diferencia de comportamiento)
Para ilustrar el efecto de precomputar el primer argumento, usa el siguiente ejemplo (nuevamente, literalmente, pág. 709).
o[a = 0;
Table[a = a + 1;
ToExpression[StringJoin["a" <> ToString[a]]][i, j],
{i, 3}, {j, 3}], {2000}] // Timing
Do[a = 0;
Array[a = a + 1; ToExpression[ StringJoin["a" <> ToString[a]]],
{3, 3}], {2000}] // Timing
{0.700173, nulo}
{0.102587, nulo}
(Estoy usando mma7 en una Mac. Mi copia de Programming usa v5.1. Puede que haya una actualización para esto)
Esta no es la única diferencia entre Array
y Table
discutida en Programación , por supuesto.
Veo otras respuestas, me interesará saber lo que otros piensan de este punto.
Sin dar algunos ejemplos concretos, es difícil responder a su pregunta correctamente.
Dado que Mathematica es un programa de código cerrado, las implementaciones exactas detrás de Table
y Array
no pueden ser conocidas, a menos que sean explicadas por personas que participaron en el desarrollo de Mathematica.
Estos factores dificultarán su capacidad de razonar qué estructura debe usar en qué circunstancias. La experimentación será su mejor apuesta, por lo que le sugiero que mantenga versiones paralelas de sus programas, una utilizando Table
y la otra utilizando Array
. De esta manera, podrás ver cuál es más rápido.
Aún mejor, podría escribir una función de envoltura que depende de una variable global. Esta función, según la variable, utilizará Table
o Array
como la implementación subyacente, por lo que podrá cambiar rápidamente de una versión a otra, sin hacer muchas modificaciones a su código.
Su declaración:
Sin embargo, he notado que en varios ejemplos en los que usé Array en lugar de Tabla para expresar el mismo resultado, se ejecutó notablemente más rápido, especialmente a medida que la dimensión de la tabla creció.
no suele ser cierto para matrices unidimensionales . Echar un vistazo:
Cl;
Needs["PlotLegends`"]
$HistoryLength = 0;
a = 10^8;
arr = {}; tab = {};
Do[(
arr = {First@Timing@Array[# &, a], arr};
tab = {First@Timing@Table[i, {i, a}], tab};
), {10}];
ListLinePlot[{Flatten@arr, Flatten@tab},
AxesLabel -> {Style["Iteration", 14], Style["Time", 14]},
PlotLegend -> {Style["Array", 14], Style["Table", 14]},
PlotRange -> {{0, 10}, {1.6, 2}}]
Una razón por la que Array
puede ser más rápida es que a menudo compila mejor su primer argumento.
En Mathematica 7:
In[1]:= SystemOptions[CompileOptions -> ArrayCompileLength]
Out[1]= {"CompileOptions" -> {"ArrayCompileLength" -> 250}}
y
In[2]:= SystemOptions[CompileOptions -> TableCompileLength]
Out[2]= {"CompileOptions" -> {"TableCompileLength" -> 250}}
Entonces, uno puede inferir que Array
y Table
deberían compilarse en el mismo punto.
Pero intentémoslo. timeAvg
función timeAvg
de Timo :
n = 15;
Array[Mod[#^2, 5]*(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]*(1 + j), {i, n}, {j, n}] // timeAvg
(* Out = 0.00034496 *)
(* Out = 0.00030016 *)
n = 16;
Array[Mod[#^2, 5]*(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]*(1 + j), {i, n}, {j, n}] // timeAvg
(* Out = 0.000060032 *)
(* Out = 0.0005008 *)
Lo que vemos es que Array puede compilar Mod[#^2, 5]*(1 + #2) &
mientras que Table
no puede compilar Mod[i^2, 5]*(1 + j)
y por lo tanto para Array
vuelve más rápida cuando se alcanza CompileLength. Muchas funciones no son tan favorables. Si simplemente cambia la multiplicación a la división en la función, lo que resulta en un resultado racional en lugar de entero, entonces esta compilación automática no ocurre, y la Table
es más rápida:
n = 15;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, n}, {j, n}] // timeAvg
(* Out = 0.000576 *)
(* Out = 0.00042496 *)
n = 16;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, n}, {j, n}] // timeAvg
(* Out = 0.0005744 *)
(* Out = 0.0004352 *)
¿Pero qué pasa si podemos hacer esta compilación también? Si usamos números de punto flotante, comenzando con 1.
, obtendremos una salida Real
, que se puede compilar:
n = 15;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}, 1.] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, 1., n}, {j, 1., n}] // timeAvg
(* Out = 0.0006256 *)
(* Out = 0.00047488 *)
n = 16;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}, 1.] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, 1., n}, {j, 1., n}] // timeAvg
(* Out = 0.00010528 *)
(* Out = 0.00053472 *)
Y una vez más, Array
es más rápido en la matriz de dimensión más grande.
Array
no tiene ventajas de rendimiento sobre la Table
. Hay diferencias entre ellos que hacen que uno prefiera a otro.
Table
es más lento en arreglos multidimensionales. Todos ellos utilizaron variables para mantener el tamaño de la tabla. Table
tiene atributos HoldAll
y solo auto-evalúa el límite de interacción más externo. Debido a que los iteradores internos permanecen sin evaluar, el elemento de la tabla no se compila. Usando números explícitos o With
con resultado en auto compilación: In[2]:= With[{b = 10^4, c = 10^4},
{Timing@(#[[1, 1]] &[ar = Array[(# + #2) &, {b, c}]]) ,
Timing@(#[[1, 1]] &[ta = Table[(i + j), {i, b}, {j, c}]])}
]
Out[2]= {{4.93, 2}, {4.742, 2}}
In[3]:= Attributes[Table]
Out[3]= {HoldAll, Protected}
La Array
permite crear una matriz de valores de función tanto como la Table
. Toman diferentes argumentos. Array
tiene una función:
In[34]:= Array[Function[{i, j}, a[i, j]], {3, 3}]
Out[34]= {{a[1, 1], a[1, 2], a[1, 3]}, {a[2, 1], a[2, 2],
a[2, 3]}, {a[3, 1], a[3, 2], a[3, 3]}}
while table toma una forma explícita:
In[35]:= Table[a[i, j], {i, 3}, {j, 3}]
Out[35]= {{a[1, 1], a[1, 2], a[1, 3]}, {a[2, 1], a[2, 2],
a[2, 3]}, {a[3, 1], a[3, 2], a[3, 3]}}
Array
solo puede revisar matrices normales, mientras que Table
puede realizar iteraciones arbitrarias en la lista:
In[36]:= Table[a[i, j], {i, {2, 3, 5, 7, 11}}, {j, {13, 17, 19}}]
Out[36]= {{a[2, 13], a[2, 17], a[2, 19]}, {a[3, 13], a[3, 17],
a[3, 19]}, {a[5, 13], a[5, 17], a[5, 19]}, {a[7, 13], a[7, 17],
a[7, 19]}, {a[11, 13], a[11, 17], a[11, 19]}}
A veces la Array
puede ser más sucinta. Compara la tabla de multiplicar:
In[37]:= Array[Times, {5, 5}]
Out[37]= {{1, 2, 3, 4, 5}, {2, 4, 6, 8, 10}, {3, 6, 9, 12, 15}, {4, 8,
12, 16, 20}, {5, 10, 15, 20, 25}}
versus
In[38]:= Table[i j, {i, 5}, {j, 5}]
Out[38]= {{1, 2, 3, 4, 5}, {2, 4, 6, 8, 10}, {3, 6, 9, 12, 15}, {4, 8,
12, 16, 20}, {5, 10, 15, 20, 25}}
Array
permite construir expresiones con cualquier encabezado, no solo en la lista:
In[39]:= Array[a, {3, 3}, {1, 1}, h]
Out[39]= h[h[a[1, 1], a[1, 2], a[1, 3]], h[a[2, 1], a[2, 2], a[2, 3]],
h[a[3, 1], a[3, 2], a[3, 3]]]
Por defecto, el encabezado h
se elige para que sea la List
da como resultado la creación de la matriz regular. La tabla no tiene esta flexibilidad.