Edytuj podsumowanie
- W mojej pierwotnej odpowiedzi zauważyłem jedynie, że kod zawiera wiele powielonych obliczeń i że wiele z potęg obejmuje czynniki 1/3. Na przykład
pow(x, 0.1e1/0.3e1)
to to samo, co cbrt(x)
.
- Moja druga edycja była po prostu błędna, a moja trzecia ekstrapolowana na ten błąd. To właśnie sprawia, że ludzie boją się zmieniać wyniki podobne do wyroczni z symbolicznych programów matematycznych, które zaczynają się na literę „M”. Skreśliłem (tj.
Wykreśliłem ) te zmiany i umieściłem je na dole aktualnej wersji tej odpowiedzi. Jednak ich nie usunąłem. Jestem człowiekiem. Łatwo nam popełnić błąd.
- Moja czwarta edycja opracowali bardzo zwartą wyrażenie poprawnie reprezentuje zawiłe wyraz w pytaniu IF parametry
l1
, l2
i l3
są dodatnimi liczbami rzeczywistymi, a jeśli a
jest niezerowe liczby rzeczywiste. (Nie otrzymaliśmy jeszcze informacji z PO na temat specyfiki tych współczynników. Biorąc pod uwagę naturę problemu, są to rozsądne założenia).
- Ta edycja próbuje odpowiedzieć na ogólny problem, jak uprościć te wyrażenia.
Po pierwsze
Używam Maple do generowania kodu C ++, aby uniknąć błędów.
Maple i Mathematica czasami pomijają oczywistość. Co ważniejsze, użytkownicy Maple i Mathematica czasami popełniają błędy. Zastępowanie „często”, a może nawet „prawie zawsze” zamiast „czasami jest prawdopodobnie bliżej celu.
Mogłeś pomóc Maple uprościć to wyrażenie, mówiąc mu o danych parametrach. W omawianym przykładzie podejrzewam, że l1
, l2
i l3
są dodatnimi liczbami rzeczywistymi, a to a
jest niezerową liczbą rzeczywistą. Jeśli tak jest, powiedz to. Te symboliczne programy matematyczne zazwyczaj zakładają, że dostępne ilości są złożone. Ograniczenie domeny pozwala programowi przyjmować założenia, które nie są poprawne w liczbach zespolonych.
Jak uprościć te duże bałagania z symbolicznych programów matematycznych (ta edycja)
Programy do matematyki symbolicznej zazwyczaj zapewniają możliwość dostarczania informacji o różnych parametrach. Użyj tej zdolności, szczególnie jeśli twój problem obejmuje dzielenie lub potęgowanie. Na przykład pod ręką, można pomogły Klon uprościć że ekspresja informując go, że l1
, l2
i l3
są liczbami rzeczywistymi dodatnimi i że a
jest niezerowe liczby rzeczywiste. Jeśli tak jest, powiedz to. Te symboliczne programy matematyczne zazwyczaj zakładają, że dostępne ilości są złożone. Ograniczenie domeny pozwala programowi przyjmować takie założenia, jak a x b x = (ab) x . To jest tylko wtedy, gdy a
i b
są dodatnimi liczbami rzeczywistymi, a jeśli x
jest prawdziwe. Nie dotyczy liczb zespolonych.
Ostatecznie te symboliczne programy matematyczne są zgodne z algorytmami. Pomóż temu. Spróbuj bawić się rozwijaniem, gromadzeniem i upraszczaniem, zanim wygenerujesz kod. W tym przypadku mógłbyś zebrać te terminy, które zawierają czynnik, mu
a te z czynnikiem K
. Sprowadzenie wypowiedzi do „najprostszej formy” pozostaje sztuką.
Kiedy otrzymujesz brzydki bałagan wygenerowanego kodu, nie akceptuj tego jako prawdy, której nie możesz dotykać. Spróbuj sam to uprościć. Spójrz, co miał symboliczny program matematyczny, zanim wygenerował kod. Spójrz, jak zredukowałem twój wyraz twarzy do czegoś znacznie prostszego i znacznie szybszego i jak odpowiedź Waltera posunęła moją o kilka kroków dalej. Nie ma magicznego przepisu. Gdyby istniał magiczny przepis, Maple zastosowałby go i udzielił odpowiedzi, której udzielił Walter.
O konkretnym pytaniu
Robisz dużo dodawania i odejmowania w tych obliczeniach. Możesz wpaść w poważne kłopoty, jeśli masz warunki, które prawie się znoszą. Marnujesz dużo procesora, jeśli masz jeden termin, który dominuje nad innymi.
Następnie marnujesz dużo procesora, wykonując powtarzające się obliczenia. O ile nie włączyłeś tej opcji -ffast-math
, która pozwala kompilatorowi złamać niektóre reguły zmiennoprzecinkowe IEEE, kompilator nie będzie (w rzeczywistości nie może) upraszczać tego wyrażenia za Ciebie. Zamiast tego zrobi dokładnie to, co mu kazałeś. Jako minimum powinieneś obliczyć l1 * l2 * l3
przed obliczeniem tego bałaganu.
Wreszcie wykonujesz wiele połączeń pow
, co jest bardzo powolne. Zauważ, że kilka z tych wywołań ma postać (l1 * l2 * l3) (1/3) . Wiele z tych wywołań pow
można wykonać jednym wywołaniem std::cbrt
:
l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;
Z tym,
X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)
staje się X * l123_pow_1_3
.
X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
staje się X / l123_pow_1_3
.
X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)
staje się X * l123_pow_4_3
.
X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)
staje się X / l123_pow_4_3
.
Maple przegapił oczywistość.
Na przykład istnieje znacznie łatwiejszy sposób pisania
(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Zakładając, że l1
, l2
il3
są prawdziwe zamiast liczb zespolonych, a korzeniem rzeczywistego modułu (zamiast zasady kompleks korzeniowego) do ekstrakcji, powyżej redukuje się do
2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))
lub
2.0/(3.0 * l123_pow_1_3)
Używanie cbrt_l123
zamiast l123_pow_1_3
paskudnego wyrażenia w pytaniu sprowadza się do
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);
Zawsze sprawdzaj dokładnie, ale zawsze upraszczaj.
Oto niektóre z moich kroków prowadzących do powyższego:
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;
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;
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;
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;
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;
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;
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);
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);
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);
Zła odpowiedź, celowo zachowana dla pokory
Zauważ, że to jest dotknięte. To jest źle.
Aktualizacja
Maple przegapił oczywistość. Na przykład istnieje znacznie łatwiejszy sposób pisania
(pow (l1 * l2 * l3, -0,1e1 / 0,3e1) - l1 * l2 * l3 * pow (l1 * l2 * l3, -0,4e1 / 0,3e1) / 0,3e1)
Przy założeniu l1
, l2
i l3
są prawdziwe zamiast liczb zespolonych, a rzeczywista modułu głównego (zamiast zasady kompleks korzeniowego) do ekstrakcji, powyżej redukuje się do zera. To obliczenie zera powtarza się wielokrotnie.
Druga aktualizacja
Jeśli poprawnie wykonałem matematykę (nie ma gwarancji, że poprawnie wykonałem obliczenia), nieprzyjemne wyrażenie w pytaniu sprowadza się do
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);
Powyższe zakłada, że l1
, l2
i l3
są dodatnimi liczbami rzeczywistymi.
pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
zmienną ... Musisz jednak przetestować swój kod, aby upewnić się, czy działa szybko, czy wolno.