【问题标题】:Matrix multiplication (with different dimensions)矩阵乘法(不同维度)
【发布时间】:2017-11-28 16:06:47
【问题描述】:

对于学校的数学课,我需要创建一个应用程序来使用矩阵做一些事情(只是做任何事情)。我决定创建一个矩阵计算器。我有一个 Matrix 类,它包含一个二维数组、一个行整数和一个列整数。我创建了以下函数来将两个矩阵相乘:

public: Matrix* multiply(Matrix* other)
{
  Matrix* temp = new Matrix(other->r, other->c);
  for(int i = 0; i < this->r; i++)
  {
    for(int j = 0; j < this->c; j++)
    {
      for(int k = 0; k < other->c; k++)
        temp->mat[i][j] += this->mat[i][k] * other->mat[k][j];
    }
  }
  return temp;
}

这很有效,但前提是我将具有相同尺寸的矩阵相乘(例如 Mat4x4*Mat4x4 或 Mat2x4*Mat2x4)。我知道我不能只将 Mat4x4 与 Mat9X2 或任何东西相乘,但我知道第二个矩阵的列应该等于第一个矩阵的行(所以 Mat2x2 应该能够与 Mat2x1 相乘)并且答案会具有第二个矩阵的维度。我如何(或应该)创建函数,以便它将具有相同和不同维度的矩阵相乘?

提前致谢

【问题讨论】:

  • 问题出在最里面的循环上。比如说,在维度 m*nn*p 矩阵的矩阵乘法中,您的 k 循环应该运行到 k&lt;n
  • 与您的问题无关,但从这样的函数返回拥有指针不是一个好的设计决策。
  • 这里使用“矩阵乘法”的方式并不是矩阵乘法的正式数学定义。矩阵乘法不可交换(即AxB != BxA)。如果 A 的大小为 m*n,则 B 的大小必须为 n*p,结果将是大小为 m*p 的矩阵。我建议doing some reading about this
  • @DeiDei 你能详细说明或链接我的解释吗?谢谢你提到坏习惯
  • @ImaginaryHuman072889 感谢您的回复,它帮助我找到了答案

标签: c++ math matrix


【解决方案1】:

您的程序的解决方案是使临时维度不是其他维度而是this-&gt;rother-&gt;c,以便使维度与矩阵乘法的输出有效。

希望这会有所帮助。

【讨论】:

  • 这不是解决方案,但确实避免了第二个问题,谢谢
  • @danivdwerf 你能贴出你的Matrix类的来源
  • @danivdwerf 您需要确保第二个 for 循环不会越界应该是 other->c 您遇到的错误是什么?
  • 我没有收到错误消息。我的意思是更改临时矩阵的行可以防止我还没有偶然发现的可能错误
【解决方案2】:

以下代码包含一个 Matrix 类实现,旨在展示 C++ 的一些特性(如唯一指针、随机数和流格式)。当我想稍微解释一下该语言时,我经常使用它。也许它可以帮助你。

#include <cassert>
#include <iostream>
#include <iomanip>
#include <memory>
#include <random>

// Pedagogical implementation of matrix type.
class Matrix {

 public:

  // Create a rows-by-cols matrix filled with random numbers in (-1, 1).
  static Matrix Random(std::size_t rows, std::size_t cols) {

    Matrix m(rows, cols);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<double> dis(-1, 1);

    for (std::size_t row = 0; row < rows; ++row) {
      for (std::size_t col = 0; col < cols; ++col) {
        m(row, col) = dis(gen);
      }
    }

    return m;
  }

  // Build an uninitialized rows-by-cols matrix.
  Matrix(std::size_t rows, std::size_t cols)
      : m_data { std::make_unique<double[]>(rows * cols) },
        m_rows { rows },
        m_cols { cols }
  {
    assert(m_rows > 0);
    assert(m_cols > 0);
  }

  // Return number of rows
  std::size_t rows() const { return m_rows; }

  // Return number of columns
  std::size_t cols() const { return m_cols; }

  // Value at (row, col)
  double operator()(std::size_t row, std::size_t col) const {
    assert(row < rows());
    assert(col < cols());
    return m_data[row * cols() + col];
  }

  // Reference to value at (row, col)
  double& operator()(std::size_t row, std::size_t col) {
    assert(row < rows());
    assert(col < cols());
    return m_data[row * cols() + col];
  }

  // Matrix multiply
  Matrix operator*(const Matrix& other) const {

    assert(cols() == other.rows());

    Matrix out(rows(), other.cols());

    for (std::size_t i = 0; i < rows(); ++i) {
      for (std::size_t j = 0; j < other.cols(); ++j) {
        double sum { 0 };
        for (std::size_t k = 0; k < cols(); ++k) {
          sum += (*this)(i, k) * other(k, j);
        }
        out(i, j) = sum;
      }
    }
    return out;
  }

 private:

  std::unique_ptr<double[]> m_data; // will cleanup after itself
  const std::size_t m_rows;
  const std::size_t m_cols;
};

// Pretty-print a matrix
std::ostream& operator<<(std::ostream& os, const Matrix& m) {
  os << std::scientific << std::setprecision(16);
  for (std::size_t row = 0; row < m.rows(); ++row) {
    for (std::size_t col = 0; col < m.cols(); ++col) {
      os << std::setw(23) << m(row, col) << " ";
    }
    os << "\n";
  }
  return os;
}

int main() {

  Matrix A = Matrix::Random(3, 4);
  Matrix B = Matrix::Random(4, 2);

  std::cout << "A\n" << A
            << "B\n" << B
            << "A * B\n" << (A * B);
}

可能的输出:

$ clang++ matmul.cpp -std=c++17 -Ofast -march=native -Wall -Wextra
$ ./a.out
A
 1.0367049464391398e-01  7.4917987082978588e-03 -2.7966084757805687e-01 -7.2325095373639048e-01 
 2.2478938813996119e-01  8.4194832286446353e-01  5.3602376615184033e-01  7.1132727553003439e-01 
 1.9608747339865196e-01 -6.4829263198209253e-01 -2.7477471919710350e-01  1.2721104074473044e-01 
B
-8.5938605801284385e-01 -6.2981285198013204e-01 
-6.0333085647033191e-01 -6.8234173530317577e-01 
-1.2614486249714407e-01 -3.3875904433100934e-01 
-6.9618174970366520e-01  6.6785401241316045e-01 
A * B
 4.4517888255515814e-01 -4.5869338680118737e-01 
-1.2639839804611623e+00 -4.2259184895688506e-01 
 1.6871952235091500e-01  4.9689953389829533e-01 

【讨论】:

    【解决方案3】:

    事实证明,行和列的顺序让我大吃一惊。公式是正确的。对不起,不必要的帖子。

    【讨论】: