Modifier le résumé
- Ma réponse originale a simplement noté que le code contenait beaucoup de calculs répliqués et que de nombreuses puissances impliquaient des facteurs de 1/3. Par exemple,
pow(x, 0.1e1/0.3e1)
est le même que cbrt(x)
.
- Ma deuxième modification était tout simplement erronée et ma troisième extrapolait cette erreur. C'est ce qui fait peur aux gens de changer les résultats de type oracle des programmes de mathématiques symboliques qui commencent par la lettre «M». J'ai rayées (c. -à-
grève ) ces modifications et les a poussés au fond de la révision actuelle de cette réponse. Cependant, je ne les ai pas supprimés. Je suis humain. Il est facile pour nous de faire une erreur.
- Mon quatrième édition a développé une expression très compacte qui représente correctement l'expression alambiquée dans la question SI les paramètres
l1
, l2
et l3
sont des nombres réels positifs et si a
un nombre réel non nul. (Nous n'avons pas encore entendu le PO concernant la nature spécifique de ces coefficients. Compte tenu de la nature du problème, il s'agit d'hypothèses raisonnables.)
- Cette modification tente de répondre au problème générique de la simplification de ces expressions.
Tout d'abord
J'utilise Maple pour générer le code C ++ afin d'éviter les erreurs.
Maple et Mathematica passent parfois à côté de l'évidence. Plus important encore, les utilisateurs de Maple et Mathematica font parfois des erreurs. Substituer «souvent», ou peut-être même «presque toujours», au lieu de «parfois est probablement plus proche de la cible.
Vous auriez pu aider Maple à simplifier cette expression en lui indiquant les paramètres en question. Dans l'exemple en question, je soupçonne que l1
, l2
et l3
sont des nombres réels positifs et c'est a
un nombre réel différent de zéro. Si c'est le cas, dites-le-lui. Ces programmes de mathématiques symboliques supposent généralement que les quantités disponibles sont complexes. La restriction du domaine permet au programme de faire des hypothèses qui ne sont pas valides dans les nombres complexes.
Comment simplifier ces gros dégâts à partir de programmes de mathématiques symboliques (cette modification)
Les programmes de mathématiques symboliques permettent généralement de fournir des informations sur les différents paramètres. Utilisez cette capacité, en particulier si votre problème implique une division ou une exponentiation. Dans l'exemple à portée de main, vous auriez pu Maple simplifier cette expression en lui disant que l1
, l2
et l3
sont des nombres réels positifs et a
est un nombre réel non nul. Si c'est le cas, dites-le-lui. Ces programmes de mathématiques symboliques supposent généralement que les quantités disponibles sont complexes. Restreindre le domaine permet au programme de faire des hypothèses telles que a x b x = (ab) x . Ce n'est que si a
et b
sont des nombres réels positifs et si x
est réel. Ce n'est pas valable dans les nombres complexes.
En fin de compte, ces programmes mathématiques symboliques suivent des algorithmes. Aidez-le. Essayez de jouer avec l'expansion, la collecte et la simplification avant de générer du code. Dans ce cas, vous auriez pu collecter les termes impliquant un facteur de mu
et ceux impliquant un facteur de K
. Réduire une expression à sa «forme la plus simple» reste un peu un art.
Lorsque vous obtenez un horrible désordre de code généré, ne l'acceptez pas comme une vérité à ne pas toucher. Essayez de le simplifier vous-même. Regardez ce que le programme mathématique symbolique avait avant de générer du code. Regardez comment j'ai réduit votre expression à quelque chose de beaucoup plus simple et beaucoup plus rapide, et comment la réponse de Walter a poussé la mienne plusieurs pas plus loin. Il n'y a pas de recette magique. S'il y avait une recette magique, Maple l'aurait appliquée et aurait donné la réponse que Walter a donnée.
À propos de la question spécifique
Vous faites beaucoup d'addition et de soustraction dans ce calcul. Vous pouvez avoir de graves problèmes si vous avez des termes qui s'annulent presque mutuellement. Vous gaspillez beaucoup de CPU si vous avez un terme qui domine les autres.
Ensuite, vous gaspillez beaucoup de CPU en effectuant des calculs répétés. Sauf si vous avez activé -ffast-math
, ce qui permet au compilateur de briser certaines des règles de la virgule flottante IEEE, le compilateur ne simplifiera pas (en fait, ne doit pas) simplifier cette expression pour vous. Il fera plutôt exactement ce que vous lui avez dit de faire. Au minimum, vous devez calculer l1 * l2 * l3
avant de calculer ce désordre.
Enfin, vous passez beaucoup d'appels vers pow
, ce qui est extrêmement lent. Notez que plusieurs de ces appels sont de la forme (l1 * l2 * l3) (1/3) . Beaucoup de ces appels à pow
pourraient être effectués avec un seul appel à std::cbrt
:
l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;
Avec ça,
X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)
devient X * l123_pow_1_3
.
X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
devient X / l123_pow_1_3
.
X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)
devient X * l123_pow_4_3
.
X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)
devient X / l123_pow_4_3
.
Maple a raté l'évidence.
Par exemple, il existe un moyen beaucoup plus simple d'écrire
(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
En supposant que l1
, l2
et l3
sont des nombres réels plutôt que des nombres complexes, et que la racine cubique réelle (plutôt que la racine complexe principale) doit être extraite, ce qui précède se réduit à
2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))
ou
2.0/(3.0 * l123_pow_1_3)
En utilisant cbrt_l123
au lieu de l123_pow_1_3
, l'expression désagréable de la question se réduit à
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);
Vérifiez toujours, mais simplifiez toujours aussi.
Voici quelques-unes de mes étapes pour arriver à ce qui précède:
// 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);
Mauvaise réponse, intentionnellement gardée pour l'humilité
Notez que c'est frappé. C'est faux.
Mettre à jour
Maple a raté l'évidence. Par exemple, il existe un moyen beaucoup plus simple d'écrire
(pow (l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
En supposant que l1
, l2
et l3
sont des nombres réels plutôt que des nombres complexes, et que la racine cubique réelle (plutôt que la racine complexe principale) doit être extraite, ce qui précède se réduit à zéro. Ce calcul de zéro est répété plusieurs fois.
Deuxième mise à jour
Si j'ai bien fait le calcul (rien ne garantit que j'ai bien fait le calcul), la mauvaise expression de la question se réduit à
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);
Ce qui précède suppose que l1
, l2
et l3
sont des nombres réels positifs.
pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
par une variable ... Vous devez cependant comparer votre code pour être sûr qu'il fonctionne rapidement ou lentement.