【问题标题】:Multiplying two matrix in a different way - can't figure it out how to do it以不同的方式将两个矩阵相乘 - 不知道该怎么做
【发布时间】:2021-06-01 20:37:03
【问题描述】:

作为作业,我有一个听起来像这样的问题:

We have a n*n square matrix. It is called 'subdiagonal'
if all the elements above the main diagonal are null.

a) Copy the useful elements (the ones which are not null, so basically all the elements
 from the main diagonal and below) to an array. (I've done that)

b) Write an algorithm which takes two subdiagonal matrix A, B as an input.
 Those are transformed into arrays V_a and V_b with the algorithm from a),
 then they calculate C = A*B only using only V_a and V_b

例如

Let's say A =
1 0 0 0 0
2 3 0 0 0
4 1 3 0 0
1 9 0 2 0
1 0 1 2 2
B =
2 0 0 0 0
1 1 0 0 0
0 1 2 0 0
1 1 2 3 0
2 0 0 1 2

after this input, V_a = 1,2,3,4,1,3,1,9,0,2,1,0,1,2,2; V_b = 2,1,1,0,1,2,1,1,2,3,2,0,0,1,2

and the product V_c will be 2,7,3,9,4,6,13,11,4,6,8,3,6,8,4

so the matrix will look like 
2   0  0  0  0
7   3  0  0  0
9   4  6  0  0
13  11  4  6  0
8   3  6  8  4

这是我已经研究了一段时间的代码:

#include <iostream>
#include <algorithm>

void read(int& a, int**& matrix)
{
    std::cin >> a;
    matrix = new int*[a];
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < a; j++)
        {
            matrix[i] = new int[a];
        }
    }
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < a; j++)
        {
            std::cin >> matrix[i][j];
        }
    }
}

void showMatrix(int a, int** matrix)
{
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < a; j++)
        {
            std::cout << matrix[i][j] << " ";
        }
        std::cout << std::endl;
    }
}

void showArray(int a, int* array)
{
    for (int i = 0; i < a; i++)
    {
        std::cout << array[i] << " ";
    }
}

void createArray(int a, int& arrayLength, int** matrix, int*& array)
{
    int nrDeElemente = a*a - (a * (a - 1)) / 2;
    array = new int[nrDeElemente+1];
    arrayLength = 0;
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < i+1; j++)
        {
            array[arrayLength++] = matrix[i][j];
        }
    }
}

int* multiplyArrays(int a, int arrayLength, int* array1, int* array2)
{
    int* array3 = new int[arrayLength + 1];
    for (int i = 0; i < a; i++)
    {
        array3[i] = 0;
    }
    int t = 1;
    for (int i = 0; i < arrayLength; ++i)
    {
        for (int j = 0; j < t; ++j)
        {
            for (int p = j; p < a; p++)
            {
                array3[i] += array1[j] * array2[p];
            }
        }
        ++t;
    }
    return array3;
}

int main()
{
    int **matrix1, **matrix2;
    int *array1, *array2, *multiplyResult;
    int a, arrayLength;
    read(a, matrix1);
    read(a, matrix2);
    createArray(a, arrayLength, matrix1, array1);
    createArray(a, arrayLength, matrix2, array2);
    multiplyResult = multiplyArrays(a, arrayLength, array1, array2);
    showArray(arrayLength, multiplyResult);
}

我做了a),但我不知道怎么做b)

我想我在概念上理解它(经过数小时的试验),但我真的不知道如何实现它。

我需要 3 个 for 循环,例如:

->最外层的必须负责我们在新数组上计算的位置

->下一个必须从第二个数组中选择要相乘的元素。 (选择乘数)这是其中之一 我不知道如何实现的循环。当行(来自矩阵)结束时它必须以某种方式停止并从它停止的位置开始 + 1 个元素。

->最里面的必须选择乘法的第二项(被乘数)。 我也不知道我应该如何实现这个。它应该选择与乘数一样多的元素,而且循环很奇怪(因为我每次都需要从同一行中选择所有元素)。

谁能帮我解决b点并解释他们的想法?

我很挣扎,我真的觉得我需要帮助。

顺便说一句,multiplyArrays 中的 3 个 for 循环毫无意义,您可以只写其他东西来代替它们。这 3 个 for 循环基本上是我的程序唯一需要的东西(我认为)。

谢谢:)

【问题讨论】:

  • 为了古怪,可以将“将有用的元素复制到数组中”解释为仍然不允许填充零,因此您仍然可以以一种简单的方式映射原始矩阵 :) 但是我认为,这不是任务的潜在意图......
  • 我不是用英语学习的,所以我不得不翻译这个问题,这就是为什么它有点模棱两可。在我自己的语言中,它更像是“将矩阵的有用部分(有用 = 主对角线及其下方的元素)转换为数组)。
  • 好的,所以 V_a 有第一行的 1 个项目,第二行的 2 个项目,第三行的 3 个项目,等等。所以每个循环都比上一次大一个。
  • @JerryJeremiah 是的

标签: c++ arrays algorithm matrix


【解决方案1】:

矩阵乘法C = A*B 由C(i,j) = sum_k A(i,k)*B(k,j) 定义。矩阵A 具有结构非零,其中i &gt;= k 和B,其中k &gt;= j。因此迭代就足够了

  • (外循环)i 从 0 到 n-1
  • (中间)j 从 0 到 i
  • (内部)k 从j 到i。

另一部分是将坐标(i,j) 转换为相对于一维存储格式的偏移量。第一个i 行中的结构非零数由ith 三角形数(i+1)*i/2 给出。因此,该行中的jth 元素位于(从零开始的)索引(i+1)*i/2 + j。您或您的编译器可以减少乘法的强度。

【讨论】:

  • 这是我到目前为止所做的:pastebin.com/b0AKxipz 现在我不知道如何访问 array3 中的每个元素。我还需要 1 个 for 循环吗? (不过我不这么认为)
  • @OctavianNiculescu 在强度降低之前,内部循环语句应该类似于array3[(i+1)*i/2+j] += array1[(i+1)*i/2+k] * array2[(k+1)*k/2+j];。
  • 我真的不知道力量减少是什么意思,我在任何地方都没有看到。能详细点吗?
  • @OctavianNiculescu 每次都从头开始计算(i+1)*i/2 很浪费。因此,您可以将其缓存为外循环的第一条语句,甚至可以在循环外执行int ti = 0;,然后在循环顶部执行ti += i;。不过,编译器在这方面做得很好。
  • 我明白了。我想我已经做到了。谢谢你的时间:)
【解决方案2】:

矩阵相乘需要找到行在数组中的起始位置,例如 row[2] 从数组中的索引 3 开始,如下所示,

1 0 0 0 0
2 3 0 0 0
4 1 3 0 0 => row[2]
1 9 0 2 0
1 0 1 2 2

[1, 2, 3, 4, 1, 3, 1, 9, 0, 2, 1, 0, 1, 2, 2]

如果我们知道它之前有多少元素,就可以找到任何行,就像上面的例子一样,如果我们知道row[2]之前存在三个元素,那么我们可以轻松找到row[2]。

要在每行之前找到元素的数量,需要计算一个大小等于行数的辅助数组,但要做到这一点,让我们先再次查看矩阵,
如您所见,每一行都包含等于行的index + 1 的元素,

1          element count = index + 1 = 0 + 1 = 1
2 3                                  = 1 + 1 = 2
4 1 3                                = 2 + 1 = 3
1 9 0 2                              ..
1 0 1 2 2                            ..

这意味着我们的辅助数组将是,
辅助数组 = [0, 1, 2, 3, 4] 但如何?

我们知道row[0] 之前没有元素,这就是为什么auxiliaryArray[0] = 0 然后row[1] 之前的元素只是一个可以通过前一行的索引找到的元素,即previous row index + 1 => 0 + 1,如图所示以上auxiliaryArray[1] = 1 和所有行类似,
但它没有完成!辅助数组的当前状态仅包含有关前一行中存在的元素数量的信息,而不是所有先前行中存在的信息,为此我们必须计算所有先前行的总和,这称为partial sum,它将完成如下,

row[0] = row[0]
row[1] = row[0] + row[1]
row[2] = row[1] + row[2]
..
..

最后的结果,
辅助数组 = [0, 1, 3, 6, 10]

所以你可以看到row[2] = auxiliaryArray[2] = 3之前的元素数量
通过使用上面的辅助数组,您可以找到任何行,如果您获得行的第一个元素,您可以找到所有 col 元素。
下一点要了解的是,每行必须乘以多少个元素,这又是number of elements to multiply = index + 1,正如您在上面的矩阵row[0] 中看到的,只有一个元素到多个index + 1 => 0 + 1,同样的规则适用于每一行。
最后要考虑的一点是,当行与其他矩阵的 col 相乘时,它并不总是以其他矩阵的 row[0] 开头,如下所示,otherMatrix[0][1] 在其他矩阵的左对角线之外,

2 0 0 0 0
1 1 0 0 0
0 1 2 0 0
1 1 2 3 0
2 0 0 1 2

我们终于完成了!

#include <iostream>

#include <vector>

#include <numeric>
#include <iterator>

using std::cout;

void printMatrixArray(std::size_t rowSize, const std::vector<int>& matArray){

    std::size_t elementCount = 1;
    std::vector<int>::const_iterator it = matArray.cbegin();

    for(std::size_t row = 0; row < rowSize; ++row){

        std::copy(it, it + elementCount, std::ostream_iterator<int>(cout, "\t"));
        cout<< '\n';

        it += elementCount;
        ++elementCount;
    }
}

std::vector<int> leftDiagonalBottomMatrix(const std::vector<std::vector<int>>& mat){

    std::vector<int> res;
    res.reserve(((1 + mat.size()) * mat.size()) / 2);

    std::vector<int>::size_type elementCount = 1;

    for(const std::vector<int>& row : mat){

        for(std::vector<int>::const_iterator it = row.cbegin(), endIt = row.cbegin() + elementCount; endIt != it; ++it){

            res.push_back(*it);
        }

        ++elementCount;
    }

    return res;
}

std::vector<int> multiplyMatrixArrays(const std::vector<int>& mat1Arr, const std::vector<int>& mat2Arr,
                                     std::vector<int>::size_type rowSize){

    std::vector<int> auxiliaryArray(rowSize);
    auxiliaryArray.front() = 0;

    std::iota(auxiliaryArray.begin() + 1, auxiliaryArray.end(), 1);

    std::partial_sum(auxiliaryArray.cbegin(), auxiliaryArray.cend(), auxiliaryArray.begin());

    std::vector<int> res;
    res.reserve(mat1Arr.size());

    for(std::vector<int>::size_type row = 0; row < rowSize; ++row){

        for(std::vector<int>::size_type col = 0; col <= row; ++col){

            int val = 0;

            for(std::vector<int>::size_type ele = col, elementCount = row + 1; ele < elementCount; ++ele){

                val += mat1Arr[auxiliaryArray[row] + ele] * mat2Arr[auxiliaryArray[ele] + col];
            }

            res.push_back(val);
        }
    }

    return  res;
}

std::vector<int> matrixMultiply(const std::vector<std::vector<int>>& mat1, const std::vector<std::vector<int>>& mat2){

    return multiplyMatrixArrays(leftDiagonalBottomMatrix(mat1), leftDiagonalBottomMatrix(mat2), mat1.size());
}


int main(){

    std::vector<std::vector<int>> mat1{{1, 0, 0, 0, 0}, {2, 3, 0, 0, 0}, {4, 1, 3, 0, 0}, {1, 9, 0, 2, 0},
                                       {1, 0, 1, 2, 2}};

    std::vector<std::vector<int>> mat2{{2, 0, 0, 0, 0}, {1, 1, 0, 0, 0}, {0, 1, 2, 0, 0}, {1, 1, 2, 3, 0},
                                       {2, 0, 0, 1, 2}};

    printMatrixArray(mat1.size(), matrixMultiply(mat1, mat2));
}

输出:

2   
7   3   
9   4   6   
13  11  4   6   
8   3   6   8   4

输出不打印矩阵左对角线以上的元素!

【讨论】:

  • 很好的解释和全面的答案。感谢您抽出宝贵时间,这是一种不同的方法,而不是我使用的方法,所以它对我很有帮助。
猜你喜欢
  • 1970-01-01
  • 2012-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-15
  • 2014-10-03
  • 1970-01-01
相关资源
最近更新 更多