4

问题:

我正在尝试解决这个微分方程:

K[x_, x1_] := 1;
NDSolve[{A''[x] == Integrate[K[x, x1] A[x1], {x1, 0, 1}], 
         A[0] == 0, A'[1] == 1}, A[x], x]

我收到错误(Function::slotnNDSolve::ndnum
(它应该返回一个等于的数字函数3/16 x^2 + 5/8 x

我正在寻找一种方法来解决这个微分方程:有没有办法以更好的形式编写它,以便 NDSolve 能够理解它?是否有其他功能或包可以提供帮助?

注 1:在我的完整问题中,K[x, x1]不是 1 - 它(以复杂的方式)取决于xand x1
注2:天真地推导方程的两侧是x行不通的,因为积分限制是确定的。

我的第一印象:

似乎Mathematica不喜欢我引用一个点A[x]——当我在做这个简化版本时会发生同样的错误:

NDSolve[{A''[x] == A[0.5], A[0] == 0, A'[1] == 1}, A[x], x]

(它应该返回一个等于 的数值函数2/11 x^2 + 7/11 x

在这种情况下,可以通过解析求解A''[x] == c,然后找到来避免这个问题c,但在我的第一个问题中,它似乎不起作用——它只会将微分方程转换为积分方程,而 (N)DSolve 之后无法求解。

4

3 回答 3

7

我可以建议一种将您的方程简化为积分方程的方法,该积分方程可以通过用矩阵近似其内核来进行数值求解,从而将积分减少为矩阵乘法。

首先,很明显,方程可以在 上积分两次x,首先从1x,然后从0x,所以:

在此处输入图像描述

我们现在可以离散化这个方程,把它放在一个等距的网格上:

在此处输入图像描述

在这里,A[x]变成向量,积分核iniIntK变成矩阵,而积分被矩阵乘法代替。然后将问题简化为线性方程组。

最简单的情况(我将在这里考虑)是内核iniIntK可以通过分析得出——在这种情况下,这种方法会非常快。这是将集成内核生成为纯函数的函数:

Clear[computeDoubleIntK]
computeDoubleIntK[kernelF_] :=
  Block[{x, x1},
   Function[
    Evaluate[
      Integrate[
         Integrate[kernelF[y, x1], {y, 1, x}] /. x -> y, {y, 0, x}] /. 
         {x -> #1, x1 -> #2}]]];

在我们的例子中:

In[99]:= K[x_,x1_]:=1;

In[100]:= kernel = computeDoubleIntK[K]
Out[100]= -#1+#1^2/2&

这是产生核矩阵和 rh,s 向量的函数:

 computeDiscreteKernelMatrixAndRHS[intkernel_, a0_, aprime1_ , 
    delta_, interval : {_, _}] :=
  Module[{grid, rhs, matrix},
    grid = Range[Sequence @@ interval, delta];
    rhs = a0 + aprime1*grid; (* constant plus a linear term *)
    matrix = 
      IdentityMatrix[Length[grid]] - delta*Outer[intkernel, grid, grid];
    {matrix, rhs}]

给出一个非常粗略的想法(我在这里使用delta = 1/2):

In[101]:= computeDiscreteKernelMatrixAndRHS[kernel,0,1,1/2,{0,1}]

Out[101]= {{{1,0,0},{3/16,19/16,3/16},{1/4,1/4,5/4}},{0,1/2,1}}

我们现在需要求解线性方程,并对结果进行插值,这是通过以下函数完成的:

Clear[computeSolution];
computeSolution[intkernel_, a0_, aprime1_ , delta_, interval : {_, _}] :=
With[{grid = Range[Sequence @@ interval, delta]},
  Interpolation@Transpose[{
    grid,
    LinearSolve @@ 
      computeDiscreteKernelMatrixAndRHS[intkernel, a0, aprime1, delta,interval]
  }]]

在这里,我将使用delta = 0.1

In[90]:= solA = computeSolution[kernel,0,1,0.1,{0,1}]
Out[90]= InterpolatingFunction[{{0.,1.}},<>] 

我们现在绘制结果与@Sasha 找到的精确解析解以及错误:

在此处输入图像描述

我故意选择了delta足够大,以便错误可见。如果您选择deltasay 0.01,则这些图在视觉上将是相同的。当然,采用更小的代价delta是需要产生和求解更大的矩阵。

对于可以通过分析获得的内核,主要瓶颈将在 中LinearSolve,但实际上它非常快(对于不太大的矩阵)。当内核无法解析集成时,主要瓶颈将是在多点计算内核(矩阵创建。矩阵逆具有更大的渐近复杂度,但这将开始对非常大的矩阵起作用 - 这在这种方法,因为它可以与迭代方法相结合——见下文)。您通常会定义:

intK[x_?NumericQ, x1_?NumericQ] := NIntegrate[K[y, x1], {y, 1, x}]
intIntK[x_?NumericQ, x1_?NumericQ] := NIntegrate[intK[z, x1], {z, 0, x}]

作为在这种情况下加快速度的一种方法,您可以intK在网格上预先计算内核,然后进行插值,对于intIntK. 然而,这会引入额外的错误,您必须估计(考虑)。

网格本身不需要是等距的(我只是为了简单起见使用它),但可能(并且可能应该)是自适应的,并且通常是不均匀的。

作为最后的说明,考虑一个具有非平凡但符号可积核的方程:

In[146]:= sinkern =  computeDoubleIntK[50*Sin[Pi/2*(#1-#2)]&]

Out[146]= (100 (2 Sin[1/2 \[Pi] (-#1+#2)]+Sin[(\[Pi] #2)/2] 
     (-2+\[Pi] #1)))/\[Pi]^2&

In[157]:= solSin = computeSolution[sinkern,0,1,0.01,{0,1}]
Out[157]= InterpolatingFunction[{{0.,1.}},<>]

在此处输入图像描述

以下是一些检查:

In[163]:= Chop[{solSin[0],solSin'[1]}]
Out[163]= {0,1.}

In[153]:= 
diff[x_?NumericQ]:=
  solSin''[x] - NIntegrate[50*Sin[Pi/2*(#1-#2)]&[x,x1]*solSin[x1],{x1,0,1}];

In[162]:= diff/@Range[0,1,0.1]

Out[162]=  {-0.0675775,-0.0654974,-0.0632056,-0.0593575,-0.0540479,-0.0474074,
   -0.0395995,-0.0308166,-0.0212749,-0.0112093,0.000369261}

最后,我只想强调,必须对此方法进行仔细的误差估计分析,而我没有这样做。

编辑

你也可以用这种方法得到初始的近似解,然后用FixedPoint或其他方法迭代改进——这样你将有一个相对较快的收敛,将能够达到所需的精度,而不需要构造和求解巨大的矩阵。

于 2011-08-07T23:45:11.373 回答
4

这是对 Leonid Shifrin 方法的补充。我们从一个线性函数开始,该函数在起始点插入值和一阶导数。我们在与给定核函数的集成中使用它。然后我们可以迭代,使用集成内核中的每个先前的近似值来进行下一个近似值。

我在下面展示了一个示例,它使用了一个比常量函数更复杂的内核。我将通过两次迭代并显示差异表。

kernel[x_, y_] := Sqrt[x]/(y^2 + 1/5)*Sin[x^2 + y]
intkern[x_?NumericQ, aa_] := 
 NIntegrate[kernel[x, y]*aa[y], {y, 0, 1}, MinRecursion -> 2, 
  AccuracyGoal -> 3]

Clear[a];
a0 = 0;
a1 = 1;
a[0][x_] := a0 + a1*x

soln1 = a[1][x] /. 
   First[NDSolve[{(a[1]^\[Prime]\[Prime])[x] == intkern[x, a[0], y], 
      a[1][0] == a0, a[1][1] == a1}, a[1][x], {x, 0, 1}]];
a[1][x_] = soln1;

In[283]:= Table[a[1]''[x] - intkern[x, a[1]], {x, 0., 1, .1}]

Out[283]= {4.336808689942018*10^-19, 0.01145100326794241, \
0.01721655945379122, 0.02313249302884235, 0.02990900241909161, \
0.03778448183557359, 0.04676409320217928, 0.05657128568058478, \
0.06665818935524814, 0.07624149919589895, 0.08412643746245929}

In[285]:= 
soln2 = a[2][x] /. 
   First[NDSolve[{(a[2]^\[Prime]\[Prime])[x] == intkern[x, a[1]], 
      a[2][0] == a0, a[2][1] == a1}, a[2][x], {x, 0, 1}]];
a[2][x_] = soln2;

In[287]:= Table[a[2]''[x] - intkern[x, a[2]], {x, 0., 1, .1}]

Out[287]= {-2.168404344971009*10^-19, -0.001009606971360516, \
-0.00152476679745811, -0.002045817184941901, -0.002645356229312557, \
-0.003343218015068372, -0.004121109614310836, -0.004977453722712966, \
-0.005846840469889258, -0.006731367269472544, -0.007404971586975062}

所以在这个阶段我们的误差小于 0.01。还不错。一个缺点是获得第二个近似值相当慢。可能有一些方法可以调整 NDSolve 以改进它。

这是对 Leonid 方法的补充,原因有两个。

(1) 如果由于初始线性逼近与真实结果不够接近而未能很好地收敛,则可以改为从有限差分方案找到的逼近开始。那将类似于他所做的。

(2) 他本人几乎已经表明了这一点,作为一种可能遵循他并产生改进的方法。

丹尼尔·利赫特布劳

于 2011-08-08T17:24:06.637 回答
0

您的方程式当前的编写方式A''[x] == const,而不是常量独立于x. 因此解总是具有二次多项式的形式。然后,您的问题简化为对不确定系数的求解:

In[13]:= A[x_] := a2 x^2 + a1 x + a0;

In[14]:= K[x_, x1_] := 1;

In[16]:= Solve[{A''[x] == Integrate[K[x, x1] A[x1], {x1, 0, 1}], 
  A[0] == 0, A'[1] == 1}, {a2, a1, a0}]

Out[16]= {{a2 -> 3/16, a1 -> 5/8, a0 -> 0}}

In[17]:= A[x] /. First[%]

Out[17]= (5 x)/8 + (3 x^2)/16
于 2011-08-07T19:49:37.323 回答