Редагувати резюме
- У моїй оригінальній відповіді було лише зазначено, що код містив багато повторень обчислень і що багато з повноважень включали коефіцієнти 1/3. Наприклад,
pow(x, 0.1e1/0.3e1)
це те саме, що cbrt(x)
.
- Моє друге редагування було просто неправильним, а третє - екстраполяцією цієї неправильності. Саме це змушує людей боятися змінювати результати, подібні до оракулів, із символічних математичних програм, які починаються з літери "М". Я вирішив (тобто
наніс ) ці зміни та підсунув їх до кінця поточної редакції цієї відповіді. Однак я їх не видаляв. Я людина. Нам легко помилитися.
- Моє четверте редагування розробило дуже компактний вираз, який правильно відображає звивистий вираз у питанні, якщо параметри
l1
,l2
і l3
позитивні дійсні числа , а якщоa
є ненульовий дійсне число. (Ми все ще не чули від ОП щодо конкретного характеру цих коефіцієнтів. Враховуючи характер проблеми, це обґрунтовані припущення.)
- Ця редакція намагається відповісти на загальну проблему, як спростити ці вирази.
Насамперед
Я використовую Maple для генерації коду C ++, щоб уникнути помилок.
Клен і Математика іноді пропускають очевидне. Ще важливіше те, що користувачі Maple та Mathematica іноді роблять помилки. Заміна "часто", а може, навіть "майже завжди", замість "іноді", напевно, наближається до позначки.
Ви могли б допомогти Maple спростити цей вираз, розповівши про відповідні параметри. У прикладі на руці, я підозрюю , що l1
, l2
і l3
є позитивними дійсними числами , і що a
не є нульовим реальним номером. Якщо це так, скажіть це. Ці символічні математичні програми, як правило, припускають, що наявні величини є складними. Обмеження домену дозволяє програмі робити припущення, які не є дійсними для комплексних чисел.
Як спростити ці великі халепи від символічних математичних програм (це редагування)
Програми символічної математики, як правило, надають можливість надавати інформацію про різні параметри. Використовуйте цю здатність, особливо якщо ваша проблема пов’язана з діленням чи піднесенням до степені. У прикладі , під рукою, ви могли б допомогти Maple спростити цей вираз, кажучи це , що l1
, l2
і l3
є позитивні дійсні числа , і що a
не є нульовий реальний номер. Якщо це так, скажіть це. Ці символічні математичні програми, як правило, припускають, що наявні величини є складними. Обмеження домену дозволяє програмі робити такі припущення, як a x b x = (ab) x . Це лише тоді, коли a
і b
є додатними дійсними числами і якщоx
є дійсними. Він не дійсний у комплексних числах.
Зрештою, ці символічні математичні програми слідують алгоритмам. Допоможіть. Спробуйте пограти в розширення, збір та спрощення, перш ніж створювати код. У цьому випадку ви могли б зібрати такі терміни, що включають коефіцієнт, mu
і такі, що включають коефіцієнтK
. Зведення виразу до його «найпростішої форми» залишається трохи мистецтвом.
Коли ви отримуєте потворний безлад згенерованого коду, не приймайте його як істину, до якої ви не повинні торкатися. Спробуйте спростити це самостійно. Подивіться, що мала символічна математична програма, перш ніж вона генерувала код. Подивіться, як я звів ваш вираз до чогось набагато простішого і набагато швидшого, і як відповідь Вальтера зробила мою ще на кілька кроків. Чарівного рецепту не існує. Якби був чарівний рецепт, Клен застосував би його і дав би відповідь, яку дав Вальтер.
Про конкретне питання
Ви робите багато додавання та віднімання в цьому обчисленні. Ви можете потрапити в глибокі неприємності, якщо у вас є умови, які майже скасовують одне одного. Ви витрачаєте багато CPU, якщо у вас є один термін, який домінує над іншими.
Далі ви витрачаєте багато процесора, виконуючи повторні обчислення. Якщо ви не ввімкнули це -ffast-math
, що дозволяє компілятору порушувати деякі правила IEEE з плаваючою комою, компілятор не буде (насправді, не повинен) спрощувати цей вираз для вас. Натомість воно зробить саме те, що ви йому наказали. Як мінімум, l1 * l2 * l3
перед обчисленням цього безладу слід розрахувати .
Нарешті, ви телефонуєте багато pow
, що надзвичайно повільно. Зверніть увагу, що кілька із цих дзвінків мають вигляд (l1 * l2 * l3) (1/3) . Багато з цих дзвінків pow
можна виконати одним дзвінком std::cbrt
:
l123 = l1 * l2 * l3;
l123_pow_1_3 = std::cbrt(l123);
l123_pow_4_3 = l123 * l123_pow_1_3;
З цим,
X * pow(l1 * l2 * l3, 0.1e1 / 0.3e1)
стає X * l123_pow_1_3
.
X * pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
стає X / l123_pow_1_3
.
X * pow(l1 * l2 * l3, 0.4e1 / 0.3e1)
стає X * l123_pow_4_3
.
X * pow(l1 * l2 * l3, -0.4e1 / 0.3e1)
стає X / l123_pow_4_3
.
Клен справді пропустив очевидне.
Наприклад, є набагато простіший спосіб писати
(pow(l1 * l2 * l3, -0.1e1 / 0.3e1) - l1 * l2 * l3 * pow(l1 * l2 * l3, -0.4e1 / 0.3e1) / 0.3e1)
Якщо припустити , що l1
, l2
і l3
реально , а не комплексні чисел, що корінь реального куба (а не принцип комплексного кореня) повинні бути витягнутий, вище , зводиться до
2.0/(3.0 * pow(l1 * l2 * l3, 1.0/3.0))
або
2.0/(3.0 * l123_pow_1_3)
Використовуючи cbrt_l123
замість l123_pow_1_3
, неприємний вираз у питанні зводиться до
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);
Завжди переглядайте, але завжди також спрощуйте.
Ось декілька моїх кроків для досягнення вищезазначеного:
// 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);
Неправильна відповідь, навмисно дотримана для смирення
Зверніть увагу, що це вражено. Це неправильно.
Оновлення
Клен справді пропустив очевидне. Наприклад, є набагато простіший спосіб писати
(порошок (l1 * l2 * l3, -0,1e1 / 0,3e1) - l1 * l2 * l3 * порошок (l1 * l2 * l3, -0,4e1 / 0,3e1) / 0,3e1)
Якщо припустити , що l1
, l2
іl3
реально , а не комплексні чисел, а також про те , що дійсний корінь куби (а не принцип комплексного корінь) повинні бути витягнутий, вище , зводиться до нуля. Цей розрахунок нуля повторюється багато разів.
Друге оновлення
Якщо я правильно розрахував математику (немає жодної гарантії, що я зробив математику правильно), неприємний вираз у питанні зводиться до
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);
Вище , передбачає , що l1
, l2
і l3
позитивні дійсні числа.
pow(l1 * l2 * l3, -0.1e1 / 0.3e1)
на змінну ... Вам потрібно порівняти свій код, щоб бути впевненим, працює він швидко чи повільно.