如何在NDSolve中引用我的函数的特定点?

时间:2011-08-07 18:46:36

标签: wolfram-mathematica differential-equations equation-solving

问题:

我正试图解决这个差异方程:

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 - 它取决于xx1(以复杂的方式)。<登记/> 注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事后不解决。

3 个答案:

答案 0 :(得分:7)

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

首先,很明显,公式可以在x上整合两次,首先从1x,然后从0x ,那样:

enter image description here

我们现在可以将这个等式离散化,将它放在等距网格上:

enter image description here

这里,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&

这是生成内核矩阵和r.h,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找到的精确解析解以及错误:

enter image description here

我故意选择delta足够大,以便错误可见。如果您选择delta0.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.}},<>]

enter image description here

以下是一些检查:

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或其他方法迭代地改进它 - 这样您将获得相对快速的收敛并且能够达到所需的精度无需构建和解决巨大的矩阵。

答案 1 :(得分: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}

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

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

(1)如果这不能很好地收敛,因为初始线性近似不足以接近真实结果,则可能反而从有限差分方案找到的近似开始。那将类似于他的所作所为。

(2)他自己也表达了这一点,作为一种可能跟随他并产生改进的方法。

Daniel Lichtblau

答案 2 :(得分: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