2016-04-26 10 views
2

Ich muss die Matrix (gespeichert als ein Array von Arrays) mit einigen Werten füllen. Die Matrix ist ein Jacobi für eine einfache Diffusion Problem und sieht wie folgt aus:Verschachteltes "if" (AKA "Schalter") in Smalltalk (Pharo)

J(1,1) = 1, J(N,N)=0 

und für 1<n<N:

J(n,n) = -2k/dx^2 - 2*c(n) 
J(n,n-1)=J(n,n+1) = k/dx^2 

der Rest der Matrix Einträge Nullen sind.

Bisher habe ich diese Ungeheuerlichkeit:

(1 to: c size) collect: [ :n | 
       (1 to: c size) collect: [ :m | 
        n = 1 | (n = c size) 
         ifTrue: [ m = n ifTrue: [ 1.0 ] ifFalse: [ 0.0 ] ] 
         ifFalse: [ m = n 
          ifTrue: [ -2.0 * k/dx squared - (2.0 * (c at: n)) ] 
          ifFalse: [ m = (n-1) | (m = (n+1)) 
           ifTrue: [ k/dx squared ] 
           ifFalse: [ 0.0 ] ] ] 
        ] ] 

Beachten Sie die verschachtelte "if-Statements" (Smalltalk-Äquivalente). Das funktioniert. Aber gibt es vielleicht eine elegantere Art, dasselbe zu tun? So wie es jetzt aussieht, ist es eher unlesbar.

+1

Für Nicht-Spielzeug Probleme möchten Sie vielleicht eine effizientere (Sparse) Matrix-Implementierung dafür verwenden. –

+0

@StephanEggermont Sicher genug, für eine ernstere Aufgabe musste ich eine spärliche (banded) Matrix implementieren: Geschwindigkeitsleistung x2 - x3. – mobiuseng

Antwort

4

Aus Gründen der besseren Lesbarkeit würde ich erwägen, die extra O(n) Zeit zu opfern und IFs ganz zu vermeiden (was es nur noch schneller macht ...).

J(N,N) = 0 
J(1,1) = 1 
//and for 1<n<N: 
J(n,n) = Y(n) 
J(n,m-1) = J(n,m+1) = X 

Was mir sagt, ist, dass die gesamte Matrix so etwas wie dieses

(1 X 0 0 0) 
(X Y X 0 0) 
(0 X Y X 0) 
(0 0 X Y X) 
(0 0 0 X 0) 

sieht was bedeutet, dass ich mit nur Nullen die gesamte Matrix erstellen, und dann die Diagonale und den benachbarten Diagonalen ändern.

jNM := [ k/dx squared ]. 
jNN := [ :n | -2.0 * k/dx squared - (2.0 * (c at: n)) ]. 

n := c size. 
m := Matrix 
    new: n 
    tabulate: [:i :j | 0 ]. 
(1 to: n - 1) do: [ :i | 
    m at: i at: i put: (jNN value: i). 
    m at: i + 1 at: i put: jnM value. 
    m at: i at: i + 1 put: jnM value. 
]. 
m at: 1 at: 1 put: 1. 

Hinweis: Ich bin mit der Mathematik hinter diesem nicht vertraut, aber der Wert für J(n,m-1) scheint wie ein mir konstant.

Anmerkung 2: Ich bin die Werte bei i + 1 Indizes setzen, weil ich in der Position bin ab 1;1, aber Sie können von der entgegengesetzten Richtung starten und i-1.

+0

Ja, ich hätte daran denken sollen, die Diagonale überhaupt erst zu durchlaufen! Was deine Frage betrifft: 'J (n, n-1)' ist hier tatsächlich konstant.Aber im Allgemeinen kann es komplizierter sein. – mobiuseng

6
n := c size. 
Matrix 
    new: n 
    tabulate: [:i :j | self jacobianAtRow: i column: j] 

wo

jacobianAtRow: i column: j 
    n := c size. 
    (i = 1 or: [i = n]) ifTrue: [^j = i ifTrue: [1.0] ifFalse [0.0]]. 
    j = i ifTrue: [^-2.0 * k/dx squared - (2.0 * (c at: i))]. 
    (j = (i - 1) or: [j = (i + 1)]) ifTrue: [^k/dx squared]. 
    ^0.0 

Grundsätzlich ist die allgemeine Idee ist folgende: wenn Sie verschachtelte ifs finden, Faktor aus diesem Stück Code auf ein Verfahren für sich und verwandeln die Verschachtelung in eine Fällen artige Aufzählung das gibt bei jeder Möglichkeit einen Wert zurück.

+0

Ich denke, dieser Ansatz wird im Allgemeinen besser sein, aber diese Matrixpopulation ist nur für den Testfall. Also werde ich mit einer anderen Antwort gehen, die für meinen speziellen Fall besser ist. – mobiuseng

+0

Ich mochte @ Peters Lösung auch und ich denke, dass wir beide die Absicht hatten, Sie auf die 'new: tabulate:' -Methode aufmerksam zu machen (die ich '' 'Block: dimension:' 'nenne) –

+0

Leider bin ich die Matrix Verwenden hat diese Methode nicht. Vielleicht sollte ich es nicht benutzen und die Standard 'Matrix' Klasse verwenden (oder' 'new: tabulate:' hinzufügen), aber das ist ein anderes Problem :) – mobiuseng