Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Как лучше нарисовать параболу по трём точкам?


Автор: KPEHDEL 25.5.2013, 13:05
Здравствуйте.
Делаю программу для численного интегрирования. Есть Методы левых, правых? центральных, прямоугольников, трапеций и симпсона.

Вот с последним проблема. С другими вроде всё достаточно понятно. Пример:
Левых прямоугольников: http://s2.ipicture.ru/uploads/20130525/GpjkpRRG.png
Трапеций: http://s2.ipicture.ru/uploads/20130525/n6xoS2nq.png


Проблема и вопрос в том, как лучше нарисовать/визуализировать метод Симпсона(Парабол).

Использую компонент Chart.
На данный момент метод выглядит примерно так:
Код

procedure TForm1.simpson;
var
  i:integer;
begin
  I_simpson:=0;
  SeriesSimpson.Clear;
  if N mod 2 <> 0 then
    EditSimpson.Text:=trim(EditN.text)+'%2!=0'
  else
    begin
      for i:=0 to N-1 do  // Проходим по всем разбиениям
        begin
          i_simpson:=i_simpson + ((points[i].xR- points[i].xL)*(funcs[i].fL+4*funcs[i].fM+funcs[i].fR))/6;  // Считаю.
          SeriesSimpson.AddXY(points[i].xL, funcs[i].fL); // Рисую.
          //Здесь наверное создавать вложенный цикл и в нём добавлять на график(в пределах разбиения) точки по какому-то принципу.
        end;
      SeriesSimpson.AddXY(points[N].xL, funcs[N].fL);
      EditSimpson.Text:=floattostrf(i_simpson, ffGeneral, 4, 8);
      SeriesSimpson.Active:=false;
    end;
end;


Если для визуализации предыдущих методов достаточно было двух точек, то здесь точек для визуализации надо больше и располагать их внутри отрезка.

Собственно сам вопрос. 
Как через 3 точки нарисовать параболу?
Допустим, для каждого разделения будет вложенный цикл. По какому принципу(формуле) между этими 3 точками добавить и рассчитать их координаты, чтобы получилась парабола?

Визуально должно выглядеть примерно так:
http://en.academic.ru/pictures/enwiki/83/Simpsons_method_illustration.png
http://s2.ipicture.ru/uploads/20130525/pCJ32P0J.png
http://s2.ipicture.ru/uploads/20130525/izJsVNfR.png


С нетерпением жду ответа.
Заранее спасибо.

Автор: Beltar 25.5.2013, 13:17
Если я правильно понял, то http://ru.wikipedia.org/wiki/Парабола
Последний раздел, вычисление коэффицинтов по трем точкам, после чего построение графика y(x) становится тривиальным.

Автор: Фантом 25.5.2013, 13:21
По определению метода Симпсона, на каждом отрезке интегрирования функция интерполируется параболой, проходящей через точки функции с аргументами, равными крайним и средней точкам отрезка. Следовательно, если есть отрезок [x1,x3] и средняя точка x2=(x1+x3)/2, а также значения функции y1=f(x1) и т.д., то у Вас есть система уравнений вида:
a*x1^2+b*x1+c=y1
a*x2^2+b*x2+c=y2
a*x3^2+b*x3+c=y3

Она решается относительно a,b,c, тем самым Вы получаете выражения для коэффициентов параболы, которую на этом участке надо нарисовать.

Автор: KPEHDEL 25.5.2013, 18:09
Спасибо за ответы.
Что-то я совсем запутался... Помогите осознать, что делаю не так...

Результат получился такой:
http://s2.ipicture.ru/uploads/20130525/fE4RP54Z.png

Что даже немного не похоже не правду. Что-то я не так делаю с f(x).

Код

procedure TForm1.simpson;
var
  i,j:integer;
  x1,x2,x3:real;
  y,y1,y2,y3:real;
  a,b,c:real;

begin
  I_simpson:=0;
  SeriesSimpson.Clear;
  if N mod 2 <> 0 then
    EditSimpson.Text:=trim(EditN.text)+'%2!=0'
  else
    begin
      for i:=0 to N-1 do  // Проходим по всем разбиениям
        begin
          i_simpson:=i_simpson + ((points[i].xR- points[i].xL)*(funcs[i].fL+4*funcs[i].fM+funcs[i].fR))/6;  // Считаю.
          SeriesSimpson.AddXY(points[i].xL, funcs[i].fL); // Рисую.

          x1 := points[i].xL;
          x3 := points[i].xR;
          x2 := ( x1 + x3 ) / 2;
          //Здесь наверное создавать вложенный цикл и в нём добавляем точки для рисования параболы.
          for j:=1 to 10 do
          begin
            y1 := Fx(x1+j/10);
            y3 := Fx(x3+j/10);
            y2 := ( y1 + y3 ) / 2;

            a := ( y3 - ( x3 * ( y2 - y1 ) + x2 * y1 - x1 * y2 ) / ( x2 - x1 ) ) / ( x3 * ( x3 - x1 - x2 ) + x1 * x2 );
            b := ( ( y2 - y1 ) / ( x2 - x1 ) ) - a*( x1 + x2 );
            c := ( ( x2*y1 - x1*y2 ) / x2 - x1 ) + a*x1*x2;
            y := a*(x1*x1) + b*x1 + c;
            SeriesSimpson.AddXY( points[i].xL + j / 10, y );
          end;
        end;
      SeriesSimpson.AddXY(points[N].xL, funcs[N].fL);
      EditSimpson.Text:=floattostrf(i_simpson, ffGeneral, 4, 8);
      SeriesSimpson.Active:=false;
    end;
end;

Автор: KPEHDEL 25.5.2013, 20:45
Есть прогресс.
f(x) = sin(x): http://s2.ipicture.ru/uploads/20130525/mTX3NE1K.png
f(x) = x: http://s2.ipicture.ru/uploads/20130525/01C5cQhu.png

На интервале 8 разбиений, но так как вертикальные черты прорисовываются и от точек для параболы, выглядит будто их много.

Собственно 2 вопроса:
1. Это ведь не верно? Будь верно, на f(x) = x, мы бы увидели не сходимость, а искривления параболы? Или я что-то не понимаю?
2. Как убрать вертикальные черты от точек для параболы, которые задаются во вложенном цикле на каждом отрезке разбиения?

Если не верно, подскажите в чём и я продолжу разбираться. Код ниже.

Код:
Код

procedure TForm1.simpson;
var
  i,j:integer;
  x1,x2,x3:real;
  x,y,y1,y2,y3:real;
  a,b,c:real;

begin
  I_simpson:=0;
  SeriesSimpson.Clear;
  if N mod 2 <> 0 then
    EditSimpson.Text:=trim(EditN.text)+'%2!=0'
  else
    begin
      for i:=0 to N-1 do  // Проходим по всем разбиениям
        begin
          i_simpson:=i_simpson + ((points[i].xR- points[i].xL)*(funcs[i].fL+4*funcs[i].fM+funcs[i].fR))/6;  // Считаю.
          SeriesSimpson.AddXY(points[i].xL, funcs[i].fL); // Рисую.

          x := points[i].xL;
          x1 := points[i].xL;
          x3 := points[i].xR;
          x2 := ( x1 + x3 ) / 2;

          y1 := funcs[i].fL;
          y3 := funcs[i].fR;
          y2 := ( y1 + y3 ) / 2;

          //Здесь наверное создавать вложенный цикл и в нём добавляем точки для рисования параболы.
          while x < x3 do
          begin
            a := ( y3 - ( x3 * ( y2 - y1 ) + x2 * y1 - x1 * y2 ) / ( x2 - x1 ) ) / ( x3 * ( x3 - x1 - x2 ) + x1 * x2 );
            b := ( ( y2 - y1 ) / ( x2 - x1 ) ) - a*( x1 + x2 );
            c := ( ( x2*y1 - x1*y2 ) / x2 - x1 ) + a*x1*x2;
            x := x+0.25;
            y := a*sqr(x) + b*x + c;
            SeriesSimpson.AddXY( x, Fx(x) );
          end;
        end;
      SeriesSimpson.AddXY(points[N].xL, funcs[N].fL);
      EditSimpson.Text:=floattostrf(i_simpson, ffGeneral, 4, 8);
      SeriesSimpson.Active:=false;
    end;
end;



С нетерпением жду ответа.

Автор: Фантом 25.5.2013, 21:09
Цитата(KPEHDEL @  25.5.2013,  21:45 Найти цитируемый пост)

1. Это ведь не верно? Будь верно, на f(x) = x, мы бы увидели не сходимость, а искривления параболы? Или я что-то не понимаю?

Прямая - частный случай параболы, так что все нормально. Коэффициент a у Вас везде в этом случае получается равным 0 (проверьте, если не лень), как и должно быть.

Цитата(KPEHDEL @  25.5.2013,  21:45 Найти цитируемый пост)
2. Как убрать вертикальные черты от точек для параболы, которые задаются во вложенном цикле на каждом отрезке разбиения?

Ну, тут Вам виднее, как Вы их рисуете...

Автор: Beltar 25.5.2013, 22:00
Код

 a := ( y3 - ( x3 * ( y2 - y1 ) + x2 * y1 - x1 * y2 ) / ( x2 - x1 ) ) / ( x3 * ( x3 - x1 - x2 ) + x1 * x2 );
            b := ( ( y2 - y1 ) / ( x2 - x1 ) ) - a*( x1 + x2 );
            c := ( ( x2*y1 - x1*y2 ) / x2 - x1 ) + a*x1*x2;
            y := a*(x1*x1) + b*x1 + c;


1) Вычисления a, b, c из цикла выносятся.
2) Ты зачем в ф-ию подстваляешь x1, когда надо ее просто протабулировать от x1 до x2?

Автор: KPEHDEL 25.5.2013, 22:46
Beltar,
Цитата

1) Вычисления a, b, c из цикла выносятся.

Да, вынес.

Цитата

2) Ты зачем в ф-ию подстваляешь x1, когда надо ее просто протабулировать от x1 до x2? 


Ну в последнем варианте кода я так и делаю:
Цитата

x := x+0.25;
y := a*sqr(x) + b*x + c;
SeriesSimpson.AddXY( x, Fx(x) );


Если это верно. То проблема решена, всем спасибо smile 

Ну и если кто-то знает как убрать вертикальные разделительные линии, которые появляются при использовании метода AddXY(x,y) для создания точки, буду благодарен...(Как не отображать известно, а вот как управлять отображением нужных из кода хз) А то что-то не могу найти. Быть может для этого надо использовать другой метод добавления точки?

Автор: KPEHDEL 26.5.2013, 09:45
Блин, я обнаружил ошибку... Всё не верно...
В коде выше, в этом куске:
Код

while x < x3 do
          begin
            a := ( y3 - ( x3 * ( y2 - y1 ) + x2 * y1 - x1 * y2 ) / ( x2 - x1 ) ) / ( x3 * ( x3 - x1 - x2 ) + x1 * x2 );
            b := ( ( y2 - y1 ) / ( x2 - x1 ) ) - a*( x1 + x2 );
            c := ( ( x2*y1 - x1*y2 ) / x2 - x1 ) + a*x1*x2;
            x := x+0.25;
            y := a*sqr(x) + b*x + c;
            SeriesSimpson.AddXY( x, Fx(x) );
          end;


При добавлении точки, подставляю Fx(x). Тем самым я просто рисую текущую функцию smile 
Вычисленный y, нигде не используется... А должен подставлять y. Если подставлю y, то на графике полная http://s1.ipicture.ru/uploads/20130526/K0ijKfTf.png.

Подскажите пожалуйста где ошибка и как правильно записать?

Автор: KPEHDEL 26.5.2013, 19:42
Нашёл одну ошибку.
Она заключалась в вычислении  коэффициента C.
Код

// c := ( ( x2*y1 - x1*y2 ) / x2 - x1 ) + a*x1*x2; // так было
c := (x2*y1 - x1*y2) / (x2-x1) + a*x1*x2; // так нужно


Однако есть где-то ещё ошибка, график всё ещё не верен. 
http://s2.ipicture.ru/uploads/20130528/Ypl6X4PA.jpg

К сообщению прикрепил архив с исходниками, возможно так будет проще найти ошибку.


Автор: KPEHDEL 28.5.2013, 09:29
По мимо ошибки в вычислении коэффициента C.

Нашёл ещё одну ошибку. Зачем-то я на каждом отрезке интервала в X присваивал 0.
Теперь это выглядит так:
http://s1.ipicture.ru/uploads/20130528/UaOit895.png

Но это тоже не верно, не параболы( Помогите найти остальные ошибки.

Добавлено @ 09:44
Кажется я понял.

Фантом, 
Я глупец, повёлся и не подумал)
Не x2=(x1+x3)/2, а x2=(x3-x1)/2  smile И соответственно y2=(y3-y1)/2

И того получается:
на 8 узлах: http://s2.ipicture.ru/uploads/20130528/84eLj7gB.jpg

На 2 узлах: http://s2.ipicture.ru/uploads/20130528/8QLTO0xM.jpg

Но это ведь тоже не верно? Ведь Симпсон должен быть точнее, а на графике он что-то совсем не точен...

Автор: Фантом 28.5.2013, 10:17
Цитата(KPEHDEL @  28.5.2013,  10:29 Найти цитируемый пост)

Я глупец, повёлся и не подумал)
Не x2=(x1+x3)/2, а x2=(x3-x1)/2  smile И соответственно y2=(y3-y1)/2

Это Вы как-то неудачно подумали.  smile Если сдвиг переменной не используется (а исходно предполагалось именно это), то там должна быть сумма.

Автор: KPEHDEL 28.5.2013, 10:29
Фантом, беда. Тогда я в конец запутался.
Если делаю 
x2=(x1+x3)/2, y2=(y1+y3) получается на графике вот что:
http://s1.ipicture.ru/uploads/20130528/4rMVCW6d.jpg
А это на Симпсона не похоже, скорее на трапеций.

Текущие исходники прикрепил к сообщению.

В чём может быть дело?

Автор: KPEHDEL 28.5.2013, 18:13
Кажется я понял почему так получается. 
При: x2=(x1+x3)/2, y2=(y1+y3) 
У нас получается, что средняя точка. точна по середине. И того, все 3 точки расположены на прямой.
У Параболы такого быть не может.

Всё верно? Если да, то как быть? Как правильно посчитать третью точку?)

Кажется мне надо смотреть в сторону кривой Безье.


Автор: Фантом 29.5.2013, 00:29
Цитата(KPEHDEL @  28.5.2013,  19:13 Найти цитируемый пост)
При: x2=(x1+x3)/2, y2=(y1+y3) 

Откуда? y_i=f(x_i), расположены на одной прямой все три точки будут только в том случае, если Вы интегрируете линейную функцию (чего явно не наблюдается).

По-видимому, в этом и ошибка. x3 можно и нужно считать как среднее значение концов интервала x1 и x2, но к значениям функции это совершенно не относится.

Автор: KPEHDEL 30.5.2013, 21:05
Проблема решена. Тему можно пометить решённой.
Ошибка была в том, что я глупец, делал y2=(y1+y3) /2, а надо y2=Fx(x2);
Архив с программой прикрепил к сообщению, может кому-то пригодится.


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)