解拉普拉斯方程


13

数值数学导论

这就是“你好,世界!” PDE(偏微分方程)。拉普拉斯方程或扩散方程经常出现在物理学中,例如热方程,变形,流体动力学等。由于现实生活是3D,但我们要说的是“世界,你好!” 而不是唱“ 99瓶啤酒,...”,此任务以1D形式给出。您可以将其解释为两端都绑在墙上的橡胶长袍。

在一个[0,1]域中找到一个函数u在给定源功能f和边界值u_Lu_R这样的:

  • -u'' = f
  • u(0) = u_L
  • u(1) = u_R

u'' 表示的二阶导数 u

这是可以解决的纯粹theoritical但你的任务是它数值求解一个离散域XN要点:

  • x = {i/(N-1) | i=0..N-1}或从1开始:{(i-1)/(N-1) | i=1..N}
  • h = 1/(N-1) 是间距

输入值

  • f 作为函数或表达式或字符串
  • u_Lu_R作为浮点值
  • N 作为整数> = 2

输出量

  • 阵列,列表,某种形式的分离式串u这样的u_i == u(x_i)

例子

例子1

输入:f = -2u_L = u_R = 0N = 10(不要拿f=-2错了,它不是一个值,而是一个常数函数,返回-2所有x它就像我们的绳子恒定的重力。)

输出: [-0.0, -0.09876543209876543, -0.1728395061728395, -0.22222222222222224, -0.24691358024691357, -0.24691358024691357, -0.22222222222222224, -0.1728395061728395, -0.09876543209876547, -0.0]

有一个简单的精确解决方案: u = -x*(1-x)

例子2

输入:f = 10*xu_L = 0 u_R = 1N = 15(这里有右侧很多上风的)

输出: [ 0., 0.1898688, 0.37609329, 0.55502915, 0.72303207, 0.87645773, 1.01166181, 1.125, 1.21282799, 1.27150146, 1.29737609, 1.28680758, 1.2361516, 1.14176385, 1.]

确切的解决方案指出: u = 1/3*(8*x-5*x^3)

例子3

输入:f = sin(2*pi*x)u_L = u_R = 1N = 20(有人打破了重力或有一种向上和顺风)

输出: [ 1., 1.0083001, 1.01570075, 1.02139999, 1.0247802, 1.0254751, 1.02340937, 1.01880687, 1.01216636, 1.00420743, 0.99579257, 0.98783364, 0.98119313, 0.97659063, 0.9745249, 0.9752198, 0.97860001, 0.98429925, 0.9916999, 1.]

这里的确切解决方案是 u = (sin(2*π*x))/(4*π^2)+1

例子4

输入:f = exp(x^2)u_L = u_R = 0N=30

输出: [ 0. 0.02021032 0.03923016 0.05705528 0.07367854 0.0890899 0.10327633 0.11622169 0.12790665 0.13830853 0.14740113 0.15515453 0.16153488 0.1665041 0.17001962 0.172034 0.17249459 0.17134303 0.16851482 0.1639387 0.15753606 0.1492202 0.13889553 0.12645668 0.11178744 0.09475961 0.07523169 0.05304738 0.02803389 0. ]

注意轻微的不对称

FDM

解决此问题的一种可能方法是有限差分法

  • 改写-u_i'' = f_i
  • (-u_{i-1} + 2u_i - u{i+1})/h² = f_i 等于
  • -u_{i-1} + 2u_i - u{i+1} = h²f_i
  • 设置方程式:

  • 等于矩阵向量方程式:

  • 求解此方程并输出 u_i

一个用Python演示的实现:

import matplotlib.pyplot as plt
import numpy as np
def laplace(f, uL, uR, N):
 h = 1./(N-1)
 x = [i*h for i in range(N)]

 A = np.zeros((N,N))
 b = np.zeros((N,))

 A[0,0] = 1
 b[0] = uL

 for i in range(1,N-1):
  A[i,i-1] = -1
  A[i,i]   =  2
  A[i,i+1] = -1
  b[i]     = h**2*f(x[i])

 A[N-1,N-1] = 1
 b[N-1]     = uR

 u = np.linalg.solve(A,b)

 plt.plot(x,u,'*-')
 plt.show()

 return u

print laplace(lambda x:-2, 0, 0, 10)
print laplace(lambda x:10*x, 0, 1, 15)
print laplace(lambda x:np.sin(2*np.pi*x), 1, 1, 20)

没有矩阵代数的替代实现(使用Jacobi方法

def laplace(f, uL, uR, N):
 h=1./(N-1)
 b=[f(i*h)*h*h for i in range(N)]
 b[0],b[-1]=uL,uR
 u = [0]*N

 def residual():
  return np.sqrt(sum(r*r for r in[b[i] + u[i-1] - 2*u[i] + u[i+1] for i in range(1,N-1)]))

 def jacobi():
  return [uL] + [0.5*(b[i] + u[i-1] + u[i+1]) for i in range(1,N-1)] + [uR]

 while residual() > 1e-6:
  u = jacobi()

 return u

但是,您可以使用任何其他方法来求解拉普拉斯方程。如果您使用的迭代方法,你应该迭代,直到残留|b-Au|<1e-6,与b被右手边矢量u_L,f_1h²,f_2h²,...

笔记

根据您的解决方案方法,您可能无法完全根据给定的解决方案来解决示例。至少对于N->infinity误差应该接近零。

不允许出现标准漏洞,允许内置PDE。

奖金

显示解决方案(图形或ASCII艺术形式)的奖励为-30%。

获奖

这是codegolf,因此以字节为单位的最短代码胜出!


我建议添加一个无法解析的示例,例如使用f(x) = exp(x^2)
瑕疵的

@flawr当然,它有解决方案,但是涉及错误功能。
Karl Napf '16

1
抱歉,这可能是错误的表达方式,“非基本反导”是否更适合?我的意思是像这样的函数log(log(x))sqrt(1-x^4)确实具有积分的函数,但是在基本函数中无法表达。
瑕疵的

@flawr否,这很好,错误函数不是基本函数,我只是想说有一种方法可以解析地表示解决方案,但u(x) = 1/2 (-sqrt(π) x erfi(x)+sqrt(π) erfi(1) x+e^(x^2)-e x+x-1)无法精确计算。
Karl Napf '16

为什么要迭代到1e-6而不迭代到1e-30?
RosLuP

Answers:


4

Mathematica,52.5字节(= 75 *(1-30%))

每个@flawr的评论+0.7字节。

ListPlot[{#,u@#}&/@Subdivide@#4/.NDSolve[-u''@x==#&&u@0==#2&&u@1==#3,u,x]]&

这将绘制输出。

例如

ListPlot[ ... ]&[10 x, 0, 1, 15]

在此处输入图片说明

说明

NDSolve[-u''@x==#&&u@0==#2&&u@1==#3,u,x]

解决功能u

Subdivide@#4

Subdivide 间隔[0,1]分为N个(第4个输入)部分。

{#,u@#}&/@ ...

映射u到的输出Subdivide

ListPlot[ ... ]

绘制最终结果。

非绘图解决方案:58个字节

u/@Subdivide@#4/.NDSolve[-u''@x==#&&u@0==#2&&u@1==#3,u,x]&

这不适用于f(x) = exp(x^2)
更加模糊的

也许您可能想将NDSolve非基本解决方案用于一般情况。
瑕疵的

6

Matlab,84,81.2 79.1字节= 113-30%

function u=l(f,N,a,b);A=toeplitz([2,-1,(3:N)*0]);A([1,2,end-[1,0]])=eye(2);u=[a,f((1:N-2)/N)*(N-1)^2,b]/A;plot(u)

请注意,在此示例中,我使用行向量,这意味着矩阵A已转置。f用作功能手柄,a,b是左侧/右侧Dirichlet约束。

function u=l(f,N,a,b);
A=toeplitz([2,-1,(3:N)*0]);       % use the "toeplitz" builtin to generate the matrix
A([1,2,end-[1,0]])=eye(2);        % adjust first and last column of matrix
u=[a,f((1:N-2)/N)*(N-1)^2,b]/A;   % build right hand side (as row vector) and right mu
plot(u)                           % plot the solution

对于此示例,f = 10*x, u_L = 0 u_R = 1, N = 15结果为:


3

R,123.2 102.9 98.7字节(141-30%)

编辑:感谢@Angs节省了几个字节!

如果有人要编辑图片,请随意进行。这基本上是对发布的matlab和python版本的R改编。

function(f,a,b,N){n=N-1;x=1:N/n;A=toeplitz(c(2,-1,rep(0,N-2)));A[1,1:2]=1:0;A[N,n:N]=0:1;y=n^-2*sapply(x,f);y[1]=a;y[N]=b;plot(x,solve(A,y))}

取消说明:

u=function(f,a,b,N){
    n=N-1;                                              # Alias for N-1
    x=0:n/n;                                            # Generate the x-axis
    A=toeplitz(c(2,-1,rep(0,N-2)));                     # Generate the A-matrix
    A[1,1:2]=1:0;                                       # Replace first row--
    A[N,n:N]=0:1;                                       # Replace last row
    y=n^-2*sapply(x,f)                                  # Generate h^2*f(x)
    y[1]=a;y[N]=b;                                      # Replace first and last elements with uL(a) and uR(b)
    plot(x,solve(A,y))                                  # Solve the matrix system A*x=y for x and plot against x 
}

示例和测试案例:

可以使用以下命令调用已命名和未调用的函数:

u(function(x)-2,0,0,10)
u(function(x)x*10,0,1,15)
u(function(x)sin(2*pi*x),1,1,20)
u(function(x)x^2,0,0,30)

请注意,该f参数是R函数。

要运行高尔夫版本,只需使用 (function(...){...})(args)

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明


我认为,is.numeric(f)如果您声明f为函数,则可以删除该检查,不需要直接在函数调用中将其传递给求解程序。
Karl Napf

我明白了,我不知道两者之间有什么区别。好吧,如果它更短,则可以修改您的求解器以接受f为函数,因此您不必检查它是否为常数(函数)的情况。
Karl Napf

1
@Billywob根本不需要f数字。f = (function(x)-2)适用于第一个示例,因此不需要rep
2016年

您可以使用x<-0:10/10;f<-function(x){-2};10^-2*sapply(x,f)若f(x)不是quaranteed要矢量或只是10^-2*f(x)如果f被矢量(laplace(Vectorize(function(x)2),0,0,10
Angs

不要使用eval,将其f作为适当的功能给出。
2016年

2

Haskell,195168字节

import Numeric.LinearAlgebra
p f s e n|m<-[0..]!!n=((n><n)(([1,0]:([3..n]>>[[-1,2,-1]])++[[0,1]])>>=(++(0<$[3..n]))))<\>(col$s:map((/(m-1)^2).f.(/(m-1)))[1..m-2]++[e])

可读性大受打击。取消高尔夫:

laplace f start end _N = linearSolveLS _M y
  where
  n = fromIntegral _N
  _M = (_N><_N) --construct n×n matrix from list
        ( ( [1,0]           --make a list of [1,0]
          : ([3.._N]>>[[-1,2,-1]]) --         (n-2)×[-1,2,-1]
          ++ [[0,1]])       --               [0,1]
        >>= (++(0<$[3.._N])) --append (n-2) zeroes and concat
        )                   --(m><n) discards the extra zeroes at the end
  h  = 1/(n-1) :: Double
  y  = asColumn . fromList $ start : map ((*h^2).f.(*h)) [1..n-2] ++ [end]

待办事项:打印83 71字节。

我看

import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Cairo

天哪!


我对Haskell不太了解,但是也许没有矩阵代数的解决方案可能会更短,我添加了第二个示例实现。
Karl Napf '16

@KarlNapf距离不是很近这只是半高尔夫球,但是它必须使用许多详细的内置函数。使用矩阵代数,大多数代码将构建矩阵(64字节)和导入(29字节)。残差和雅各比占据了很大空间。
昂斯,2016年

好吧,太糟糕了,但是值得一试。
Karl Napf '16

1

公理,579460字节

l(w,y)==(r:=0;for i in 1..y|index?(i,w)repeat r:=i;r)
g(z:EQ EXPR INT,y:BasicOperator,a0:Float,a1:Float,a2:Float):Float==(r:=digits();digits(r+30);q:=seriesSolve(z,y,x=0,[a,b])::UTS(EXPR INT,x,0);w:=eval(q,0);s:=l(w,r+30);o:=solve([w.s=a0,eval(q,1).s=a1]::List(EQ POLY Float),[a,b]);v:=eval(eval(eval(q,a2).s,o.1.1),o.1.2);digits(r);v)
m(z:EXPR INT,a0:Float,a1:Float,n:INT):List Float==(n:=n-1;y:=operator 'y;r:=[g(D(y x,x,2)=-z,y,a0,a1,i/n)for i in 0..n];r)

解开并测试

Len(w,y)==(r:=0;for i in 1..y|index?(i,w)repeat r:=i;r)

-- g(z,a0,a1,a2)
-- Numeric solve z as f(y''(x),y'(x),y(x))=g(x) with ini conditions y(0)=a0   y(1)=a1 in x=a2
NSolve2order(z:EQ EXPR INT,y:BasicOperator,a0:Float,a1:Float,a2:Float):Float==
      r:=digits();digits(r+30)
      q:=seriesSolve(z,y,x=0,[a,b])::UTS(EXPR INT,x,0)
      w:=eval(q,0);s:=Len(w,r+30)
      o:=solve([w.s=a0,eval(q,1).s=a1]::List(EQ POLY Float),[a,b])
      v:=eval(eval(eval(q,a2).s,o.1.1),o.1.2);digits(r)
      v

InNpoints(z:EXPR INT,a0:Float,a1:Float,n:INT):List Float==(n:=n-1;y:=operator 'y;r:=[NSolve2order(D(y x,x,2)=-z,y,a0,a1,i/n)for i in 0..n];r)

问题的函数是m(,,,)上面的代码放在文件“ file.input”中并加载到Axiom中。结果取决于digits()函数。

如果有人认为它不打高尔夫球=>他或她可以演示如何做...谢谢

聚苯乙烯

看来后面还有6个数字。因为e ^(x ^ 2)在这里或示例中都不行,但在这里我增加了数字,但数字没有改变...对我来说,这意味着示例中的数字是错误的。为什么其他所有人都没有显示他们的数字?

对于sin(2 *%pi * x)也有问题

“这里的精确解是u =(sin(2 *π* x))/(4 *π^ 2)+1”,我复制了x = 1/19的精确解:

              (sin(2*π/19))/(4*π^2)+1

在WolframAlpha的 https://www.wolframalpha.com/input/?i=(sin(2 %CF%80%2F19))%2F(4%CF%80%5E2)%2B1它导致

1.008224733636964333380661957738992274267070440829381577926...
1.0083001
  1234
1.00822473

建议的结果1.0083001与实际结果1.00822473 ...的第4位数字不同(不是第6位)

-- in interactive mode
(?) -> )read  file
(10) -> digits(9)
   (10)  10
                                                        Type: PositiveInteger
(11) -> m(-2,0,0,10)
   (11)
   [0.0, - 0.0987654321, - 0.172839506, - 0.222222222, - 0.24691358,
    - 0.24691358, - 0.222222222, - 0.172839506, - 0.098765432, 0.0]
                                                             Type: List Float
(12) -> m(10*x,0,1,15)
   (12)
   [0.0, 0.189868805, 0.376093294, 0.555029155, 0.72303207, 0.876457726,
    1.01166181, 1.125, 1.21282799, 1.27150146, 1.29737609, 1.28680758,
    1.2361516, 1.14176385, 1.0]
                                                             Type: List Float
(13) -> m(sin(2*%pi*x),1,1,20)
   (13)
   [1.0, 1.00822473, 1.01555819, 1.02120567, 1.0245552, 1.02524378, 1.02319681,
    1.0186361, 1.01205589, 1.00416923, 0.99583077, 0.987944112, 0.981363896,
    0.976803191, 0.97475622, 0.975444804, 0.978794326, 0.98444181, 0.991775266,
    1.0]
                                                         Type: List Float
(14) -> m(exp(x^2),0,0,30)
   (14)
   [0.0, 0.0202160702, 0.0392414284, 0.0570718181, 0.0737001105, 0.0891162547,
    0.103307204, 0.116256821, 0.127945761, 0.138351328, 0.147447305,
    0.155203757, 0.161586801, 0.166558343, 0.170075777, 0.172091643,
    0.172553238, 0.171402177, 0.168573899, 0.163997099, 0.157593103,
    0.149275146, 0.13894757, 0.126504908, 0.111830857, 0.0947971117,
    0.0752620441, 0.0530692118, 0.0280456602, - 0.293873588 E -38]
                                                             Type: List Float

数值解与精确解有所不同,因为此处的FDM仅为二阶,这意味着只能精确表示2阶以下的多项式。因此,只有f=-2示例具有匹配的解析和数值解。
Karl Napf

如果我将数字更改为80或70-> g(sin(2 *%pi * x),1,1,1 / 19)1.0082247336 3696433338 0661957738 9922742670 7044082938 1577926908 950765832其他数字1.0082247336 3696433338 0661957738 9922742670 7044082938 1577926 ...
RosLuP
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.