Индексация начинается с 0, как и для массивов в Ruby. Возможно, это противоречит вашему опыту работы с матрицами, но индексация с 1 в качестве альтернативы не предусмотрена. Можно реализовать эту возможность самостоятельно:
# Наивный подход... не поступайте так!
class Matrix
alias bracket []
def [] (i,j)
bracket(i-1,j-1)
end
end
m = Matrix[[1,2,3],[4,5,6],[7,8,9]]
p m[2,2] # 5
На первый взгляд, этот код должен работать. Большинство операций над матрицами даже будет давать правильный результат при такой индексации. Так в чем же проблема? В том, что мы не знаем деталей внутренней реализации класса Matrix
. Если в нем для доступа к элементам матрицы всегда используется собственный метод []
, то все будет хорошо. Но если где-нибудь имеются прямые обращения к внутреннему массиву или применяются иные оптимизированные решения, то возникнет ошибка. Поэтому, решившись на такой трюк, вы должны тщательно протестировать новое поведение.
К тому же необходимо изменить методы row
и vector
. В них индексы тоже начинаются с 0, но метод []
не вызывается. Я не проверял, что еще придется модифицировать.
Иногда необходимо узнать размерность или форму матрицы. Для этого есть разные методы, например row_size
и column_size
.
Метод row_size
возвращает число строк в матрице. Что касается метода column_size
, тут есть одна тонкость: он проверяет лишь размер первой строки. Если по каким-либо причинам матрица не прямоугольная, то полученное значение бессмысленно. Кроме того, поскольку метод square?
(проверяющий, является ли матрица квадратной) обращается к row_size
и column_size
, его результат тоже нельзя считать стопроцентно надежным.
m1 = Matrix[[1,2,3],[4,5,6],[7,8,9]]
m2 = Matrix[[1,2,3],[4,5,6],[7,8]]
m1.row_.size # 3
m1.column_size # 3 m2.row_size # 3
m2.column_size # 3 (неправильно)
m1.square? # true
m2.square? # true (неправильно)
Решить эту мелкую проблему можно, например, определив метод rectangular?
.
class Matrix
def rectangular?
arr = to_a
first = arr[0].size
arr[1..-1].all? {|x| x.size == first }
end
end
Можно, конечно, модифицировать метод square?
, так чтобы сначала он проверял, является ли матрица прямоугольной. В таком случае нужно будет изменить метод column_size
, чтобы он возвращал nil
для непрямоугольной матрицы.
Для вырезания части матрицы имеется несколько методов. Метод row_vectors
возвращает массив объектов класса Vector
, представляющих строки (см. обсуждение класса Vector
ниже.) Метод column_vectors
работает аналогично, но для столбцов. Наконец, метод minor
возвращает матрицу меньшего размера; его параметрами являются либо четыре числа (нижняя и верхняя границы номеров строк и столбцов), либо два диапазона.
m = Matrix[[1,2,3,4],[5,6,7,8],[6,7,8,9]]
rows = m.row_vectors # Три объекта Vector.
cols = m.column_vectors # Четыре объекта Vector.
m2 = m.minor(1,2,1,2) # Matrix[[6,7,],[7,8]]
m3 = m.minor(0..1,1..3) # Matrix[[[2,3,4],[6,7,8]]
К матрицам применимы обычные операции: сложение, вычитание, умножение и деление. Для выполнения некоторых из них должны соблюдаться ограничения на размеры матриц-операндов; в противном случае будет возбуждено исключение (например, при попытке перемножить матрицы размерностей 3×3 и 4×4).
Поддерживаются стандартные преобразования: inverse
(обращение), transpose
(транспонирование) и determinant
(вычисление определителя). Для целочисленных матриц определитель лучше вычислять с помощью библиотеки mathn
(раздел 5.12).
Класс Vector
— это, по существу, частный случай одномерной матрицы. Его объект можно создать с помощью методов []
или elements
; в первом случае параметром является развернутый массив, а во втором — обычный массив и необязательный параметр сору
(по умолчанию равный true
).
arr = [2,3,4,5]
v1 = Vector[*arr] # Vector[2,3,4,5]
v2 = Vector.elements(arr) # Vector[2,3,4,5]
v3 = Vector.elements(arr,false) # Vector[2,3,4,5]
arr[2] = 7 # теперь v3 - Vector[2,3,7,5].
Метод covector
преобразует вектор длины N в матрицу размерности N×1 (выполняя попутно транспонирование).
v = Vector[2,3,4]
m = v.covector # Matrix[[2,3,4]]
Поддерживается сложение и вычитание векторов одинаковой длины. Вектор можно умножать на матрицу и на скаляр. Все эти операции подчиняются обычным математическим правилам.
v1 = Vector[2,3,4]
v2 = Vector[4,5,6]
v3 = v1 + v2 # Vector[6,8,10]
v4 = v1*v2.covector # Matrix![8,10,12],[12,15,18],[16,20,24]]
v5 = v1*5 # Vector[10,15,20]
Имеется метод inner_product
(скалярное произведение):
v1 = Vector[2,3,4]
v2 = Vector[4,5,6]
x = v1.inner_product(v2) # 47
Дополнительную информацию о классах Matrix
и vector
можно найти в любом справочном руководстве, например воспользовавшись командной утилитой ri
, или на сайте ruby-doc.org.