Editar resumen
- Mi respuesta original simplemente señaló que el código contenía muchos cálculos replicados y que muchos de los poderes involucraban factores de 1/3. Por ejemplo,
pow(x, 0.1e1/0.3e1)
es lo mismo que cbrt(x)
.
- Mi segunda edición fue simplemente incorrecta y la tercera extrapoló este error. Esto es lo que hace que la gente tenga miedo de cambiar los resultados similares a los de un oráculo de los programas de matemáticas simbólicos que comienzan con la letra 'M'. Eliminé (es decir,
taché ) esas ediciones y las empujé al final de la revisión actual de esta respuesta. Sin embargo, no los eliminé. Soy humano. Es fácil para nosotros cometer un error.
- Mi cuarta edición desarrolló una expresión muy compacta que representa correctamente la expresión enrevesada en la pregunta SI los parámetros
l1
, l2
y l3
son números reales positivos y si a
es un número real distinto de cero. (Aún no hemos escuchado del OP sobre la naturaleza específica de estos coeficientes. Dada la naturaleza del problema, estos son supuestos razonables).
- Esta edición intenta responder al problema genérico de cómo simplificar estas expresiones.
Lo primero es lo primero
Utilizo Maple para generar el código C ++ para evitar errores.
Maple y Mathematica a veces pasan por alto lo obvio. Aún más importante, los usuarios de Maple y Mathematica a veces cometen errores. Sustituir "a menudo", o tal vez incluso "casi siempre", en lugar de "a veces es probablemente más cercano a la marca".
Podrías haber ayudado a Maple a simplificar esa expresión contándole sobre los parámetros en cuestión. En el ejemplo que nos ocupa, sospecho que l1
, l2
y l3
son números reales positivos y ese a
es un número real distinto de cero. Si ese es el caso, dígaselo. Esos programas de matemáticas simbólicas generalmente asumen que las cantidades disponibles son complejas. La restricción del dominio permite que el programa haga suposiciones que no son válidas en los números complejos.
Cómo simplificar esos grandes líos de los programas de matemáticas simbólicas (esta edición)
Los programas de matemáticas simbólicas generalmente brindan la capacidad de proporcionar información sobre los diversos parámetros. Use esa habilidad, particularmente si su problema involucra división o exponenciación. En el ejemplo que nos ocupa, se podría haber ayudado a simplificar arce esa expresión por diciéndole que l1
, l2
y l3
son números reales positivos y que a
es un número real distinto de cero. Si ese es el caso, dígaselo. Esos programas de matemáticas simbólicas generalmente asumen que las cantidades disponibles son complejas. Restringir el dominio permite que el programa haga suposiciones como a x b x = (ab) x . Esto es solo si a
y b
son números reales positivos y si x
es real. No es válido en números complejos.
En última instancia, esos programas matemáticos simbólicos siguen algoritmos. Ayúdalo. Pruebe a expandir, recopilar y simplificar antes de generar código. En este caso, podría haber recopilado los términos que implican un factor de mu
y los que implican un factor de K
. Reducir una expresión a su "forma más simple" sigue siendo un arte.
Cuando tenga un feo lío de código generado, no lo acepte como una verdad que no debe tocar. Intente simplificarlo usted mismo. Mire lo que tenía el programa matemático simbólico antes de generar código. Mira cómo reduje tu expresión a algo mucho más simple y mucho más rápido, y cómo la respuesta de Walter llevó la mía varios pasos más allá. No existe una receta mágica. Si hubiera una receta mágica, Maple la habría aplicado y dado la respuesta que dio Walter.
Sobre la pregunta específica
Estás sumando y restando mucho en ese cálculo. Puede meterse en serios problemas si tiene términos que casi se cancelan entre sí. Está desperdiciando mucha CPU si tiene un término que domina sobre los demás.
A continuación, está desperdiciando mucha CPU al realizar cálculos repetidos. A menos que haya habilitado -ffast-math
, lo que permite que el compilador rompa algunas de las reglas del punto flotante IEEE, el compilador no simplificará (de hecho, no debe) esa expresión para usted. En cambio, hará exactamente lo que le dijo que hiciera. Como mínimo, debe calcular l1 * l2 * l3
antes de calcular ese desorden.
Finalmente, está haciendo muchas llamadas a pow
, lo cual es extremadamente lento. Tenga en cuenta que varias de esas llamadas tienen el formato (l1 * l2 * l3) (1/3) . Muchas de esas llamadas a pow
podrían realizarse con una sola llamada a std::cbrt
:
l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;
Con este,
X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)
se convierte X * l123_pow_1_3
.
X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
se convierte X / l123_pow_1_3
.
X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)
se convierte X * l123_pow_4_3
.
X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)
se convierte X / l123_pow_4_3
.
Maple se perdió lo obvio.
Por ejemplo, hay una forma mucho más sencilla de escribir
(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Suponiendo que l1
, l2
y l3
son reales en lugar de los números complejos, y que la raíz cúbica real (en lugar de al principio raíz compleja) se van a extraer, lo anterior se reduce a:
2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))
o
2.0/(3.0 * l123_pow_1_3)
Usando en cbrt_l123
lugar de l123_pow_1_3
, la expresión desagradable en la pregunta se reduce a
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
mu/(3.0*l123)*( pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
+ pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
+ pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
+K*(l123-1.0)*(N1+N2+N3);
Siempre verifique dos veces, pero siempre simplifique también.
Estos son algunos de mis pasos para llegar a lo anterior:
// Step 0: Trim all whitespace.
T=(mu*(pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l1-pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l1/0.3e1-pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l1/0.3e1)/a+K*(l1*l2*l3-0.1e1)*l2*l3)*N1/l2/l3+(mu*(-pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l2/0.3e1+pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l2-pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l2/0.3e1)/a+K*(l1*l2*l3-0.1e1)*l1*l3)*N2/l1/l3+(mu*(-pow(l1*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l3/0.3e1-pow(l2*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a/l3/0.3e1+pow(l3*pow(l1*l2*l3,-0.1e1/0.3e1),a)*a*(pow(l1*l2*l3,-0.1e1/0.3e1)-l1*l2*l3*pow(l1*l2*l3,-0.4e1/0.3e1)/0.3e1)*pow(l1*l2*l3,0.1e1/0.3e1)/l3)/a+K*(l1*l2*l3-0.1e1)*l1*l2)*N3/l1/l2;
// Step 1:
// l1*l2*l3 -> l123
// 0.1e1 -> 1.0
// 0.4e1 -> 4.0
// 0.3e1 -> 3
l123 = l1 * l2 * l3;
T=(mu*(pow(l1*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l1-pow(l2*pow(l123,-1.0/3),a)*a/l1/3-pow(l3*pow(l123,-1.0/3),a)*a/l1/3)/a+K*(l123-1.0)*l2*l3)*N1/l2/l3+(mu*(-pow(l1*pow(l123,-1.0/3),a)*a/l2/3+pow(l2*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l2-pow(l3*pow(l123,-1.0/3),a)*a/l2/3)/a+K*(l123-1.0)*l1*l3)*N2/l1/l3+(mu*(-pow(l1*pow(l123,-1.0/3),a)*a/l3/3-pow(l2*pow(l123,-1.0/3),a)*a/l3/3+pow(l3*pow(l123,-1.0/3),a)*a*(pow(l123,-1.0/3)-l123*pow(l123,-4.0/3)/3)*pow(l123,1.0/3)/l3)/a+K*(l123-1.0)*l1*l2)*N3/l1/l2;
// Step 2:
// pow(l123,1.0/3) -> cbrt_l123
// l123*pow(l123,-4.0/3) -> pow(l123,-1.0/3)
// (pow(l123,-1.0/3)-pow(l123,-1.0/3)/3) -> 2.0/(3.0*cbrt_l123)
// *pow(l123,-1.0/3) -> /cbrt_l123
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T=(mu*(pow(l1/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l1-pow(l2/cbrt_l123,a)*a/l1/3-pow(l3/cbrt_l123,a)*a/l1/3)/a+K*(l123-1.0)*l2*l3)*N1/l2/l3+(mu*(-pow(l1/cbrt_l123,a)*a/l2/3+pow(l2/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l2-pow(l3/cbrt_l123,a)*a/l2/3)/a+K*(l123-1.0)*l1*l3)*N2/l1/l3+(mu*(-pow(l1/cbrt_l123,a)*a/l3/3-pow(l2/cbrt_l123,a)*a/l3/3+pow(l3/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l3)/a+K*(l123-1.0)*l1*l2)*N3/l1/l2;
// Step 3:
// Whitespace is nice.
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
(mu*( pow(l1/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l1
-pow(l2/cbrt_l123,a)*a/l1/3
-pow(l3/cbrt_l123,a)*a/l1/3)/a
+K*(l123-1.0)*l2*l3)*N1/l2/l3
+(mu*(-pow(l1/cbrt_l123,a)*a/l2/3
+pow(l2/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l2
-pow(l3/cbrt_l123,a)*a/l2/3)/a
+K*(l123-1.0)*l1*l3)*N2/l1/l3
+(mu*(-pow(l1/cbrt_l123,a)*a/l3/3
-pow(l2/cbrt_l123,a)*a/l3/3
+pow(l3/cbrt_l123,a)*a*2.0/(3.0*cbrt_l123)*cbrt_l123/l3)/a
+K*(l123-1.0)*l1*l2)*N3/l1/l2;
// Step 4:
// Eliminate the 'a' in (term1*a + term2*a + term3*a)/a
// Expand (mu_term + K_term)*something to mu_term*something + K_term*something
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
(mu*( pow(l1/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l1
-pow(l2/cbrt_l123,a)/l1/3
-pow(l3/cbrt_l123,a)/l1/3))*N1/l2/l3
+K*(l123-1.0)*l2*l3*N1/l2/l3
+(mu*(-pow(l1/cbrt_l123,a)/l2/3
+pow(l2/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l2
-pow(l3/cbrt_l123,a)/l2/3))*N2/l1/l3
+K*(l123-1.0)*l1*l3*N2/l1/l3
+(mu*(-pow(l1/cbrt_l123,a)/l3/3
-pow(l2/cbrt_l123,a)/l3/3
+pow(l3/cbrt_l123,a)*2.0/(3.0*cbrt_l123)*cbrt_l123/l3))*N3/l1/l2
+K*(l123-1.0)*l1*l2*N3/l1/l2;
// Step 5:
// Rearrange
// Reduce l2*l3*N1/l2/l3 to N1 (and similar)
// Reduce 2.0/(3.0*cbrt_l123)*cbrt_l123/l1 to 2.0/3.0/l1 (and similar)
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
(mu*( pow(l1/cbrt_l123,a)*2.0/3.0/l1
-pow(l2/cbrt_l123,a)/l1/3
-pow(l3/cbrt_l123,a)/l1/3))*N1/l2/l3
+(mu*(-pow(l1/cbrt_l123,a)/l2/3
+pow(l2/cbrt_l123,a)*2.0/3.0/l2
-pow(l3/cbrt_l123,a)/l2/3))*N2/l1/l3
+(mu*(-pow(l1/cbrt_l123,a)/l3/3
-pow(l2/cbrt_l123,a)/l3/3
+pow(l3/cbrt_l123,a)*2.0/3.0/l3))*N3/l1/l2
+K*(l123-1.0)*N1
+K*(l123-1.0)*N2
+K*(l123-1.0)*N3;
// Step 6:
// Factor out mu and K*(l123-1.0)
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
mu*( ( pow(l1/cbrt_l123,a)*2.0/3.0/l1
-pow(l2/cbrt_l123,a)/l1/3
-pow(l3/cbrt_l123,a)/l1/3)*N1/l2/l3
+ (-pow(l1/cbrt_l123,a)/l2/3
+pow(l2/cbrt_l123,a)*2.0/3.0/l2
-pow(l3/cbrt_l123,a)/l2/3)*N2/l1/l3
+ (-pow(l1/cbrt_l123,a)/l3/3
-pow(l2/cbrt_l123,a)/l3/3
+pow(l3/cbrt_l123,a)*2.0/3.0/l3)*N3/l1/l2)
+K*(l123-1.0)*(N1+N2+N3);
// Step 7:
// Expand
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
mu*( pow(l1/cbrt_l123,a)*2.0/3.0/l1*N1/l2/l3
-pow(l2/cbrt_l123,a)/l1/3*N1/l2/l3
-pow(l3/cbrt_l123,a)/l1/3*N1/l2/l3
-pow(l1/cbrt_l123,a)/l2/3*N2/l1/l3
+pow(l2/cbrt_l123,a)*2.0/3.0/l2*N2/l1/l3
-pow(l3/cbrt_l123,a)/l2/3*N2/l1/l3
-pow(l1/cbrt_l123,a)/l3/3*N3/l1/l2
-pow(l2/cbrt_l123,a)/l3/3*N3/l1/l2
+pow(l3/cbrt_l123,a)*2.0/3.0/l3*N3/l1/l2)
+K*(l123-1.0)*(N1+N2+N3);
// Step 8:
// Simplify.
l123 = l1 * l2 * l3;
cbrt_l123 = cbrt(l123);
T =
mu/(3.0*l123)*( pow(l1/cbrt_l123,a)*(2.0*N1-N2-N3)
+ pow(l2/cbrt_l123,a)*(2.0*N2-N3-N1)
+ pow(l3/cbrt_l123,a)*(2.0*N3-N1-N2))
+K*(l123-1.0)*(N1+N2+N3);
Respuesta incorrecta, guardada intencionalmente por humildad
Tenga en cuenta que esto está afectado. Está incorrecto.
Actualizar
Maple se perdió lo obvio. Por ejemplo, hay una forma mucho más sencilla de escribir
(pow (l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Suponiendo que l1
, l2
y l3
son números reales en lugar de complejos, y que se debe extraer la raíz cúbica real (en lugar de la raíz compleja principal), lo anterior se reduce a cero. Este cálculo de cero se repite muchas veces.
Segunda actualización
Si hice bien las matemáticas (no hay garantía de que haya hecho bien las matemáticas), la expresión desagradable en la pregunta se reduce a
l123 = l1 * l2 * l3;
cbrt_l123_inv = 1.0 / cbrt(l123);
nasty_expression =
K * (l123 - 1.0) * (N1 + N2 + N3)
- ( pow(l1 * cbrt_l123_inv, a) * (N2 + N3)
+ pow(l2 * cbrt_l123_inv, a) * (N1 + N3)
+ pow(l3 * cbrt_l123_inv, a) * (N1 + N2)) * mu / (3.0*l123);
Lo anterior asume que l1
, l2
y l3
son números reales positivos.
pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
con una variable ... Sin embargo, debe comparar su código para asegurarse de si se ejecuta rápido o lento.