Рунге Кутта в C для уравнения Лоренца

я пытаюсь вычислить систему Лоренца, используя метод Рунге Кутты, но я не могу найти, где в моем коде есть ошибка. Когда я запускаю его, система переходит в статическую точку, и я должен получить бабочку (аттрактор Лоренца). Я думаю, что это что-то о циклах for в разделе методов Рунге Кутта. Вот мой код

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
double f(int,double,double []);
double s,r,b;
FILE *output;
int main()
{
    output=fopen("lorenzdata.dat","w");
    int j,N=3;
    double x[2],dt,y[2],K1[2],K2[2],K3[2],K4[2],ti,t,i;
    printf("\n Introduce parameters s, r and b sepparated by a space:");
    scanf("%lf %lf %lf",&s,&r,&b);
    printf("\n Introduce initial conditions t0,x0,y0 and z0:");
    scanf("%lf %lf %lf %lf",&ti,&x[0],&x[1],&x[2]);
    printf("\n Introduce time step:");
    scanf("%lf",&dt);
    printf("\n Specify time to compute in the equations:");
    scanf("%lf",&t);
/* Loop para Runge Kutta 4 */
    do
    {
      /*  printf("%9.4f %9.4f %9.4f %9.4f \n",ti,x[0],x[1],x[2]); */
        i++;
        for(j=0;j<N;j++)
        {
            K1[j] = f(j,ti,x);
        }
        for(j=0;j<N;j++)
        {
            y[j] = x[j]+(K1[j]/2)*dt;
        }
        for(j=0;j<N;j++)
        {
            K2[j] = f(j,ti+dt/2,y);
        }
        for(j=0;j<N;j++)
        {
            y[j] = x[j]+(K2[j]/2)*dt;
        }
        for(j=0;j<N;j++)
        {
            K3[j] = f(j,ti+dt/2,y);
        }
        for(j=0;j<N;j++)
        {
            y[j] = x[j]+(K3[j])*dt;
        }
        for(j=0;j<N;j++)
        {
            K4[j] = f(j,ti+dt,y);
        }
        for(j=0;j<N;j++)
        {
            x[j] += dt*((K1[j]/6)+(K2[j]/3)+(K3[j]/3)+(K4[j]/6));
        }
        ti +=dt;
        fprintf(output,"%9.4f %9.4f %9.4f \n",x[0],x[1],x[2]);
    }while(i*dt <= t);
    fclose(output);
    return 0;
}
/* Definimos la funcion */
double f(int m,double h,double x[])
{
    if(m==0)
    {
        return s*(x[1]-x[0]);
    }
    else if(m==1)
    {
        return x[0]*(r-x[2])-x[1];
    }
    else if(m==2)
    {
        return x[0]*x[1]-b*x[2];
    }
}

заранее спасибо

ИЗМЕНИТЬ

Я снова написал код (более упрощенным способом) в Linux, и он работает нормально, кажется, мой редактор в Windows имел что-то (возможно, кодировку), из-за чего, когда я компилировал код, он выдавал много бесконечных значений. Не знаю почему, и мне потребовалось много времени, чтобы заметить это. Спасибо за вашу помощь


person PerroNoob    schedule 18.02.2012    source источник
comment
В чем смысл двойного h в качестве входных данных для функции f? Похоже, не привыкает. Это необходимо?   -  person    schedule 20.02.2012
comment
мм нет, я поставил h просто для того, чтобы сделать код более обобщенным, на тот случай, если дифференциальные уравнения явно зависят от t   -  person PerroNoob    schedule 20.02.2012


Ответы (3)


Линии, похожие на

for(j=0;j<N;j++)
{
    y[j] = x[j]+(K1[j]/2)*dt;
}

не правы.

Это должно выглядеть так,

for(j=0;j<N;j++)
{
    K1[j] = f(j,ti,x[j],y[j]);
    L1[j] = g(j,ti,x[j],y[j]);
}
for(j=0;j<N;j++)
{
    K2[j] = f(j,ti+dt/2,x+k1[j]/2,y+L1[j]/2);
    L2[j] = g(j,ti+dt/2,x+k1[j]/2,y+L1[j]/2);
}
for(j=0;j<N;j++)
{
    K3[j] = f(j,ti+dt/2,x+K2[j]/2,y+L2[j]/2);
    L3[j] = g(j,ti+dt/2,x+K2[j]/2,y+L2[j]/2);
}
for(j=0;j<N;j++)
{
    K4[j] = f(j,ti+dt,x+K3[j],y+L3[j]);
    L4[j] = g(j,ti+dt,x+K3[j],y+L3[j]);
}
for(j=0;j<N;j++)
{
    x[j] += dt*((K1[j]/6)+(K2[j]/3)+(K3[j]/3)+(K4[j]/6));
    y[j] += dt*((L1[j]/6)+(L2[j]/3)+(L3[j]/3)+(L4[j]/6));
}

Рунге-Кутта работает следующим образом.

У вас есть система уравнений.

dx/dt = f(t,x,y) dy/dt = g(t,x,y)

Для каждого аргумента функций вам нужен подшаг Рунге-Кутты (k1, k2 и т. д.). Таким образом, для каждого подшага «k» вам нужны обновленные значения «подшага» x и y. В качестве примера для 2-го подэтапа x+k1/2 и y+l1/2.

person user1139069    schedule 18.02.2012
comment
Ммм... но я делаю это, учитывая компонент j функции f. Если вы видите в конце кода, f имеет 3 компонента. И то же самое касается x, у меня есть x[j], учитывая, что у меня есть x,y,z в уравнениях (x[0], x[1], x[2]). Если я напишу код, как вы говорите, циклов for не должно быть, я думаю - person PerroNoob; 18.02.2012
comment
Это очень запутанно, как вы это написали, но я вижу, что вы делаете сейчас. Проблема в том, что вы дважды умножаете на dt. В строке y[j] = x[j]+(K3[j])*dt; вы умножаете на dt, поэтому в этой строке x[j] += dt*((K1[j]/6)+(K2[j]/3)+(K3[j]/3)+(K4[j]/6)); вам не нужно умножать на dt. - person user1139069; 19.02.2012
comment
Мм, я думаю, мне нужно это сделать, потому что y[j] - это просто «обновленный» x[], который должен быть оценен внутри f() - person PerroNoob; 20.02.2012
comment
Если бы я делал это, я бы переместился в dt сюда K1[j] = dt*f(j,ti,x); и умножил бы на dt только один раз. Это гарантирует, что ваши k будут иметь те же единицы, что и ваши x. Во-вторых, поскольку вы вычисляете позиции x, y и z, вместо использования массива я бы использовал x, y и z. Это сделало бы код намного более читабельным. Ваша функция на самом деле состоит из трех функций. Таким образом, вместо использования операторов if было бы эффективнее и понятнее записать их в виде отдельных функций, принимающих x, y и z в качестве входных данных. Это также сделало бы код более читабельным, а также устранило бы все циклы for в вашем цикле do. - person user1139069; 20.02.2012
comment
Я упорно делал это. Я использовал массивы, думая, что мог бы сделать более общий код, но, похоже, он работает плохо, я не знаю почему, потому что я все проверял много раз. Но я сделаю то, что ты говоришь, так лучше. Спасибо за вашу помощь :) - person PerroNoob; 20.02.2012
comment
Ошибка, которую вы нашли, на самом деле не является ошибкой. Циклы, которые вы называете неправильными, на самом деле создают состояние y=x+0.5*dt*K1, которое необходимо на следующем этапе метода RK. В C нет векторно-арифметических операций на лету. Ваш исправленный код не имеет смысла, поскольку вы пытаетесь добавить приращения с плавающей запятой к указателю массива x. Кроме того, функции могут зависеть от всего вектора состояния, а не только от jth компонента. - person Lutz Lehmann; 15.06.2018
comment
Ваш комментарий не имеет смысла. - person user1139069; 18.06.2018

Ну, игнорируя любую другую проблему, где инициализируется переменная «i»?

person Jxj    schedule 18.02.2012
comment
Это даже не должно компилироваться; double не имеет оператора ++ - person Seth Carnegie; 18.02.2012
comment
@SethCarnegie У меня есть (по крайней мере) два компилятора, которые его поддерживают. Впрочем, как-то странно… - person justin; 18.02.2012
comment
О, я этого не заметил, но в любом случае код все равно выдает неверный результат - person PerroNoob; 18.02.2012
comment
for(j=0;j<N;j++) { y[j] = x[j]+(K3[j]/2)*dt; } тоже неверно, я считаю, не должно ли быть for(j=0;j<N;j++) { y[j] = x[j]+K3[j]*dt; }? - person Jxj; 18.02.2012
comment
Пробовал скомпилировать под линуксом и памяти сильно прибавилось, думаю у меня цикл неправильно написан - person PerroNoob; 19.02.2012

Я не думаю, что вы ошиблись, за исключением того, что N=3 означает, что распределение x, y и Ks должно быть

x[3], y[3], K1[3], ...

вместо double x[2],dt,y[2],K1[2],K2[2],K3[2],K4[2],ti,t,i;

person GaryX    schedule 23.07.2013