解拉普拉斯方程


13

数值数学导论

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

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

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

u'' 表示的二阶导数 u

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

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

输入值

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

输出量

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

例子

例子1

输入:f = -2,u_L = u_R = 0,N = 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*x,u_L = 0 u_R = 1,N = 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 = 1,N = 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 = 0,N=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.