Processing math: 10%
Глава II
2.5. Вычислительные алгоритмы

В этом разделе мы разрабатываем алгоритмы для вычисления значений базисных функций и их производных. Пусть U={u0,,um} будет векторным узлом как в ур­авнении (2.13), и предположим, что мы заинтересованы в базисных функциях степени p. Кроме того, предположим, u фиксирован, и u[ui,ui+1). Мы разрабатываем пять алгоритмов, которые вычисляют:

Мы представляем два алгоритма, которые вычисляют p+1 функций до двух, вычислияющих только один, потому что они наиболее важны и на самом деле несколько проще.

Из Св.2.2 и в предположении, что u[ui,ui+1), следует, что мы можем сосредо­точить наше внимание на функциях Ni-p,p,,Ni,p и их производных; Все остальные функции тождественно равны нулю, и это расточительно, чтобы фактически вычи­слить их. Таким образом, первым шагом в оценке является определение продолжитель­ности узла, в котором лежит u. Можно использовать либо линейный или двоичный поиск векторного узла; мы представляем здесь двоичный поиск. Так как мы использу­ем интервалы вида и u[ui,ui+1), тонкая проблема в оценке базисных функций част­ным случаем u=um. Лучше всего с этим справиться на самом низком уровне, устано­вив индекс интервала n(=m-p-1). Таким образом, в этом случае u(um-p-1, um-p]. FindSpan это функция возвращающая целое число, которая означает индекс диапазона.

Алгоритм А2.1

Int  FindSpan(n,p,u,U)
{ /* Определение  индекса  узлового  промежутка */
  /* Вход: n,p,u,U  */
  /* Выход: индекс  узлового  промежутка */
If  (u == U[n + 1])  return(n);  /* Особый случай */
low = p;  high = n + 1;  /* Выполняем  двоичный  поиск */
mid = (low + high)/2;
while  (u < U[mid] || u >= U[mid + 1])
   {
   If  (u < U[mid])  high = mid;
     else   low = mid;
   mid = (low + high)/2;
   }
return(mid);
}
    

Теперь мы укажем второй алгоритм. Предполагая, что u в iтом интервале, вычисление ненулевых функций, приводит к перевернутой треугольной схеме

Ni-p,p
Ni-1,1
Ni,0 ... vdots
N_{i,1}
N_{i,p}

Примеры

Пример2.3 Пусть p=2, U={0,0,1,2,3,4,4,5,5,}, и u=5⁄2 (смотри рисунок 2.6). Тогда i=4, так u∈[u_4,u_5). Таким образом, мы вычисляем
N_{2,2}(5⁄2)
N_{3,1}(5⁄2)
N_{4,0}(5⁄2) N_{3,2}(5⁄2)
N_{4,1}(5⁄2)
N_{4,2}(5⁄2)
Подставляя u=5⁄2 в формуле (2.5) (читатель должен сделать это) получаем
N_{4,0}(5/2)=1
N_{3,1}(5/2)=1/2 N_{4,1}(5/2)=1/2
N_{2,2}(5/2)=1/8 N_{3,2}(5/2)=6/8 N_{4,2}(5/2)=1/8
Обратите внимание, что функции степени фиксированно равны в сумме 1 (Св.2.4).

Это будет ясно читателю, который выполнял замены в этом примере, что есть много избыточных вычислений присущие уравнению (2.5). Например, выписывая функции второй степени в общих чертах, мы имеем

N_{i-2,2}(u)={u-u_{i-2}}/{u_i-u_{i-2}}N_{i-2,1}(u)+{u_{i+1}-u}/{u_{i+1}-u_{i-1}}N_{i-1,1}(u)(2.14)

N_{i-1,2}(u)={u-u_{i-1}}/{u_{i+1}-u_{i-1}}N_{i-1,1}(u)+{u_{i+2}-u}/{u_{i+2}-u_i}N_{i,1}(u)(2.15)

N_{i,2}(u)={u-u_i}/{u_{i+2}-u_i}N_{i,1}(u)+{u_{i+3}-u}/{u_{i+3}-u_{i+1}}N_{i+1,1}(u)(2.16)

Обратите внимание, что Введем обозначения

"left"[j]=u-u_{i+1-j}"right"[j]=u_{i+j}-u

Уравнения (2.14) - (2.16), то

N_{i-2,2}(u)={"left"[3]}/{"right"[0]+"left"[3]}N_{i-2,1}(u)+{"right"[1]}/{"right"[1]+"left"[2]}N_{i-1,1}(u)

N_{i-1,2}(u)={"left"[2]}/{"right"[1]+"left"[2]}N_{i-1,1}(u)+{"right"[2]}/{"right"[2]+"left"[1]}N_{i,1}(u)

N_{i,2}(u)={"left"[1]}/{"right"[2]+"left"[1]}N_{i,1}(u)+{"right"[3]}/{"right"[3]+"left"[0]}N_{i+1,1}(u)

Основываясь на этих наблюдениях, Алгоритм A2.2 вычисляет все отличные от нуля базисных функций и сохраняет их в массиве N[0],…,N[p].

Алгоритм А2.2

BasisFuns(i,u,p,U,N)
{ /*  Вычислить  неисчезающее  базисную  функцию  */
  /* Вход: i,u,p,U  */
  /* Выход: N  */
N[0] = 1.0;
for  (j=1; j<=p; j++)
   {
   left[j] = u - U[i + 1 - j];
   right[j] = U[i + j] - u;
   saved = 0.0;
   for  (r=0; r<j; r++)
      {
      temp = N[r] / (right[r + 1] + left[j - r]);
      N[r] = saved + right[r + 1]*temp;
      saved = left[j - r]*temp;
      }
   N[j] = saved;
   }
}
    
Заметим, что Алгоритм A2.2 не только эффективен, но также гарантирует, что не будет деления на ноль, что может произойти с непосредственным применением уравнения (2.5).

Теперь к третьему алгоритму; в частности, мы хотим, чтобы вычислить все N_{r,p}^{(k)}(u), для i-p≤r≤i и 0≤k≤n, где n≤p. Инспекция уравнения (2.10) показывает, что основные ингредиенты:

Рассматриваемый как двумерный массив размерности (p+1)×(p+1), базисные функции вписываются в верхний треугольник (в том числе по диагонали), и различные узлы находятся к низу треугольника, т.е.

N_{i,0}(u) N_{i-1,1}(u) N_{i-2,2}(u)
u_{i+1}-u_i N_{i,1}(u) N_{i-1,2}(u)
u_{i+1}-u_{i-1} u_{i+2}-u_i N_{i,2}(u)

Примеры

Пример2.4 Пусть p=2, U={0,0,0,1,2,3,4,4,5,5,5}, и u=5⁄2. Тогда u∈[u_4,u_5), и массив становится

N_{4,0}(5/2)=1 N_{3,1}(5/2)=1/2 N_{2,2}(5/2)=1/8
u_5-u_4=1 N_{4,1}(5/2)=1/2 N_{3,2}(5/2)=6/8
u_5-u_3=2 u_6-u_4=2 N_{4,2}(5/2)=1/8

Теперь вычислим N_{4,2}^{(1)}(5⁄2) и N_{4,2}^{(2)}(5⁄2); с i=4 в формуле (2.10), имеем

a_{1,0}=1/{u_6-u_4}=1/2

a_{1,1}=-1/{u_7-u_5}=-1

a_{2,0}=a_{1,0}/{u_5-u_4}=1/2

a_{2,1}={a_{1,1}-a_{1,0}}/{u_6-u_5}={-1-1/2}/{4-3}=-3/2

a_{2,2}=a_{1,1}/{u_7-u_6}=1/{4-4}=1/0

N_{4,2}^{(1)}=2[a_{1,0}N_{4,1}(5/2)+a_{1,1}N_{5,1}(5/2)]

и

N_{4,2}^{(2)}=2[a_{2,0}N_{4,0}(5/2)+a_{2,1}N_{5,0}(5/2)+a_{2,2}N_{6,0}(5/2)]

Теперь a_{1,1}, a_{2,1}, и a_{2,2} все используют различные узлы, которые не находится в массиве, но они умножаются соответственно на N_{5,1}(5⁄2), N_{5,0}(5⁄2), и N_{6,0}(5⁄2), которые также не в массиве. Эти термины определены равными нулю, и мы остались с

N_{4,2}^{(1)}=2a_{1,0}N_{4,1}(5/2)=1/2

N_{4,2}^{(2)}=2a_{2,0}N_{4,0}(5/2)=1

Чтобы проверить эти значения, напомним, из Раздела 2.2 следует, что N_{4,2}(u)=1⁄2(u-2)^2 на u∈[2,3). Вычисление N_{3,2}^{(1)}(5⁄2), N_{3,2}^{(2)}(5⁄2), N_{2,2}^{(1)}(5⁄2), и N_{2,2}^{(2)}(5⁄2) аналогично.

Основываясь на этих наблюдениях (и Пример2.4), это не трудно разработать Алгоритм A2.3, который вычисляет ненулевые базисные функции и их производных, вплоть до nой производной (n≤p). Выход в двумерном массиве, ders. ders[k][j] является kой производной функции N_{i-p+j,p}, где 0≤k≤n и 0≤j≤p. Используются два локальных массива:

Алгоритм позволяет избежать деления на ноль и/или использования членов не в массиве ndu[][].

Алгоритм А2.3

DersBasisFuns(i,u,p,n,U,ders)
{ /* Вычислить  ненулевые  базисные  функции  и  их */
  /* производные. Первый  раздел  это  изменённый  A2.2 */
  /* для  хранения  функций  и  узлов  различия. */
  /* Вход: i,u,p,n,U  */
  /* Выход: ders  */
ndu[0][0] = 1.0;
for  (j=1; j<=p; j++)
   {
   left[j] = u – U[i+1-j];
   right[j] = U[i+j] – u;
   saved = 0.0;
   for  (r=0; r<j; r++)
      {  /*  Нижний  треугольник  */
      ndu[j][r] = right[r+1] + left[j-r];
      temp = ndu[r][j-1]/ndu[j][r];
         /*  Верхний треугольник  */
      ndu[j][r] = saved + right[r+1]*temp;
      saved = left[j-r]*temp;
      }
   ndu[j][j] = save
   }
for  (j=0; j<=p; j++)  /* Загрузим  базисные  функции */
   ders[0][j] = ndu[j][p];
/* В  этом  разделе  вычисляем  производные (уравнение [2.9]) */
for  (r=0; r<p; r++)  /* Цикл  по  индексу  функции */
   {
   s1 = 0; s2 = 1; /* Альтернативные  строки  в  массиве */
   a[0][0] = 1.0;
/* Цикл вычисления k-ой производной */
   for  (k=1; k<=n; k++)  
      {
      D = 0.0;
      Rk = r – k;  pk = p – k;
      if  (r >= k)
         {
         a[s2][0] = a[s1][0]/ndu[pk+1][rk];
         d = a[s2][0]*ndu[rk][pk];
         }
      if  (rk >= -1)    j1 = 1;
         else           j1 = -rk;
      if  (r - 1 <= pk) j2 = k - 1;
         else           j2 = p - r;
   for  (j=j1; j<=j2; j++)
      {
      a[s2][j] = (a[s1][j] – a[s1][j-1])/ndu[pk+1][rk+j];
      d += a[s2][k]*ndu[rk+j][pk];
      }
   if  (r <= pk)
      {
      a[s2][k] = -a[s1][k-1]/ndu[pk+1][r];
      d += a[s2][k]*ndu[r][pk];
      }
   ders[k][r] = d;
   j = s1;  s1 = s2; s2 = j;  /* Поменять местами строки */
   }
}
/* Умножьте  через  его  в  определенных  факторов */
/* (Уравнение [2.9]) */
r = p;
for  (k=1; k<=n; k++)  
   {
   for  (j=0; j<=p; j++)  derk[k][j] *=r;
   r *= (p - k);
   }
}
    

Обратим внимание теперь на последние два алгоритма, а именно вычисления одной базисной функции, N_(i,p)(u), или производные, N_(i,p)^((k))(u), единичной базисной функции. Решения этих задач приводят к треугольным таблицам вида

N_{i,0}
N_{i,1}
N_{i+1,0} N_{i,2}
ldots vdots N_{i,p}
N_{i+p-1,0} N_{i+p-2,2}
N_{i+p-1,1}
N_{i+p,0}

Примеры

Пример2.5 Пусть p=2, U={0,0,0,1,2,3,4,4,5,5,5}, и u=5⁄2. Расчет N_{3,2}(5⁄2) дает
N_{3,0}(5⁄2)=0
N_{3,1}(5⁄2)=1/2
N_{4,0}(5⁄2)=1 N_{3,2}(5⁄2)=6/8
N_{4,1}(5⁄2)=1/2
N_{5,0}(5⁄2)=0
N_{4,2} (5⁄2) получают из
N_{4,0}(5⁄2)=1
N_{4,1}(5⁄2)=1/2
N_{5,0}(5⁄2)=0 N_{4,2}(5⁄2)=1/8
N_{5,1}(5⁄2)=0
N_{6,0}(5⁄2)=0
Обратите внимание, что положение и относительное количество ненулевых элементов в таблице зависит от p и на позиции 1 в первом столбце. Алгоритм A2.4 вычисляет только ненулевые элементы. Значение N_{i,p}(u) возвращается в Nip; m является высокий индекс U(m+1 узлов). Алгоритм похож на Алгоритм A2.2 в использовании переменных temp и saved.

Алгоритм А2.4

OneBasisFuns(p,m,U,i,u,Nip)
{ /* Вычислить  базисную  функцию */
  /* Вход: p,m,U,i,u */
  /* Выход: Nip */
if ((i == 0 && u == U[0]) ||      /* Частный */
     (i == m-p-1 && u == U[m])  /* случай */
   {
   Nip = 1.0; return;
   }
If (u < U[i] || u >= U[i+p+1])   /* Локальное  свойство */
   {
   Nip = -.0; return;
   }
for (j=0; j<=p; j++) /* Инициализация  функций  нулевой  степени */
   if (u >= U[i+j] && u < U[i+j+1]) N[j] = 1.0;
      else   N[j] = 0.0;
for (k=1; k<=p; k++)  /* Вычислить  треугольную  таблицу */
   {
   if (N[0] == 0.0)   saved = 0.0;
      else   saved = ((u-U[i])*N[0])/(U[i+k]-U[i]);
   for (j=0; j<=p-k+1; j++)
      {
      Uleft = U[i+j+1];
      Uright = U[i+j+k+1]
      If (N[j+i] == 0.0)
        {
         N[j] = saved; saved = 0.0;
        }
         else
         {
         temp = N[j+1]/(Uright-Uleft);
         N[j] = saved+(Uright-u)*temp;
         saved = (u- Uleft)*temp;
        }
      }
   }
Nip = N[0];
}        
    

Теперь для фиксированной i, вычисление производных, N_{i,p}^{(k)}(u), для k=0,…,n, n≤p, использует уравнение (2.9). Например, если p=3 и n=3, то

N_{i,3}^{(1)}=3(N_{i,2}/(u_{i+3}-u_i)-N_{i+1,2}/(u_{i+4}-u_{i+1}))

N_{i,3}^{(2)}=3(N_{i,2}^{(1)}/(u_{i+3}-u_i)-N_{i+1,2}^{(1)}/(u_{i+4}-u_{i+1}))

N_{i,3}^{(3)}=3(N_{i,2}^{(2)}/(u_{i+3}-u_i)-N_{i+1,2}^{(2)}/(u_{i+4}-u_{i+1}))

Используя треугольные таблицы, мы должны вычислить
k=0:
N_{i,0}
N_{i,1}
N_{i+1,0} N_{i,2}
N_{i+1,1} N_{i,3}
N_{i+2,0} N_{i+1,2}
N_{i+2,1}
N_{i+3,0}
k=1:
N_{i,2}
N_{i,3}^{(1)}
N_{i+1,2}
k=2:
N_{i,1}
N_{i,2}^{(1)}
N_{i+1,1} N_{i,2}^{(2)}
N_{i+1,2}^{(1)}
N_{i+2,1}
k=3:
N_{i,0}
N_{i,1}^{(1)}
N_{i+1,0} N_{i,2}^{(2)}
N_{i+1,1}^{(1)} N_{i,3}^{(3)}
N_{i+2,1} N_{i+1,2}^{(2)}
N_{i+2,1}^{(1)}
N_{i+3,0}

На словах алгоритм:

  1. вычислить и сохранить всю соответствующую треугольную таблицу;
  2. чтобы получить k-ую производной, загрузите столбец таблицы, которая содержит функции степени p-k, и вычислить оставшуюся часть треугольника.

Алгоритм A2.5 вычисляет N_{i,p}^{(k)}(u) для k=0,…,n,n≤p. k-ая производная возвращается в ders[k].

Алгоритм А2.5

DersOneBasisFuns(p,m,U,i,u,n,ders)
{ /* Вычислить  производных  базисной  функции  Nip */
  /* Вход: p,m,U,i,u,n */
  /* Выход: ders */
if (u < U[i] || u >= U[i+p+1])  /* Локальное  свойство */
   {
   for  (k=0; k<=n; k++)   ders[k] = 0.0;
   return;
   }
for (j=0; j<=p; j++)  /* Инициализация  функций  нулевой  степени */
   if (u >= U[i+j] && u < U[i+j+1])  N[j][0] = 1.0;
     else   N[j][0] = 0.0;
for (k=0; k<=p; k++)  /* Вычислить  полную  треугольную  таблицу */
   {
   if (N[0][k-1] == 0.0)   saved = 0.0;
      else  saved = ((u – U[i])*N[0][k-1])/(U[i+k] - U[i]);
   for (j=0; j<p-k+1; j++)
      {
      Uleft = U[i+j+1];
      Uright = U[i+j+k+1];
      if (N[j+1][k-1] == 0.0)
         {
         N[j][k] = saved; saved = 0.0;
         }
         else
         {
         temp = N[j+1][k-1]/(Uright-Uleft);
         N[j][k] = saved+(Uright-u)*temp;
         Saved = (u-Uleft)*temp;
         }
      }
   }
ders[0] = N[0][p]; /* Значение  функций */
for (k=1; k<=n; k++)  /* Вычисление  производных */
   {
   for (j=0; j<k; j++)  /* Загрузка  соответствующей  колонки */
      ND[j] = N[j][p-k];
   for (jj=1; jj<=k; jj++) /* Вычислить  таблицу  ширины  k */
      {
      if (ND[0] == 0.0)  saved = 0.0;
         else      saved = ND[0]/(U[i+p-k+jj]-U[i]);
      for (j=0; j<k-jj+1; j++)
         {
         Uleft = U[i+j+1];
         Uright = U[i+j+p+jj+1];
         if (ND[j+1] == 0.0)
            {
            ND[j] = (p-k+jj)*saved;   saved = 0.0;
            }
            else
            {
            temp = ND[j+1]/(Uright-Uleft);
            ND[j] = (p-k+jj)*( saved-temp);
            saved =temp;
            }
      }
   ders[k] = ND[0];  /* k-ая  производная */
   }
}        
    

Наконец, отметим, что Алгоритмы A2.3 и A2.5 вычисляют производные с права, если u есть узел. Тем не менее, уравнения (2.5), (2.9), (2.10), и другие в этой главе мож­но было бы определить с помощью интервалов вида и u∈(u_i,u_{i+1}]. Это не изменит Алгоритмы A2.2 через A2.5. Другими словами, производные слева можно найти, просто имея алгоритм использующий интервалы в форме (u_i,u_{i+1}], вместо [u_i,u_{i+1}). В предыдущем примере, с p=2 и U={0,0,0,1,2,3,4,4,5,5,5}, если u=2, то охватывают i=3 следует производные от левой, и i=4 урожайность производные с правой стороны.

Упражнения

  1. Рассмотрим линейные и квадратичные функции, вычисленные ранее и показанные на рисунках 2.5 и 2.6. Подставим u=5⁄2 в полиномиальные уравнения, чтобы получить N_{3,1}(5⁄2), N_{4,1}(5⁄2), N_{2,2}(5⁄2), N_{3,2}(5⁄2) , и N_{4,2}(5⁄2)). Что вы заметили в сумме двух линейных и сумме трех квадратичных функций?
  2. Рассмотрим квадратичные функции на рисунке 2.6. Используя полино­миальные выражения для N_{3,2}(5⁄2), оцените функцию и ее первую и вторую производные при u=2 как слева, так и справа. Соблюдайте преемственность. Имеет ли свойство P2.5? Сделайте то же самое с N_{4,2}(u) при u=4.
  3. Пусть U={0,0,0,0,1,2,3,4,4,5,5,5,5}. Как это меняет функции степени 0, 1 и 2 на рисунках 2.4-2.6? Вычислить и нарисовать девять кубических базисных функций, связанных с U.
  4. Рассмотрим функцию N_{2,2}(u) на рисунке 2.5, N_{2,2}(u)=1⁄2u^2 на [0,1), -3⁄2+3u-u^2 на [1,2) и 1⁄2(3 - u)^2 на [2,3). Используйте формулу (2.10), чтобы получить выражения для первой и второй производных N_{2,2}(u)
  5. Снова рассмотрим? N_{2,2}(u) на рисунке 2.5. Получите первые производ­ные N_{2,1} и N_{3,1}, напрямую дифференцируя выражения полинома. Затем используйте их вместе с уравнением. (2.11), чтобы получить N_{2,2}^'
  6. Снова пусть p = 2, u=5⁄2 U={0,0,0,1,2,3,4,4,5,5,5}. Перейдите вручную с помощью алгоритма A2.2, чтобы найти значения трех ненулевых базисных функций. Проследите по алгоритму A2.3, чтобы найти первую и вторую производные базисных функций.
  7. Используйте те же p и U, что и в упражнении 2.6, с u=2. Трассировка по алгоритму A2.3 с n=1, один раз с i=3 и один раз с i=4. Затем дифферен­цируйте соответствующие полиномиальные выражения для N_{j,2}, приведен­ных в разделе 2.2, и оцените производные слева и справа при u=2. Сравните результаты с результатами, полученными из алгоритма А2.3.
  8. Используя те же p и U, что и в упражнении 2.6, пусть u=4. Проведите через алгоритмы A2.2 и A2.3 , чтобы убедиться, что нет проблем с двойными узлами.
  9. При тех же значениях p и U, что и в упражнении 2.6, пусть u=5⁄2 - трассировка по алгоритму A2.5 и вычисление производных N_{4,2}^{(k)}(5⁄2) для k=0,1,2.