แก้สมการลาปลาส


13

คณิตศาสตร์เชิงตัวเลขเบื้องต้น

นี่คือ "สวัสดีโลก!" ของ PDEs (สมการเชิงอนุพันธ์ย่อย) Laplace หรือ Diffusion Equation ปรากฏบ่อยครั้งในวิชาฟิสิกส์เช่น Heat Equation, Deforming, Fluid Dynamics ฯลฯ ... เนื่องจากชีวิตจริงเป็น 3 มิติ แต่เราต้องการพูดว่า "Hello, World!" และไม่ร้องเพลง "99 ขวดเบียร์ ... " งานนี้มอบให้ใน 1D คุณอาจตีความว่าสิ่งนี้เป็นเสื้อคลุมยางผูกติดกับผนังทั้งสองด้านด้วยแรงบางอย่างที่ใช้กับมัน

ใน[0,1]โดเมนค้นหาฟังก์ชันuสำหรับฟังก์ชันต้นทางfและค่าขอบเขตu_Lและu_Rเช่น:

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

u'' หมายถึงอนุพันธ์อันดับสองของ u

สิ่งนี้สามารถแก้ไขได้ทางทฤษฎีล้วนๆ แต่งานของคุณคือแก้ปัญหาเป็นตัวเลขบนโดเมนที่แยกส่วนxสำหรับNจุด:

  • x = {i/(N-1) | i=0..N-1}หรือ 1-based:{(i-1)/(N-1) | i=1..N}
  • h = 1/(N-1) เป็นระยะห่าง

อินพุต

  • f เป็นฟังก์ชั่นหรือการแสดงออกหรือสตริง
  • u_L, u_Rเป็นค่าจุดลอย
  • N เป็นจำนวนเต็ม> = 2

เอาท์พุต

  • Array, List, สตริงการแยกบางอย่างของ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

โบนัส

โบนัส -30% สำหรับแสดงโซลูชันทั้งแบบกราฟิกหรือ ASCII-art

การชนะ

นี่คือ codegolf ดังนั้นรหัสที่สั้นที่สุดในหน่วยไบต์ชนะ!


f(x) = exp(x^2)ผมขอแนะนำให้เพิ่มตัวอย่างซึ่งไม่ได้แก้ปัญหาการวิเคราะห์เช่นกับ
ข้อบกพร่อง

@ flawr แน่นอนว่ามันมีทางออก แต่ฟังก์ชั่นข้อผิดพลาดที่เกี่ยวข้อง
Karl Napf

1
ขออภัยนั่นอาจเป็นการแสดงออกที่ไม่ถูกต้องอาจเป็น "antiderivative ที่ไม่ใช่ระดับประถมศึกษา" เหมาะกว่าหรือไม่ ฉันหมายถึงฟังก์ชั่นเช่น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

ทำไมทำซ้ำจนถึง 1e-6 และไม่ทำซ้ำจนถึง 1e-30
RosLuP

คำตอบ:


4

Mathematica, 52.5 ไบต์ (= 75 * (1 - 30%))

+0.7 bytes ต่อความคิดเห็นของ @flawr

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@#}&/@ ...

แผนที่เพื่อการส่งออกของuSubdivide

ListPlot[ ... ]

เขียนผลลัพธ์สุดท้าย

โซลูชันที่ไม่ใช่กราฟ: 58 ไบต์

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

สิ่งนี้ใช้ไม่ได้f(x) = exp(x^2)
flawr

บางทีคุณอาจต้องการใช้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เป็น contraints 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!

หากมีคนต้องการแก้ไขภาพอย่าลังเลที่จะทำเช่นนั้น นี่เป็นพื้นฐานการปรับ R ของทั้งรุ่น matlab และ python

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

เพื่อเรียกใช้เวอร์ชั่น golfed เพียงใช้ (function(...){...})(args)

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


ฉันคิดว่าคุณสามารถวางis.numeric(f)เช็คถ้าคุณประกาศfเป็นฟังก์ชั่นมันไม่จำเป็นต้องผ่านมันโดยตรงในการเรียกใช้ฟังก์ชั่นเพื่อแก้
Karl Napf

อาฉันเห็นฉันไม่ทราบว่ามีความแตกต่างระหว่างทั้งสอง ถ้ามันสั้นกว่านี้คุณสามารถแก้ไข solver ของคุณเพื่อยอมรับfว่าเป็นฟังก์ชั่นได้ดังนั้นคุณไม่ต้องตรวจสอบเคสว่าเป็นค่าคงที่หรือไม่
Karl Napf

1
@Billywob ไม่จำเป็นต้องfเป็นตัวเลข ทำงานให้กับตัวอย่างแรกจึงมีความจำเป็นที่ไม่เคยไปf = (function(x)-2) rep
Angs

คุณสามารถใช้x<-0:10/10;f<-function(x){-2};10^-2*sapply(x,f)ถ้า f (x) ไม่ใช่การ10^-2*f(x)flaplace(Vectorize(function(x)2),0,0,10
กักกันโรคที่

อย่าใช้ eval ให้fเป็นฟังก์ชันที่เหมาะสม
Angs

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

การอ่านได้ค่อนข้างฮิต Ungolfed:

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

D'โอ้!


ฉันไม่รู้มากเกี่ยวกับ Haskell แต่บางทีวิธีที่ไม่มีพีชคณิตเมทริกซ์อาจสั้นกว่านี้ฉันได้เพิ่มการใช้งานตัวอย่างที่สอง
Karl Napf

@KarlNapf ไม่ได้เข้ามาใกล้มากนี่เป็นเพียงเซกเมนต์กอล์ฟ แต่ต้องใช้ฟังก์ชั่นในตัว verbose มากมาย ด้วยพีชคณิตเมทริกซ์รหัสส่วนใหญ่กำลังสร้างเมทริกซ์ (64 ไบต์) และการนำเข้า (29 ไบต์) ส่วนที่เหลือและจาโคบีใช้พื้นที่ค่อนข้างมาก
Angs

ไม่ดีเกินไป แต่ก็คุ้มค่าที่จะลอง
Karl Napf

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)

ถอดและทดสอบ

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 ผลลัพธ์ขึ้นอยู่กับฟังก์ชันดิจิต ()

หากบางคนคิดว่ามันไม่ได้เล่นกอล์ฟ => เขาหรือเธอสามารถแสดงวิธีทำ ... ขอบคุณ

PS

ดูเหมือนว่าตัวเลขทั้งหกจะอยู่ข้างหลัง สำหรับ e ^ (x ^ 2) ไม่ตกลงที่นี่หรือในตัวอย่าง แต่ที่นี่ฉันเพิ่มตัวเลข แต่ตัวเลขไม่เปลี่ยนแปลง ... สำหรับฉันมันหมายถึงตัวเลขในตัวอย่างนั้นไม่ถูกต้อง ทำไมคนอื่น ๆ ถึงไม่แสดงหมายเลข?

สำหรับบาป (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 เท่านั้นที่สามารถแสดงได้อย่างแน่นอน ดังนั้นf=-2ตัวอย่างเท่านั้นที่มีวิธีการวิเคราะห์และการจับคู่ที่ตรงกัน
Karl Napf

ที่นี่วิธีการแก้ปัญหาตัวเลขดูเหมือนว่าตกลงถ้าฉันเปลี่ยนตัวเลขเป็น 80 หรือ 70 -> g (บาป (2 *% pi * x), 1,1,1 / 19) 1.0082247336 3696433336 3666433338 0661957738 9922742638 1577926908 950765832 7044082938 1577926 ...
RosLuP
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.