Excel VBA: транспонировать вектор и умножение матриц в функции - PullRequest
1 голос
/ 06 июля 2019

У меня есть матрица 3x3 "mat" и вектор "vec" (3x1) действительных чисел, которые я хочу умножить в матрице (в смысле линейной алгебры) в функции VBA следующим образом: t (vec) mat vec для получения действительного числа 1x1, которое я могу использовать в уравнении.

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

Public Function QuickMaths()
Dim vec As Variant
Dim mat As Variant
mat = Array(Array(1,1+1,3), _ 
          Array(2^2,5,6), _
          Array(7,8,9))
vec = Array(2*5,11,12)
QuickMaths = Application.WorksheetFunction.MMult(Application.WorksheetFunction.MMult(Application.WorksheetFunction.Transpose(vec), mat), vec)

End Function

Я получаю #VALUE из этого на листе, когда запускаю его. Я ожидал бы, что выходные данные будут иметь матрицу 1x1, но я не знаю, будет ли Excel VBA считать скаляр, который может быть выведен на лист как одно значение (например, Double).

Пожалуйста, пришлите помощь.

1 Ответ

1 голос
/ 06 июля 2019

Было бы хорошо, если бы вы предоставили ожидаемый результат (конкретный скаляр, который вы ожидаете в конце).

Исходя из того, что я собираю из вашего кода и вопроса, я собираюсь предположитьВы пытаетесь выполнить два шага.Первое из них:

First step

Второе:

Second step

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

Ваш первый массив (mat) - это массив массивов (недвумерный массив), который я не считаю MMULT ручками (https://support.office.com/en-us/article/mmult-function-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb). Так что вам может потребоваться заменить:

mat = Array(Array(1, 1 + 1, 3), _
          Array(2 ^ 2, 5, 6), _
          Array(7, 8, 9))

на:

ReDim mat(0 To 2, 0 To 2)
mat(0, 0) = 1
mat(0, 1) = 2
mat(0, 2) = 3
mat(1, 0) = 4
mat(1, 1) = 5
mat(1, 2) = 6
mat(2, 0) = 7
mat(2, 1) = 8
mat(2, 2) = 9

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

Исходя из вышеизложенного, ваш код может выглядеть примерно так:

Option Explicit

Public Function QuickMaths() As Variant

    ' This function returns a value of type Variant.
    ' Could return a Long/Double/numeric type; scalar should be at QuickMaths(1,1)
    ' But MMULT can return non-numeric values, so you risk
    ' getting a type mismatch error if the matrix multiplication
    ' is not successful (for whatever reason).
    ' Maybe this shouldn't be this function's concern -- or maybe it should.

    Dim mat As Variant
    mat = Array(Array(1, 1 + 1, 3), _
              Array(2 ^ 2, 5, 6), _
              Array(7, 8, 9))
    mat = FlattenAnArrayOfArrays(mat)

    Dim vec As Variant
    vec = Array(2 * 5, 11, 12)

    Dim resultantMatrix As Variant
    resultantMatrix = Application.MMult(vec, mat) ' Number of columns in "vec" must match number of rows in "mat"
    resultantMatrix = Application.MMult(vec, Application.Transpose(resultantMatrix))

    QuickMaths = resultantMatrix

End Function

Private Function FlattenAnArrayOfArrays(ByRef arrayOfArrays As Variant) As Variant()
    ' Given an array of arrays, returns a two-dimensional array.
    ' This function is very basic and has no error handling implemented.

    Dim firstArray() As Variant
    firstArray = arrayOfArrays(LBound(arrayOfArrays)) ' Columns inferred from first array in "arrayOfArrays"

    Dim outputArray() As Variant
    ReDim outputArray(LBound(arrayOfArrays) To UBound(arrayOfArrays), LBound(firstArray) To UBound(firstArray))

    Dim rowIndex As Long
    For rowIndex = LBound(outputArray, 1) To UBound(outputArray, 1)
        Dim columnIndex As Long
        For columnIndex = LBound(outputArray, 2) To UBound(outputArray, 2)
            outputArray(rowIndex, columnIndex) = arrayOfArrays(rowIndex)(columnIndex)
        Next columnIndex
    Next rowIndex

    FlattenAnArrayOfArrays = outputArray

End Function

Точки закрытия:

  • Возвращаемое значение функции QuickMaths представляет собой матрицу 1x1, но вы можете присвоить ее значению ячейки.
  • Аналогично, если вы вызываете функцию QuickMaths из ячейки рабочего листа,ячейка будет отображать возвращаемое значение (без каких-либо проблем или необходимости формулы массива).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...