В общем понадобилось мне тут получить интерполяционный комплексный полином по большому количеству точек. Написал программу, и всё бы хорошо, но чем больше точек, тем больше погрешность вычисления (сравнил значение полинома в данных точках с заданным). На сколько я понимаю, существуют методы компенсации погрешности, но в гугле я нашёл только это и это. Попробовал применить эти методы в моём случае, но получилась джигурда. Что можно сделать в моём случае? (длинную арифметику не предлагать, она очень медленная). Собственно код: Код (C): #define FloatType float//double struct COMPLEX { FloatType Re; FloatType Im; }; struct COMPLEX_POINT { COMPLEX x; COMPLEX y; }; void AddComplex(COMPLEX* Dst, COMPLEX* a, COMPLEX* b) { Dst->Re = a->Re + b->Re; Dst->Im = a->Im + b->Im; } void SubComplex(COMPLEX* Dst, COMPLEX* Minuent, COMPLEX* Subtrahend) { Dst->Re = Minuent->Re - Subtrahend->Re; Dst->Im = Minuent->Im - Subtrahend->Im; } void MultComplex(COMPLEX* Dst, COMPLEX* a, COMPLEX* b) { FloatType Re; Re = a->Re*b->Re - a->Im*b->Im; Dst->Im = a->Re*b->Im + a->Im*b->Re; Dst->Re = Re; } void DivComplex(COMPLEX* Dst, COMPLEX* Num, COMPLEX* Denom) { COMPLEX s; s.Re = Denom->Re; s.Im = -Denom->Im; FloatType d = Denom->Re*Denom->Re + Denom->Im*Denom->Im; MultComplex(Dst, Num, &s); Dst->Re /= d; Dst->Im /= d; } void IntPowComplex(COMPLEX* Dst, COMPLEX* Base, int Exponent) { Dst->Re = 1; Dst->Im = 0; if (Exponent != 0) { for (unsigned int i = 0; i < abs(Exponent); i++) { MultComplex(Dst, Dst, Base); } if (Exponent < 0) { COMPLEX tmp; tmp.Re = 1; tmp.Im = 0; DivComplex(Dst, &tmp, Dst); } } } void GetM(COMPLEX_POINT Points[], unsigned int StartIndex, unsigned int MiddleIndex, unsigned int EndIndex, COMPLEX* M) { COMPLEX tmp; M->Re = 1; M->Im = 0; if (MiddleIndex > StartIndex) { for (unsigned int i = StartIndex; i < MiddleIndex; i++) { SubComplex(&tmp, &Points[i].x, &Points[MiddleIndex].x); MultComplex(M, M, &tmp); } } if (MiddleIndex < EndIndex) { for (unsigned int i = MiddleIndex + 1; i <= EndIndex; i++) { SubComplex(&tmp, &Points[MiddleIndex].x, &Points[i].x); MultComplex(M, M, &tmp); } } } void GetCoef(COMPLEX_POINT Points[], unsigned int NumOfPoints, COMPLEX LocArr[], unsigned int index, COMPLEX* Result) { Result->Re = 0; Result->Im = 0; for (unsigned int i = 0; i <= index; i++) { COMPLEX M, tmp; GetM(Points, 0, i, index, &M); if (i % 2 == 1) { tmp.Re = -LocArr[i].Re; tmp.Im = -LocArr[i].Im; } else { tmp.Re = LocArr[i].Re; tmp.Im = LocArr[i].Im; } DivComplex(&tmp, &tmp, &M); AddComplex(Result, Result, &tmp); } } void Interpoly(COMPLEX_POINT Points[], unsigned int NumOfPoints, COMPLEX Poly[]) { if (NumOfPoints > 0) { COMPLEX* LocArr = (COMPLEX*)malloc(sizeof(COMPLEX)*NumOfPoints); for (unsigned int i = 0; i < NumOfPoints; i++) { LocArr[i] = Points[i].y; } for (unsigned int i = NumOfPoints; i>0; i--) { GetCoef(Points, NumOfPoints, LocArr, i - 1, &Poly[i - 1]); for (unsigned int j = 0; j < i; j++) { COMPLEX tmp; IntPowComplex(&tmp, &Points[j].x, i - 1); MultComplex(&tmp, &tmp, &Poly[i - 1]); SubComplex(&LocArr[j], &LocArr[j], &tmp); } } free(LocArr); } } Многочлен строит функция Interpoly, остальные - вспомогательные подфункции.
_qwe8013 https://duckduckgo.com/?q=interpolation+complex+polynom&atb=v12&ia=web пробегись по ссылям -- думаю, всё найдёшь вплоть до уже готовых решений. по идее, в matlab/maple/mathematica -- все эти фичи ужо забиты, тч материала для чтения/тестов/использования хватает.
Сначала проверяйте SSE поддержку, интел маны в кучу Код (Text): int isCPUIDsupported (void) { __asm { push ecx ; save ecx pushfd ; push original EFLAGS pop eax ; get original EFLAGS mov ecx, eax ; save original EFLAGS xor eax, 200000h ; flip bit 21 in EFLAGS push eax ; save new EFLAGS value on stack popfd ; replace current EFLAGS value pushfd ; get new EFLAGS pop eax ; store new EFLAGS in EAX xor eax, ecx ; Bit 21 of flags at 200000h will be 1 if CPUID exists shr eax, 21 ; Shift bit 21 bit 0 and return it push ecx popfd ; restore bit 21 in EFLAGS first pop ecx ; restore ecx } } __asm { _emit 0x0F _emit 0x1F _emit 0x00 _emit 0x0F _emit 0x1F _emit 0x00 mov eax, 0x1 _emit 0x0F _emit 0x1F _emit 0x00 cpuid _emit 0x0F _emit 0x1F _emit 0x00 test edx, 1<<25 _emit 0x0F _emit 0x1F _emit 0x00 jnz _SSE _emit 0x0F _emit 0x1F _emit 0x00 int 3 call ExitProcess _emit 0x0F _emit 0x1F _emit 0x00 _SSE: _emit 0x0F _emit 0x1F _emit 0x00 _emit 0x0F _emit 0x1F _emit 0x00 _emit 0x0F _emit 0x1F _emit 0x00 _emit 0x0F _emit 0x1F _emit 0x00 } Сорри я в кучу написал - кому надо - поймет, это выдержка из моего криптора
Не сильно вдавался в алгоритм (не сформулированы исходные данные и способ решения), но при беглом осмотре кода мне тут кажется, сам алгоритм страдает. Зачем возводить комплексные числа в степень, если можно поделить исходный полином на сопряжённую пару? Кстати говоря, рекомендую интерполяционный полином представлять не в виде a0⋅xn + a1⋅xn-1 + ... + aN⋅x0, а в виде произведения многочленов первой (вещественные корни) либо второй (комплексные корни) степени: (a0⋅x2 + a1⋅x + a2)⋅(b0⋅x2 + b1⋅x + b2)⋅(c0⋅x + c1) + ... Какая отсюда выгода: 1. каждый многочлен в произведении можно нормировать, вынеся a0, b0, c0 за скобки → имеем прирост в точности при вычислении каждого многочлена 2. при вычислении квадратного многочлена нет большой потери точности 3. квадратные многочлены вычисляются быстро, при правильной организации цикла можно даже не вычислять каждый раз значения x и x2, а, вычислив один раз, просто подставлять коэффициенты следующего многочлена на следующей итерации.
SadKo, любая методика тихо-мирно упирается в битность числа.. во многих случаях без длинной арифы совсем никак. а методы представления полинома не столько про "контроль погрешности", сколько про "скорость алгоса".
Я с вами не согласен, потому что совсем недавно (где-то полгода назад) приходилось решать подобную задачу. Нужно было проектировать цифровые БИХ-фильтры 32-го порядка и рисовать их АЧХ. Не поверите, но задачу решил спокойно на самых обычных float'ах.
Не факт. Важно правильный алгоритм выбрать. Так, оптимизируя каскады biquad-фильтров и применяя различные техники оптимизации вплоть до SSE- и AVX-ассемблирования, я получил где-то в 10 раз более быструю реализацию фильтров по сравнению с начальной. Аналогичное можно сказать про FFT: самая тупая реализация свёртки с использованием самой тупой реализации алгоритма FFT вносит меньше ошибок в результат, нежели расчёт свёртки "в лоб". Кстати, интересная вещь: в DSP фильтры конструируют из biquad-каскадов (полином 2 степени в числителе и полином 2 степени в знаменателе). Это объясняется тем, что при большем порядке полинома, чем 2, фильтр резко теряет устойчивость. Все мои попытки сделать фильтр на полиномах четвёртой степени потерпели фиаско: при определённых условиях фильтр просто терял устойчивость. Зато цепочка biquad-фильтров работает на ура. Стандартные complex float/double плохо оптимизируются и векторизуются. Проще вообще отказаться от комплексных чисел и вывести формулы для real и imaginary частей.
Ну а сейчас как вы интерполяционный полином получаете? Рассказали бы хоть алгоритм, чтобы долго код не ковырять и не гадать, что там происходит. Если составляете систему уравнений, то тут нужно подумать, как минимизировать возведение в степень N комплексного числа. К тому же, лучше при возведении, наверное, пользоваться полярной формой: (A ⋅ ejP)n = An ⋅ ejPn Это, по идее, может позволить вынести мусор за скобки и при возможности сократить с нулём или оставить для конечных вычислений.
_qwe8013, Abramowitz and Stegun: Handbook of Mathematical Functions Там много коэффициентов для подобных фунок.
Составляю систему уравнений с матрицей Вандермонда. Не хотел использовать лишний раз иррациональные функции, хотя возможно вы правы, и лучше в полярной форме.
Так фишка полярной формы, как раз, в том, что у вас целая степень всегда, поэтому exp(j *P) у вас единичный вектор и будет просто вращаться N раз на свой угол поворота против часовой стрелки. Вам даже иррациональные функции (кроме sqrt) применять не придётся. То есть, exp(j*P) можно по-прежнему хранить как пару чисел re + im, но нормированную до единицы по модулю, а сам модуль вынести за скобки.
Мне видится что-то вроде такого: Код (C++): typedef struct complex_t { float a; // Аргумент комплексного числа float re, im; // Единичный нормированный вектор вращения } complex_t; void complex_init(complex_t *c, float re, float im) { c->a = sqrtf(re*re + im*im); if (c->a > 0.0f) { c->re = re / c->a; c->im = im / c->a; } else { c->re = 0.0f; c->im = 0.0f; } } void complex_mul(complex_t *x, complex_t *a, complex_t *b) { x->a = a->a * b->a; float re = a->re*b->re - a->im*b->im; float im = a->re*b->im + a->im*b->re; x->re = re; x->im = im; } void complex_add(complex_t *x, complex_t *a, complex_t *b) { if (a->a <= 0.0f) { *x = *b; return; } else if (b->a <= 0.0f) { *x = *a; return; } // Здесь, на самом деле, хорошо бы заранее вынести общий множитель (скорее всего это будет sqrtf(a->a * b->a)) и преобразовать формулу, чтобы погрешность стала меньше float re = a->re * a->a + b->re * b->a; float im = a->im * a->a + b->im * b->a; float a = sqrtf (re*re + im*im); if (a <= 0.0f) { x->a = 0.0f; x->re = 0.0f; x->im = 0.0f; return; } x->a = a; x->re = re / a; x->im = re / a; } Соответственно, стараемся, чтобы поле a также было невелико, а держалось близко к 1.