|
|||||
Математика: Операции с матрицами.Решение систем с трехдиагональной матрицейCopyright © Nikitine Valeri F. 2000, На этой странице представлены два альтернативных друг другу алгоритма решения линейных систем с трехдиагональной матрицей. Подобные системы возникают при реализации решений параболических или эллиптических уравнений в одномерном случае либо в многомерном при покоординатном расщеплении решения. Первый из этих алгоритмов использует факторизацию системы, называется прогонкой (или алгоритмом Томаса) и входит во все классические учебники по численным методам. Второй алгоритм (редукция) использует последовательное бинарное исключение уравнений из системы; он менее известен и более сложен, однако обладает очень высокой устойчивостью и меньшим накоплением погрешности в процессе счета. Трехдиагональная линейная система уравнений может быть записана в следующем виде: Матрица этой системы состоит из главной диагонали (с коэффициентами -bi) и примыкающих к ней сверху и снизу диагоналей (соответственно ci и ai). Прямое (безытерационное) решение этой системы уравнений можно проводить методом ее факторизации (прогонка, или алгоритм Томаса), либо методом последовательного исключения сначала нечетных, затем делящихся на 2, но не на 4, и т.д. уравнений и соответствующего преобразования коэффициентов. Последний метод называется редукцией или бинарным исключением. Алгоритм ТомасаРешение ищется в виде: Алгоритм принципиально не может иметь ведущего элемента, и есть вероятность того, что он не сойдется даже для несингулярной матрицы. Для этого в программе, реализующей прогонку, необходимо контролировать значения fi. Еще два замечания: длины рекуррентных цепочек порядка N, поэтому при экспоненциальном накоплении погрешностей (что происходит при преобладании значений |beti|>1) прогонка практически обязательно разойдется. Второе: алгоритм сохраняет исходные значения коэффициентов. Доказано, что для устойчивой работы алгоритма Томаса достаточно диагонального преобладания, однако, во многих случаях он сходится и при отсутствии такового. РедукцияОбщее описание алгоритма таково. Формулы для пересчета коэффициентов на каждом этапе исключения таковы: ci = ci+1*C, bi = bi - (ai+1*C + ci-1*A), fi = fi + fi-1*A + fi+1*C; где A=ai/bi-1, C=ci/bi+1 -- естественно, до пересчета. Если число уравнений не является степенью двойки, алгоритм реализуется методом фиктивного восполнения области определения до степени 2. При этом никаких реальных действий, кроме нескольких целочисленных проверок, не производится, а только коэффициенты, индекс которых выпадает из области определения [0,N-1] включительно, считаются равными нулю и в преобразованиях не участвуют (подробности в программе). Особая устойчивость этого алгоритма по сравнению с алгоритмом Томаса связана с тем, что длина рекуррентных цепочек здесь не превышает log2(N)+1, и в связи с этим даже при экспоненциальном росте погрешности исходный результат будет ограниченным. Кроме того, в алгоритме Томаса в рекуррентных цепочках производятся операции между коэффициентами соседних узлов, при редукции на этапах больших начального - между далеко отстоящими, что уменьшает вероятность образования погрешности при вычитании двух близких чисел. Проверено, что алгоритм редукции успешно работает во многих случаях, когда алгоритм Томаса (прогонка) расходится. Алгоритм не требует дополнительной памяти, но сохраняет только половину из имеющихся в начале его работы коэффициентов: те, что находятся на нечетных позициях (независимо от четности N). Скорость работы почти не зависит от того, является ли N-1 целочисленной степенью 2. Ниже приводятся программы для прогонки и редукции. /* Thomas algorithm solving tridiagonal linear system. Description: int Sweep(double *u,double *a,double *b,double *c,double *f, int n,double *bet,double *gam); Parameters: u - solution (size n), on output; a,b,c,f - arrays coefficients (as in the algorithm's description above, size n) n - the solution and coefficients arrays size; bet, gam - additional arrays (size n+1). Returns: 0 - if the algorithm fails, 1 - if OK. Reduction algorithm to solve tridiagonal system. Description: void Reduce(double *u,double *a,double *b,double *c,double *f,int n); Parameters: u - solution (size n), on output; a,b,c,f - arrays coefficients (as in the algorithm's description above, size n) n - the solution and coefficients arrays size; Note: The algorithm is implenmented without singularity checkup for better performance. To apply it, one must include checkup for division by zero any time the division takes place. */ /* Thomas algorithm */ int Sweep(double *u,double *a,double *b,double *c,double *f,int n, double *bet,double *gam) { register int n; double fi; /* calculating the additional arrays (factorization of the system) */ bet[0]=gam[0]=0.; for(i=0;i<n;i++) { fi=b[i]; if(i>0) fi-=a[i]*bet[i]; if(fi==0.) return(0); fi=1./fi; if(i<n-1) bet[i+1]=c[i]*fi; gam[i+1]=(f[i]+a[i]*gam[i])*fi; } /* back substitution */ u[n-1]=gam[n]; for(i=n-1;i>0;i--) u[i-1]=bet[i]*u[i]+gam[i]; return(1); } /* Reduction techniques */ void Reduce(double *u,double *a,double *b,double *c,double *f,int n) { register int i,j; int quarter,half,full; double a1,c1; /* Calculating quarter, central and "right" point numbers */ i=n; full=1; while(i>>=1) full<<=1; if(n>full+1) full<<=1; half=full>>1; quarter=half>>1; /* Forward pass: coefficients recalculation Note: coefficients at odd positions remain intact! */ for(i=1;i<half;i<<=1) { /* leftmost point */ c1=c[0]/b[i]; a[0]=0.; c[0]=c[i]*c1; b[0]-=a[i]*c1; f[0]+=f[i]*c1; /* other points */ for(j=i+i;j<n;j+=i+i) { /* points placed too far to have influence from the right */ if(j+i>=n) { a1=a[j]/b[j-i]; c[j]=0.; a[j]=a[j-i]*a1; b[j]-=c[j-i]*a1; f[j]+=f[j-i]*a1; } /* the general case */ else { c1=c[j]/b[j+i]; a1=a[j]/b[j-i]; c[j]=c[j+i]*c1; a[j]=a[j-i]*a1; b[j]-=a[j+i]*c1+c[j-i]*a1; f[j]+=f[j+i]*c1+f[j-i]*a1; } } } /* Central coefficients b and f calculation */ if(full>=n) { /* case when n!=2^order+1 */ a1=a[half]/b[0]; b[half]-=c[0]*a1; f[half]+=f[0]*a1; } else { /* when it's equal */ c1=c[half]/b[full]; a1=a[half]/b[0]; b[half]-=a[full]*c1+c[0]*a1; f[half]+=f[full]*c1+f[0]*a1; } /* results in the central and leftmost points */ u[half]=f[half]/b[half]; u[0]=(c[0]*u[half]+f[0])/b[0]; /* result in the rightmost point if n==2^order+1 */ if(full<n) u[full]=(a[full]*u[half]+f[full])/b[full]; /* Backward pass: results in all the other points */ for(i=quarter;i>=1;i>>=1) for(j=i;j<n;j+=i+i) { /* too far to have right influence */ if(j+i>=n) u[j]=(f[j]+a[j]*u[j-i])/b[j]; /* the general case */ else u[j]=(f[j]+c[j]*u[j+i]+a[j]*u[j-i])/b[j]; } } Вверх по странице, к оглавлению и навигации.
|