ラプラス方程式を解く


13

数値数学入門

これが「Hello、World!」です のPDE(偏微分方程式)。ラプラス方程式または拡散方程式は、物理学によく登場します。たとえば、熱方程式、変形、流体力学などです。実生活は3Dですが、「こんにちは、世界!」と言いたいのです。「99本のビール...」を歌わないでください。このタスクは1Dで行われます。これは、両端の壁に何らかの力が加えられたゴム製のローブと解釈できます。

上の[0,1]ドメイン機能を見つけるu与えられたソース関数のf境界値u_Lu_R、そのようなことを:

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

u'' の二次導関数を示します u

これは純粋に理論的に解くことができますが、あなたの仕事は点の離散化された領域xで数値的に解くことですN

  • 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(DOは服用しないで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

これを解決する1つの可能な方法は、有限差分法です。

  • -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でのデモンストレーション用のこれの1つの実装:

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

@flawr確かに、解決策はありますが、エラー関数が関係しています。
カールナップ

1
申し訳ありませんが、それはおそらく間違った表現でしたが、「非元素のアンチデリバティブ」がより適しているのでしょうか?私のような平均値の関数log(log(x))sqrt(1-x^4)しかし、初等関数では表現できません整数を、持っています。
flawr

@flawrいいえ、それは問題ありません。エラー関数は初歩的なものでu(x) = 1/2 (-sqrt(π) x erfi(x)+sqrt(π) erfi(1) x+e^(x^2)-e x+x-1)はありません。解を分析的に表現する方法がありますが、正確に計算することはできません。
カールナップ

なぜ1e-6まで反復し、1e-30まで反復しないのですか?
RosLuP

回答:


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)
flawr

おそらくNDSolve、非基本解の一般的なケースに使用したいかもしれません。
flawr

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ます。左側/右側のディリクレ制約です。

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))}

Ungolfed&説明:

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 
}

例とテストケース:

名前付きであり、ungolfedされていない関数は以下を使用して呼び出すことができます

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。ソルバーへの関数呼び出しで直接渡す必要はありません。
カールナップ

ああ、私はそれらの間に違いがあることを知りませんでした。短い場合は、ソルバーを変更fして関数として受け入れることができるため、定数(関数)であるかどうかを確認する必要はありません。
カールナップ

1
@Billywob f数値にする必要はありません。f = (function(x)-2)最初の例で機能するため、する必要はありませんrep
アンス

あなたは使用することができ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、適切な関数として指定してください。
アンス

2

Haskell、195 168バイト

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]

TODO:83 71バイトで印刷。

レム参照:

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

どっ!


Haskellについてはあまり知りませんが、おそらく行列代数のないソリューションの方が短いかもしれません。2番目のサンプル実装を追加しました。
カールナップ

@KarlNapf はそれほど近づいていませんこれはセミゴルフに過ぎませんが、多くの冗長な組み込み関数を使用する必要があります。行列代数では、ほとんどのコードが行列(64バイト)とインポート(29バイト)を構築しています。残差とジャコビはかなり多くのスペースを必要とします。
アンズ

まあ、あまりにも悪いが、試してみる価値があった。
カールナップ

1

公理、579 460バイト

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)

ungolf it and test

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()関数に依存します。

誰かがそれがゴルフではないと思うなら、彼または彼女はそれを行う方法を示すことができます...ありがとう

PS

それはさらに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は、実際の結果から4桁目が異なる1.00822473 ...(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次のみであるため、数値解は正確な解とは異なります。つまり、2次までの多項式のみを正確に表現できます。そのため、このf=-2例のみが一致する分析および数値解を持っています。
カールナップ

ここで、数字を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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.