Решить уравнение Лапласа


13

Введение в числовую математику

Это "Привет, мир!" уравнений в частных производных (уравнения с частными производными). Уравнение Лапласа или диффузии часто встречается в физике, например, в уравнении теплопроводности, деформировании, динамике жидкости и т. Д. Поскольку реальная жизнь - это 3D, но мы хотим сказать: «Привет, мир!» а не петь "99 бутылок пива, ..." это задание дано в 1D. Вы можете интерпретировать это как резиновый халат, привязанный к стене на обоих концах с некоторой силой, приложенной к нему.

В [0,1]домене найдите функцию uдля заданной функции источника fи граничных значений u_Lи u_Rтакую, что:

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

u'' обозначает вторую производную u

Это можно решить чисто теоретически, но ваша задача состоит в том, чтобы решить ее численно в дискретизированной области x для Nточек:

  • х = {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

Выход

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

Альтернативная реализация без матричной алгебры (с использованием метода Якоби )

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-стиле.

выигрыш

Это 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?
РосЛюП

Ответы:


4

Mathematica, 52,5 байта (= 75 * (1 - 30%))

+0,7 байта за комментарий @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@#}&/@ ...

Карта 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!

Если кто-то хочет редактировать фотографии, не стесняйтесь делать это. Это в основном 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))}

Разрушил и объяснил:

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 
}

Пример и тестовые случаи:

Функция named и 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.
Angs

Вы можете использовать, x<-0:10/10;f<-function(x){-2};10^-2*sapply(x,f)если f (x) не гарантируется для векторизации или просто 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])

Читаемость получила большой успех. 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, но, возможно, решение без матричной алгебры может быть короче, я добавил второй пример реализации.
Карл Напф

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

раскрутить и проверить

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 цифр после. для е ^ (х ^ 2) здесь не все в порядке или в примерах, но здесь я увеличиваю цифры, но цифры не меняются ... для меня это означает, что числа в примере неверны. Почему все остальные не показали свои номера?

для греха (2 *% пи * х) тоже есть проблемы

«Здесь точное решение есть 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пример имеет соответствующее аналитическое и численное решение.
Карл Напф

здесь числовое решение кажется нормальным, если я изменю цифры на 80 или 70 -> g (sin (2 *% pi * x), 1,1,1 / 19) 7044082938 1577926 ...
РосЛюП
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.