matematykaszkolna.pl
Ortogonalizacja Mariusz: #include <iostream> #include <vector> #include <gmpxx.h> #include "Polynomial.h" mpqclass InnerProductT(const Polynomial<mpqclass>& P, const Polynomial<mpqclass>& Q) { Polynomial<mpqclass> C = P * Q; mpqclass s = 0; mpqclass p = 1; for(int k = 0; k <= C.degree(); k += 2) { s += p * C[k]; p *= (mpqclass)(k+1)/(k+2); } return s; } mpqclass InnerProductU(const Polynomial<mpqclass>& P, const Polynomial<mpqclass>& Q) { Polynomial<mpqclass> C = P * Q * Polynomial<mpqclass>({1, 0, −1}); mpqclass s = 0; mpqclass p = 1; for(int k = 0; k <= C.degree(); k += 2) { s += p * C[k]; p *= (mpqclass)(k+1)/(k+2); } return s; } mpqclass InnerProductP(const Polynomial<mpqclass>& P, const Polynomial<mpqclass>& Q) { Polynomial<mpqclass> C = P * Q; mpqclass s = 0; for(int k = 0;k <= C.degree();k += 2) { s += C[k]*(mpqclass)2/(k+1); } return s; } mpqclass InnerProductH(const Polynomial<mpqclass>& P, const Polynomial<mpqclass>& Q) { Polynomial<mpqclass> C = P * Q; mpqclass s = 0; mpqclass p = 1; for(int k = 0; k <= C.degree(); k += 2) { s += p * C[k]; p *= (mpqclass)(k+1)/2; } return s; } mpqclass InnerProductL(const Polynomial<mpqclass>& P, const Polynomial<mpqclass>& Q) { Polynomial<mpqclass> C = P * Q; mpqclass s = 0; mpqclass p = 1; for(int k = 0; k <= C.degree(); k++) { s += p * C[k]; p *= (mpqclass)(k+1); } return s; } void ModifiedGramSchmidt(int n, std::vector<Polynomial<mpqclass>>& Q, mpqclass (*InnerProduct)(const Polynomial<mpqclass>&, const Polynomial<mpqclass>&)) { Polynomial<mpqclass> Qj, Qk; mpqclass invnorm, w; Q.resize(n+1); for(int i = 0; i <= n; i++) { Q[i] = Polynomial<mpqclass>::monomial(i); } for(int j = 0; j <= n; j++) { Qj = Q[j]; invnorm = (mpqclass)1 / InnerProduct(Qj, Qj); for(int k = j + 1; k <= n; k++) { Qk = Q[k]; w = invnorm * InnerProduct(Qj, Qk); Q[k] −= Polynomial<mpqclass>(w * Q[j]); } } } void GramSchmidt(int n, std::vector<Polynomial<mpqclass>>& Q, mpqclass (*InnerProduct)(const Polynomial<mpqclass>&, const Polynomial<mpqclass>&)) { mpqclass mu; Polynomial<mpqclass> Qi, Qj; Q.resize(n+1); for(int i = 0; i <= n; i++) { Q[i] = Polynomial<mpqclass>::monomial(i); } for(int i = 0; i <= n; i++) { Qi = Q[i]; for(int j = 0; j <= i − 1; j++) { Qj = Q[j]; mu = InnerProduct(Qi, Qj) / InnerProduct(Qj, Qj); Qi −= Polynomial<mpqclass>(mu * Qj); } Q[i] = Qi; } } int main() { int n; std::cout << "Podaj n="; std::cin >> n; // Pierwszy rodzaj wielomianów Czebyszewa (T) std::vector<Polynomial<mpqclass>> QT(n+1); GramSchmidt(n, QT, InnerProductT); // Normalizacja − suma współczynników = 1 for(int i = 0; i <= n; i++) { mpqclass s = 0; for(int j = 0; j <= i; j++) { s += QT[i][j]; } QT[i] *= (mpqclass)1/s; } std::cout << "Wielomiany Czebyszewa pierwszego rodzaju (T):\n"; for(const auto& poly : QT) { std::cout << poly << std::endl; } // Drugi rodzaj wielomianów Czebyszewa (U) std::vector<Polynomial<mpqclass>> QU(n+1); GramSchmidt(n, QU, InnerProductU); // Mnożenie przez potęgi 2 mpqclass p2 = 1; std::cout << "\nWielomiany Czebyszewa drugiego rodzaju (U):\n"; for(int i = 0; i <= n; i++) { QU[i] *= p2; std::cout << QU[i] << std::endl; p2 *= 2; } std::vector<Polynomial<mpqclass>> QP(n+1); GramSchmidt(n, QP, InnerProductP); // Normalizacja − suma współczynników = 1 for(int i = 0; i <= n; i++) { mpqclass s = 0; for(int j = 0; j <= i; j++) { s += QP[i][j]; } QP[i] *= (mpqclass)1/s; } std::cout<<std::endl; std::cout << "Wielomiany Legendre'a (P):\n"; for(const auto& poly : QP) { std::cout << poly << std::endl; } std::vector<Polynomial<mpqclass>> QH(n+1); GramSchmidt(n, QH, InnerProductH); // Mnożenie przez potęgi 2 p2 = 1; std::cout<<std::endl; std::cout << "\nWielomiany Hermite'a (H):\n"; for(int i = 0; i <= n; i++) { QH[i] *= p2; std::cout << QH[i] << std::endl; p2 *= 2; } std::cout<<std::endl; std::vector<Polynomial<mpqclass>> QL(n+1); GramSchmidt(n, QL, InnerProductL); mpqclass tmp; std::cout<<std::endl; std::cout << "\nWielomiany Laguerre'a (L):\n"; for(int i = 0; i <= n; i++) { tmp = 1/QL[i][0]; QL[i] *= tmp; std::cout << QL[i] << std::endl; } system("PAUSE"); return 0; } Aby programik zadziałał musicie mieć gmp (Dla Windows wystarczą pliki dll oraz pliki nagłówkowe) oraz musicie napisać własną szablonową klasę wielomianów Użyłem tutaj (zmodyfikowanej) ortogonalizacji Grama−Schmidta Jeśli chodzi o iloczyny skalarne to: Dla wielomianów T Czebyszowa iloczyn skalarny zdefiniowany jest następująco
 p(x)q(x) 
<p(x),q(x)> = ∫−11

dx
 1−x2 
Podczas pisania funkcji obliczającej iloczyn skalarny skorzystałem ze
 xn 
wzoru rekurencyjnego na całkę ∫−11

dx
 1−x2 
Aby otrzymać wielomiany T Czebyszowa trzeba jeszcze skorzystać z warunku Tn(1) = 1 Dla wielomianów U Czebyszowa iloczyn skalarny zdefiniowany jest następująco <p(x),q(x)> = ∫−11p(x)q(x)1−x2dx Podczas pisania funkcji obliczającej iloczyn skalarny skorzystałem z tego że
 1−x2 
1−x2 =

(stąd ten dodatkowy czynnik (1−x2))
 1−x2 
oraz z wcześniej wyprowadzonego wzoru redukcyjnego Aby otrzymać wielomiany U Czebyszowa trzeba jeszcze skorzystać z warunku że współczynnik wiodący wynosi 2n Dla wielomianów Legendre'a iloczyn skalarny zdefiniowany jest następująco iloczyn skalarny zdefiniowany jest następująco <p(x),q(x)> = ∫−11p(x)q(x)dx i łatwo napisać bez wzoru redukcyjnego Aby otrzymać wielomiany Legendre'a trzeba jeszcze skorzystać z warunku Tn(1) = 1 Dla wielomianów Hermite'a iloczyn skalarny zdefiniowany jest następująco <p(x),q(x)> = ∫p(x)q(x)e−x2dx Podczas pisania funkcji obliczającej iloczyn skalarny można skorzystać ze wzoru rekurencyjnego na całkę ∫xne−x2dx Aby otrzymać wielomiany Hermite'a trzeba jeszcze skorzystać z warunku że współczynnik wiodący wynosi 2n Do tej pory w funkcjach obliczających iloczyn skalarny zaczynałem pętle od zera i w każdej iteracji zwiększałem licznik o dwa bo funkcja podcałkowa dla nieparzystego n była nieparzysta a przedział całkowania był symetryczny względem zera Dla wielomianów Laguerre'a iloczyn skalarny zdefiniowany jest następująco <p(x),q(x)> = ∫0p(x)q(x)e−xdx Podczas pisania funkcji obliczającej iloczyn skalarny można skorzystać ze wzoru rekurencyjnego na całkę ∫0xne−xdx Aby otrzymać wielomiany Laguerre'a trzeba uwzględnić jeszcze to że Ln(0) = 1 Tutaj przy obliczaniu iloczynu skalarnego zacząłem pętle od zera i w każdej iteracji zwiększałem licznik o jeden Podczas obliczania iloczynu skalarnego najbardziej kosztowny jest iloczyn wielomianów (Mnożenie znane ze szkoły wymaga O(n2) operacji choć można ten koszt zmniejszyć) W ortogonalizacji Grama−Schmidta mamy dwie zagnieżdżone pętle napisane w ten sposób że operacje o koszcie O(1) dałyby w sumie złożoność O(n2) ale w tych zagnieżdżonych pętlach mamy mnożenie wielomianów co daje nam złożoność czasową O(n4) Jeżeli chodzi o złożoność pamięciową to mamy tutaj n wielomianów z których każdy ma swój wektor współczynników co daje O(n2) Gdy próbowałem się bawić i obliczać te wielomiany w Paincie to już dla n = 6 zajmowało to dość dużo czasu
10 kwi 01:10