Editar resumo
- Minha resposta original apenas observou que o código continha muitos cálculos replicados e que muitos dos poderes envolviam fatores de 1/3. Por exemplo,
pow(x, 0.1e1/0.3e1)
é o mesmo que cbrt(x)
.
- Minha segunda edição estava errada, e minha terceira extrapolou sobre esse erro. Isso é o que deixa as pessoas com medo de mudar os resultados semelhantes a oráculos de programas matemáticos simbólicos que começam com a letra 'M'. Risquei (ou seja,
risquei ) essas edições e empurrei-as para o final da revisão atual desta resposta. No entanto, não os eliminei. Eu sou humano. É fácil cometermos um erro.
- Minha quarta edição desenvolvido uma expressão muito compacto que representa corretamente a expressão complicada na questão IF os parâmetros
l1
, l2
e l3
são números reais positivos e se a
é um número real não-zero. (Ainda não ouvimos do OP sobre a natureza específica desses coeficientes. Dada a natureza do problema, essas são suposições razoáveis.)
- Esta edição tenta responder ao problema genérico de como simplificar essas expressões.
Primeiras coisas primeiro
Eu uso o Maple para gerar o código C ++ para evitar erros.
Maple e Mathematica às vezes perdem o óbvio. Ainda mais importante, os usuários do Maple e do Mathematica às vezes cometem erros. Substituir "frequentemente", ou talvez até "quase sempre", em vez de "às vezes é provavelmente mais perto do que acertar.
Você poderia ter ajudado o Maple a simplificar essa expressão, contando sobre os parâmetros em questão. No exemplo em questão, eu suspeito que l1
, l2
e l3
são números reais positivos e esse a
é um número real diferente de zero. Se for esse o caso, diga isso. Esses programas matemáticos simbólicos normalmente assumem que as quantidades disponíveis são complexas. Restringir o domínio permite que o programa faça suposições que não são válidas nos números complexos.
Como simplificar essas grandes bagunças de programas de matemática simbólica (esta edição)
Os programas de matemática simbólica normalmente fornecem a capacidade de fornecer informações sobre os vários parâmetros. Use essa habilidade, principalmente se o seu problema envolver divisão ou exponenciação. No exemplo em mãos, você poderia ter ajudado a bordo simplificar essa expressão, dizendo-se que l1
, l2
e l3
são números reais positivos e que a
é um número real não-zero. Se for esse o caso, diga isso. Esses programas matemáticos simbólicos normalmente assumem que as quantidades disponíveis são complexas. Restringir o domínio permite que o programa faça suposições como a x b x = (ab) x . Isso ocorre apenas se a
e b
forem números reais positivos e se x
for real. Não é válido nos números complexos.
Em última análise, esses programas matemáticos simbólicos seguem algoritmos. Ajude-o junto. Tente expandir, coletar e simplificar antes de gerar o código. Nesse caso, você poderia ter coletado os termos que envolvem um fator de mu
e aqueles que envolvem um fator de K
. Reduzir uma expressão à sua "forma mais simples" continua sendo um pouco uma arte.
Quando você receber uma bagunça feia de código gerado, não aceite isso como uma verdade que você não deve tocar. Tente simplificar você mesmo. Veja o que o programa matemático simbólico tinha antes de gerar o código. Veja como eu reduzi sua expressão a algo muito mais simples e rápido, e como a resposta de Walter levou a minha vários passos adiante. Não existe receita mágica. Se houvesse uma receita mágica, Maple a teria aplicado e dado a resposta que Walter deu.
Sobre a questão específica
Você está fazendo muitas adições e subtrações nesse cálculo. Você pode entrar em apuros se tiver termos que quase cancelam um ao outro. Você está desperdiçando muita CPU se tiver um termo que domina os outros.
Em seguida, você está desperdiçando muita CPU ao realizar cálculos repetidos. A menos que você tenha ativado -ffast-math
, o que permite ao compilador quebrar algumas das regras do ponto flutuante IEEE, o compilador não irá (na verdade, não deve) simplificar essa expressão para você. Em vez disso, fará exatamente o que você disse para fazer. No mínimo, você deve calcular l1 * l2 * l3
antes de computar essa bagunça.
Por fim, você está fazendo muitas chamadas para pow
, o que é extremamente lento. Observe que várias dessas chamadas são no formato (l1 * l2 * l3) (1/3) . Muitas dessas chamadas para pow
poderiam ser realizadas com uma única chamada para std::cbrt
:
l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;
Com isso,
X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)
torna-se X * l123_pow_1_3
.
X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
torna-se X / l123_pow_1_3
.
X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)
torna-se X * l123_pow_4_3
.
X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)
torna-se X / l123_pow_4_3
.
Maple não percebeu o óbvio.
Por exemplo, há uma maneira muito mais fácil de escrever
(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Partindo do princípio de que l1
, l2
e l3
são reais em vez de números complexos, e que a verdadeira raiz cubo (em vez da raiz complexo princípio) estão a ser extraído, o acima reduz a
2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))
ou
2.0/(3.0 * l123_pow_1_3)
Usando em cbrt_l123
vez de l123_pow_1_3
, a expressão desagradável na pergunta se reduz 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);
Sempre verifique, mas sempre simplifique também.
Aqui estão alguns dos meus passos para chegar ao acima:
// 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);
Resposta errada, intencionalmente mantida para humildade
Observe que isso é atingido. Está errado.
Atualizar
Maple não percebeu o óbvio. Por exemplo, há uma maneira muito mais fácil de escrever
(pow (l1 * l2 * l3, -0,1e1 / 0,3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0,4e1 / 0,3e1) / 0,3e1)
Partindo do princípio de que l1
, l2
e l3
são reais em vez de números complexos, e que a verdadeira raiz cubo (em vez da raiz complexo princípio) estão a ser extraído, o acima reduz a zero. Este cálculo de zero é repetido várias vezes.
Segunda atualização
Se eu fiz as contas certas (não há garantia de que fiz as contas certas), a expressão desagradável na pergunta se reduz 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);
O acima assume que l1
, l2
e l3
são números reais positivos.
pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
por uma variável ... Você precisa avaliar seu código para ter certeza se ele executa rápido ou lento, no entanto.