2016-04-26 7 views
2

Мне нужно заполнить матрицу (хранящуюся как массив массивов) с некоторыми значениями. Матрица представляет собой якобиан для простой задачи диффузии и выглядит следующим образом:Вложенные «if» (AKA «switch») в Smalltalk (Pharo)

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

и 1<n<N:

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

остальные матрицы записи равны нулю.

До сих пор я это уродство:

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

Обратите внимание на вложенное "если-заявления" (Smalltalk эквиваленты). Это работает. Но, может быть, более элегантный способ сделать то же самое? Как сейчас, это довольно непроницаемо.

+1

Для проблем, не связанных с игрушкой, вы можете использовать для этого более эффективную (разреженную) матричную реализацию. –

+0

@StephanEggermont Конечно, для более серьезной задачи мне пришлось реализовать разреженную матрицу: скорость работы x2 - x3. – mobiuseng

ответ

4

Для удобства чтения я бы предпочел принести в жертву дополнительное время O(n) и вообще избегать IFs (что просто делает его еще быстрее ...).

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 

Что это говорит мне о том, что вся матрица выглядит что-то вроде этого

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

Это означает, что я могу создать целую матрицу только нули, а затем изменить диагональные и соседние диагонали.

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. 

Примечание: Я не знаком с математикой за это, но значение для J(n,m-1) кажется, что константа мне.

Примечание 2: Я ставлю значение в i + 1 индексах, потому что я, начиная с позицией 1;1, но вы можете начать с противоположной стороны и имеете i-1.

+0

Да, я должен был подумать о том, чтобы просто идти по диагонали в первую очередь! Что касается вашего вопроса: 'J (n, n-1)' здесь постоянна.Но в целом это может быть сложнее. – mobiuseng

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

где

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 

В принципе, общая идея заключается в следующем: каждый раз, когда вы находите вложенное сослагательное наклонение, фактор, что часть коды к методу самостоятельно и преобразованию вложенности в случаях, как перечисление который возвращает значение при каждой возможности.

+0

Я думаю, что этот подход будет лучше в целом, но эта матричная популяция предназначена только для тестового случая. Итак, я поеду с другим ответом, который лучше для моего конкретного случая. – mobiuseng

+0

Мне тоже понравилось решение Питера, и я думаю, что мы оба хотели вас узнать о методе 'new: tabulate:' (который я называю 'fromBlock: dimension:' вместо этого) –

+0

К сожалению, использование этого метода не имеет. Может быть, я не должен использовать его и использовать стандартный класс «Matrix» (или добавить «new: tabulate:' to it), но это другая проблема :) – mobiuseng