Modifica riepilogo
- La mia risposta originale notava semplicemente che il codice conteneva molti calcoli replicati e che molte delle potenze coinvolgevano fattori di 1/3. Ad esempio,
pow(x, 0.1e1/0.3e1)è lo stesso di cbrt(x).
- La mia seconda modifica era semplicemente sbagliata e la mia terza estrapolata su questa inesattezza. Questo è ciò che fa paura alle persone di cambiare i risultati simili a quelli di un oracolo da programmi di matematica simbolici che iniziano con la lettera "M". Ho cancellato (cioè
barrato ) quelle modifiche e le ho spinte alla fine dell'attuale revisione di questa risposta. Tuttavia, non li ho eliminati. Sono umano. È facile per noi sbagliare.
- La quarta modifica sviluppato un'espressione molto compatta che rappresenta correttamente l'espressione contorta nell'interrogazione IF i parametri
l1, l2e l3sono numeri reali positivi e se aè un numero reale diverso da zero. (Dobbiamo ancora sentire dal PO in merito alla natura specifica di questi coefficienti. Data la natura del problema, queste sono ipotesi ragionevoli.)
- Questa modifica tenta di rispondere al problema generico di come semplificare queste espressioni.
Cominciando dall'inizio
Uso Maple per generare il codice C ++ per evitare errori.
Maple e Mathematica a volte perdono l'ovvio. Ancora più importante, gli utenti di Maple e Mathematica a volte commettono errori. Sostituire "spesso", o forse anche "quasi sempre", al posto di "a volte è probabilmente più vicino al segno.
Avresti potuto aiutare Maple a semplificare quell'espressione raccontandogli i parametri in questione. Nell'esempio a portata di mano, sospetto che l1, l2e l3siano numeri reali positivi e che asia un numero reale diverso da zero. Se è così, diglielo. Quei programmi di matematica simbolici in genere presumono che le quantità a portata di mano siano complesse. La limitazione del dominio consente al programma di formulare ipotesi che non sono valide nei numeri complessi.
Come semplificare quei grandi pasticci dai programmi di matematica simbolica (questa modifica)
I programmi di matematica simbolica in genere forniscono la capacità di fornire informazioni sui vari parametri. Usa questa capacità, soprattutto se il tuo problema riguarda la divisione o l'elevazione a potenza. Nell'esempio a portata di mano, si potrebbe avere contribuito a Maple semplificare questa espressione dicendo che essa l1, l2e l3sono numeri reali positivi e che aè un numero reale diverso da zero. Se è così, diglielo. Quei programmi di matematica simbolici in genere presumono che le quantità a portata di mano siano complesse. La limitazione del dominio consente al programma di fare ipotesi come a x b x = (ab) x . Questo è solo se ae bsono numeri reali positivi e se xè reale. Non è valido nei numeri complessi.
In definitiva, quei programmi matematici simbolici seguono algoritmi. Aiutalo. Prova a giocare con l'espansione, la raccolta e la semplificazione prima di generare il codice. In questo caso, avresti potuto raccogliere quei termini che coinvolgono un fattore di mue quelli che coinvolgono un fattore di K. Ridurre un'espressione alla sua "forma più semplice" rimane un po 'un'arte.
Quando ottieni un brutto pasticcio di codice generato, non accettarlo come una verità che non devi toccare. Prova a semplificarlo da solo. Guarda cosa aveva il programma di matematica simbolica prima di generare il codice. Guarda come ho ridotto la tua espressione a qualcosa di molto più semplice e molto più veloce, e come la risposta di Walter ha portato la mia diversi passi avanti. Non esiste una ricetta magica. Se ci fosse stata una ricetta magica, Maple l'avrebbe applicata e avrebbe dato la risposta data da Walter.
Sulla domanda specifica
Stai facendo molte addizioni e sottrazioni in quel calcolo. Puoi finire nei guai se hai termini che quasi si annullano a vicenda. Stai sprecando molta CPU se hai un termine che domina sugli altri.
Successivamente, stai sprecando molta CPU eseguendo calcoli ripetuti. A meno che tu non abbia abilitato -ffast-math, che consente al compilatore di infrangere alcune delle regole del punto mobile IEEE, il compilatore non semplificherà (in effetti, non deve) quell'espressione per te. Invece farà esattamente quello che gli hai detto di fare. Come minimo, dovresti calcolare l1 * l2 * l3prima di calcolare quel casino.
Infine, stai facendo molte chiamate a pow, il che è estremamente lento. Notare che molte di queste chiamate sono nel formato (l1 * l2 * l3) (1/3) . Molte di queste chiamate a powpotrebbero essere eseguite con una singola chiamata a std::cbrt:
l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;
Con questo,
X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)diventa X * l123_pow_1_3.
X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)diventa X / l123_pow_1_3.
X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)diventa X * l123_pow_4_3.
X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)diventa X / l123_pow_4_3.
Maple ha mancato l'ovvio.
Ad esempio, c'è un modo molto più semplice per scrivere
(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Supponendo che l1, l2e l3siano numeri reali piuttosto che complessi, e che si debba estrarre la radice cubica reale (piuttosto che la radice complessa principale), quanto sopra si riduce a
2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))
o
2.0/(3.0 * l123_pow_1_3)
Usando cbrt_l123invece di l123_pow_1_3, l'espressione sgradevole nella domanda si riduce 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);
Ricontrolla sempre, ma semplifica sempre.
Ecco alcuni dei miei passi per arrivare a quanto sopra:
// 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);
Risposta sbagliata, mantenuta intenzionalmente per umiltà
Notare che questo è colpito. È sbagliato.
Aggiornare
Maple ha mancato l'ovvio. Ad esempio, c'è un modo molto più semplice per scrivere
(pow (l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Supponendo che l1, l2e l3siano numeri reali piuttosto che complessi e che si debba estrarre la radice cubica reale (piuttosto che la radice complessa principale), quanto sopra si riduce a zero. Questo calcolo dello zero viene ripetuto molte volte.
Secondo aggiornamento
Se ho fatto il diritto di matematica (non v'è alcuna garanzia che ho fatto il diritto di matematica), la brutta espressione nella questione si riduce 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);
Quanto sopra presuppone che l1, l2e l3siano numeri reali positivi.
pow(l1 * l2 * l3, -0.1e1 / 0.3e1)con una variabile ... Tuttavia, è necessario eseguire un benchmark del codice per essere sicuri che funzioni velocemente o lentamente.