Java 中微分方程组的 Runge-Kutta (RK4)
这个方程主要是这个线程的结果:Java 中的微分方程。
基本上,我尝试遵循 Jason S. 的建议,并通过 Runge-Kutta 方法 (RK4) 实现微分方程的数值解。
大家好, 我正在尝试用java创建一个简单的SIR流行病模型模拟程序。 基本上,SIR 由三个微分方程组定义:
S'(t) = - lamda(t) * S(t)
I'(t) = lamda(t) * S(t) - gamma(t) * I(t)
R'(t) = gamma(t) * I(t)
S——易感人群,I——感染人群,R——康复人群。 lamda(t) = [c * x * I(t)] / N(T) c - 接触人数,x - 传染性(与病人接触后生病的概率),N(t) - 总人口(恒定)。
gamma(t) = 1 / 患病持续时间(常数)
在第一次不太成功的尝试之后,我尝试用 Runge-KUtta 求解这个方程,这次尝试产生了以下代码:
package test;
public class Main {
public static void main(String[] args) {
double[] S = new double[N+1];
double[] I = new double[N+1];
double[] R = new double[N+1];
S[0] = 99;
I[0] = 1;
R[0] = 0;
int steps = 100;
double h = 1.0 / steps;
double k1, k2, k3, k4;
double x, y;
double m, n;
double k, l;
for (int i = 0; i < 100; i++) {
y = 0;
for (int j = 0; j < steps; j++) {
x = j * h;
k1 = h * dSdt(x, y, S[j], I[j]);
k2 = h * dSdt(x+h/2, y +k1/2, S[j], I[j]);
k3 = h * dSdt(x+h/2, y+k2/2, S[j], I[j]);
k4 = h * dSdt(x+h, y + k3, S[j], I[j]);
y += k1/6+k2/3+k3/3+k4/6;
}
S[i+1] = S[i] + y;
n = 0;
for (int j = 0; j < steps; j++) {
m = j * h;
k1 = h * dIdt(m, n, S[j], I[j]);
k2 = h * dIdt(m+h/2, n +k1/2, S[j], I[j]);
k3 = h * dIdt(m+h/2, n+k2/2, S[j], I[j]);
k4 = h * dIdt(m+h, n + k3, S[j], I[j]);
n += k1/6+k2/3+k3/3+k4/6;
}
I[i+1] = I[0] + n;
l = 0;
for (int j = 0; j < steps; j++) {
k = j * h;
k1 = h * dRdt(k, l, I[j]);
k2 = h * dRdt(k+h/2, l +k1/2, I[j]);
k3 = h * dRdt(k+h/2, l+k2/2, I[j]);
k4 = h * dRdt(k+h, l + k3, I[j]);
l += k1/6+k2/3+k3/3+k4/6;
}
R[i+1] = R[i] + l;
}
for (int i = 0; i < 100; i ++) {
System.out.println(S[i] + " " + I[i] + " " + R[i]);
}
}
public static double dSdt(double x, double y, double s, double i) {
return (- c * x * i / N) * s;
}
public static double dIdt(double x, double y, double s, double i) {
return (c * x * i / N) * s - g * i;
}
public static double dRdt(double x, double y, double i) {
return g*i;
}
private static int N = 100;
private static int c = 5;
private static double x = 0.5;
private static double g = (double) 1 / x;
}
这似乎不起作用,因为患病人数(I)应先增加,然后减少至0左右,康复人数应严格增加。生病+健康+康复的总数应该是100,但是我的代码产生了一些奇怪的结果:
99.0 1.0 0.0
98.9997525 0.9802475 0.03960495
98.99877716805084 0.9613703819491656 0.09843730763898331
98.99661215494893 0.9433326554629141 0.1761363183872249
98.99281287394908 0.9261002702516101 0.2723573345404987
98.98695085435723 0.9096410034385773 0.3867711707625441
98.97861266355956 0.8939243545756241 0.5190634940761019
98.96739889250752 0.8789214477384787 0.6689342463444292
98.95292320009872 0.8646049401404658 0.8360970974155659
98.93481141227473 0.8509489367528628 1.0202789272217598
98.91270067200323 0.8379289104653137 1.22121933523726
98.8862386366277 0.8255216273600343 1.438670175799961
98.8550827193552 0.8137050767097959 1.672395117896858
我找不到错误,请指教!非常感谢!
This quation is mostly a result of this thread: Differential Equations in Java.
Basically, I've tried to follow Jason S. advise and to implement numerical solutions to differential equations via Runge-Kutta method (RK4).
Hello everyone,
I am trying to create a simple simulation program of SIR-epidemics model in java.
Basically, SIR is defined by a system of three differential equations:
S'(t) = - lamda(t) * S(t)
I'(t) = lamda(t) * S(t) - gamma(t) * I(t)
R'(t) = gamma(t) * I(t)
S - susceptible people, I - infected people, R - recovered people.
lamda(t) = [c * x * I(t)] / N(T)
c - number of contacts, x - infectiveness (probability to get sick after contact with sick person), N(t) - total population (which is constant).
gamma(t) = 1 / duration of illness (constant)
After first not very successful attempt, I've tried to solve this equations with Runge-KUtta, and this attempt resulting in the following code:
package test;
public class Main {
public static void main(String[] args) {
double[] S = new double[N+1];
double[] I = new double[N+1];
double[] R = new double[N+1];
S[0] = 99;
I[0] = 1;
R[0] = 0;
int steps = 100;
double h = 1.0 / steps;
double k1, k2, k3, k4;
double x, y;
double m, n;
double k, l;
for (int i = 0; i < 100; i++) {
y = 0;
for (int j = 0; j < steps; j++) {
x = j * h;
k1 = h * dSdt(x, y, S[j], I[j]);
k2 = h * dSdt(x+h/2, y +k1/2, S[j], I[j]);
k3 = h * dSdt(x+h/2, y+k2/2, S[j], I[j]);
k4 = h * dSdt(x+h, y + k3, S[j], I[j]);
y += k1/6+k2/3+k3/3+k4/6;
}
S[i+1] = S[i] + y;
n = 0;
for (int j = 0; j < steps; j++) {
m = j * h;
k1 = h * dIdt(m, n, S[j], I[j]);
k2 = h * dIdt(m+h/2, n +k1/2, S[j], I[j]);
k3 = h * dIdt(m+h/2, n+k2/2, S[j], I[j]);
k4 = h * dIdt(m+h, n + k3, S[j], I[j]);
n += k1/6+k2/3+k3/3+k4/6;
}
I[i+1] = I[0] + n;
l = 0;
for (int j = 0; j < steps; j++) {
k = j * h;
k1 = h * dRdt(k, l, I[j]);
k2 = h * dRdt(k+h/2, l +k1/2, I[j]);
k3 = h * dRdt(k+h/2, l+k2/2, I[j]);
k4 = h * dRdt(k+h, l + k3, I[j]);
l += k1/6+k2/3+k3/3+k4/6;
}
R[i+1] = R[i] + l;
}
for (int i = 0; i < 100; i ++) {
System.out.println(S[i] + " " + I[i] + " " + R[i]);
}
}
public static double dSdt(double x, double y, double s, double i) {
return (- c * x * i / N) * s;
}
public static double dIdt(double x, double y, double s, double i) {
return (c * x * i / N) * s - g * i;
}
public static double dRdt(double x, double y, double i) {
return g*i;
}
private static int N = 100;
private static int c = 5;
private static double x = 0.5;
private static double g = (double) 1 / x;
}
This does not seem to work, because number of sick people (I) should first increse, and then dicrease to about 0, and number of recovered people should strictly increase. Total number of sick + healthy + recovered should be 100, but my code produces some strange results:
99.0 1.0 0.0
98.9997525 0.9802475 0.03960495
98.99877716805084 0.9613703819491656 0.09843730763898331
98.99661215494893 0.9433326554629141 0.1761363183872249
98.99281287394908 0.9261002702516101 0.2723573345404987
98.98695085435723 0.9096410034385773 0.3867711707625441
98.97861266355956 0.8939243545756241 0.5190634940761019
98.96739889250752 0.8789214477384787 0.6689342463444292
98.95292320009872 0.8646049401404658 0.8360970974155659
98.93481141227473 0.8509489367528628 1.0202789272217598
98.91270067200323 0.8379289104653137 1.22121933523726
98.8862386366277 0.8255216273600343 1.438670175799961
98.8550827193552 0.8137050767097959 1.672395117896858
I cannot find a mistake, please advise! Many thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我发现这不是一个真正的编程问题,但无论如何我都会回答。
快速浏览一下我会尝试两件事:
假设你的时间单位是天,目前你似乎正在评估第一天之后的情况(如果我错了,请纠正我)。对于您所介绍的案例,我认为您想了解几天内的演变情况。所以你必须增加你的循环数或者你的时间步长(但要小心)
其次,你似乎在这里犯了一个错误: c * x * i / N... 不应该是 (c*x*i )/N?检查这是否有影响。我认为你可以通过 S' + I' + R' 应该 = 0 的事实来检查......
再一次,我没有对此进行深入检查,但只是看一下,让我知道它是否改变了任何东西。
Not a real programming problem I find, but I'll answer anyway.
From a quick look I would try two things:
Assuming your time unit is days, at the moment you seem to be evaluating what the situation is after day 1 (correct me if I'm wrong here). For the case you're presenting I think you want to know the evolution over several days. So you have to increase your number of loops or perhaps your timestep (but be careful with that)
Second, you seem to have a mistake here: c * x * i / N... should that not be (c*x*i)/N? Check if that makes a difference. And I think you can check by the fact that S' + I' + R' should = 0...
Once again, I did not check this very deeply, but just have a look and let me know if it changes anything.