Risolvi l'equazione di Laplace


13

Introduzione alla matematica numerica

Questo è il "Ciao, mondo!" di PDE (equazioni differenziali parziali). L'equazione di Laplace o di diffusione appare spesso in Fisica, ad esempio equazione del calore, deformazione, fluidodinamica, ecc ... Poiché la vita reale è 3D ma vogliamo dire "Ciao, mondo!" e non cantare "99 bottiglie di birra, ..." questo compito è dato in 1D. Puoi interpretarlo come un abito di gomma legato a un muro su entrambe le estremità con una forza applicata ad esso.

Su un [0,1]dominio trova una funzione uper data funzione sorgente fe valori limite u_Le u_Rtale che:

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

u'' indica la seconda derivata di u

Questo può essere risolto puramente teorico ma il tuo compito è risolverlo numericamente su un dominio discretizzato x per i Npunti:

  • x = {i/(N-1) | i=0..N-1}o basato su 1:{(i-1)/(N-1) | i=1..N}
  • h = 1/(N-1) è la spaziatura

Ingresso

  • f come funzione o espressione o stringa
  • u_L, u_Rcome valori in virgola mobile
  • N come intero> = 2

Produzione

  • Array, List, una sorta di stringa separata di utaleu_i == u(x_i)

Esempi

Esempio 1

Ingresso: f = -2, u_L = u_R = 0, N = 10(non prendere f=-2male, non è un valore, ma una funzione costante che i rendimenti -2per tutti xE 'come una forza di gravità costante sulla nostra corda..)

Produzione: [-0.0, -0.09876543209876543, -0.1728395061728395, -0.22222222222222224, -0.24691358024691357, -0.24691358024691357, -0.22222222222222224, -0.1728395061728395, -0.09876543209876547, -0.0]

Esiste una soluzione semplice e precisa: u = -x*(1-x)

Esempio 2

Ingresso: f = 10*x, u_L = 0 u_R = 1, N = 15(Qui c'è un sacco di bolina sul lato destro)

Produzione: [ 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.]

La soluzione esatta per questo afferma: u = 1/3*(8*x-5*x^3)

Esempio 3

Ingresso: f = sin(2*pi*x), u_L = u_R = 1, N = 20(qualcuno ha rotto gravità o v'è una sorta di monte ea poppa)

Produzione: [ 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.]

Qui la soluzione esatta è u = (sin(2*π*x))/(4*π^2)+1

Esempio 4

Ingresso: f = exp(x^2), u_L = u_R = 0,N=30

Produzione: [ 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. ]

Nota la leggera asimmetria

FDM

Un possibile metodo per risolvere questo è il metodo della differenza finita :

  • riscrivi -u_i'' = f_icome
  • (-u_{i-1} + 2u_i - u{i+1})/h² = f_i che è uguale
  • -u_{i-1} + 2u_i - u{i+1} = h²f_i
  • Imposta le equazioni:

  • Che sono uguali a un'equazione matrice-vettore:

  • Risolvi questa equazione e genera il u_i

Una implementazione di questo per dimostrazione in 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)

Implementazione alternativa senza Matrix Algebra (usando il metodo 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

È tuttavia possibile utilizzare qualsiasi altro metodo per risolvere l'equazione di Laplace. Se usi un metodo iterativo, dovresti iterare fino al residuo |b-Au|<1e-6, con bil vettore sul lato destrou_L,f_1h²,f_2h²,...

Appunti

A seconda del metodo di soluzione non è possibile risolvere esattamente gli esempi con le soluzioni fornite. Almeno per N->infinityl'errore dovrebbe avvicinarsi a zero.

Non sono consentite scappatoie standard, sono consentiti incorporati per PDE.

indennità

Un bonus del -30% per la visualizzazione della soluzione, grafica o ASCII-art.

vincente

Questo è codegolf, quindi vince il codice più breve in byte!


Raccomando di aggiungere un esempio che non è risolvibile analiticamente, ad esempio con f(x) = exp(x^2).
flawr

@flawr Certo, ha una soluzione ma è coinvolta la funzione di errore.
Karl Napf,

1
Mi dispiace, quella era forse l'espressione sbagliata, "antiderivativo non elementare" potrebbe essere più adatto? Intendo funzioni come log(log(x))o sqrt(1-x^4)che hanno un integrale, che tuttavia non è espressibile nelle funzioni elementari.
flawr

@flawr No, va bene, la funzione di errore non è elementare, volevo solo dire che esiste un modo per esprimere analiticamente la soluzione ma u(x) = 1/2 (-sqrt(π) x erfi(x)+sqrt(π) erfi(1) x+e^(x^2)-e x+x-1)non è esattamente calcolabile.
Karl Napf,

Perché iterare fino a 1e-6 e non iterare fino a 1e-30?
RosLuP

Risposte:


4

Mathematica, 52,5 byte (= 75 * (1-30%))

+0,7 byte per il commento di @flawr.

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

Questo traccia l'output.

per esempio

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

inserisci qui la descrizione dell'immagine

Spiegazione

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

Risolvi per la funzione u.

Subdivide@#4

Subdivide l'intervallo [0,1] in N (4 ° ingresso) parti.

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

Mappa uall'output di Subdivide.

ListPlot[ ... ]

Traccia il risultato finale.

Soluzione non grafica: 58 byte

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

Questo non funziona perf(x) = exp(x^2)
flawr

Forse potresti voler usare NDSolveil caso generale di soluzioni non elementari.
flawr

6

Matlab, 84, 81,2 79,1 byte = 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)

Si noti che in questo esempio utilizzo i vettori di riga, ciò significa che la matrice Aviene trasposta. fè preso come una maniglia di funzione, a,bsono i contorni di Dirichlet lato sinistro / destro.

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

Per l'esempio f = 10*x, u_L = 0 u_R = 1, N = 15questo si traduce in:


3

R, 123,2 102,9 98,7 byte (141-30%)

Modifica: salvato una manciata di byte grazie a @Angs!

Se qualcuno vuole modificare le immagini, sentiti libero di farlo. Questo è fondamentalmente un adattamento R delle versioni matlab e python pubblicate.

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 e spiegato:

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 
}

Esempi e casi di test:

La funzione nominata e non golfata può essere chiamata usando:

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)

Si noti che l' fargomento è una funzione R.

Per eseguire la versione golf è sufficiente utilizzare (function(...){...})(args)

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


Penso che puoi rilasciare il is.numeric(f)segno di spunta se dichiari fcome funzione, non è necessario passarlo direttamente nella chiamata di funzione al solutore.
Karl Napf,

Ah, vedo, non sapevo che ci fosse una differenza tra quei due. Bene, se è più corto puoi modificare il tuo solutore per accettare fcome una funzione in modo da non dover controllare il caso, è una costante (funzione).
Karl Napf,

1
@Billywob non c'è bisogno fdi essere mai numerici. f = (function(x)-2)funziona per il primo esempio, quindi non è mai necessario rep.
Angs,

Puoi usare x<-0:10/10;f<-function(x){-2};10^-2*sapply(x,f)se f (x) non è in quarantena per essere vettorializzato o solo 10^-2*f(x)se fè vettorializzato ( laplace(Vectorize(function(x)2),0,0,10)
Angs

Non usare eval, dare fcome una funzione adeguata.
Angs,

2

Haskell, 195 168 byte

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

La leggibilità ha avuto un bel colpo. 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]

TODO: Stampa in 83 71 byte.

Fammi vedere:

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

D'oh!


Non so molto di Haskell, ma forse la soluzione senza algebra di matrice potrebbe essere più breve, ho aggiunto una seconda implementazione di esempio.
Karl Napf,

@KarlNapf non si avvicina molto Questo è solo semi-golf ma deve usare molte funzioni incorporate dettagliate. Con l'algebra della matrice la maggior parte del codice sta costruendo la matrice (64 byte) e l'importazione (29 byte). Il residuo e il jacobi occupano molto spazio.
Angs,

Beh, peccato, ma ne è valsa la pena provare.
Karl Napf,

1

Axiom, 579 460 byte

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)

scappare e testare

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)

la funzione per la domanda è m (,,,) il codice sopra è inserito nel file "file.input" e caricato in Axiom. Il risultato dipende dalla funzione digit ().

se qualcuno pensa che non sia golfato => lui o lei può mostrare come farlo ... grazie

PS

sembra che i 6 numeri dopo il. per e ^ (x ^ 2) non sono ok qui o negli esempi ma qui aumento le cifre ma i numeri non cambiano ... per me significa che i numeri nell'esempio sono sbagliati. Perché tutti gli altri non hanno mostrato i loro numeri?

per sin (2 *% pi * x) ci sono anche problemi

"Qui la soluzione esatta è u = (sin (2 * π * x)) / (4 * π ^ 2) +1" ho copiato la soluzione esatta per x = 1/19:

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

in WolframAlpha https://www.wolframalpha.com/input/?i=(sin(2% CF% 80% 2F19))% 2F (4 % CF% 80% 5E2)% 2B1 risulta

1.008224733636964333380661957738992274267070440829381577926...
1.0083001
  1234
1.00822473

1.0083001 proposto perché il risultato è diverso nella 4a cifra dal risultato reale 1.00822473 ... (e non 6a)

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

La soluzione numerica differisce dalla soluzione esatta perché qui l'FDM è solo del secondo ordine, ciò significa che solo i polinomi fino all'ordine 2 possono essere rappresentati esattamente. Quindi solo l' f=-2esempio ha una soluzione analitica e numerica corrispondente.
Karl Napf,

qui la soluzione numerica sembra ok se cambio cifre a 80 o 70 -> g (sin (2 *% pi * x), 1,1,1 / 19) 1.0082247336 3696433338 0661957738 9922742670 7044082938 1577926908 950765832 l'altro numero 1.0082247336 3696433338 07419707722 7044082938 1577926 ...
RosLuP,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.