【问题标题】:Sum of independent diagonal in a matrix矩阵中独立对角线的总和
【发布时间】:2018-02-01 20:10:29
【问题描述】:

我目前正在准备考试,我正在尝试处理动态矩阵。我遇到了一个关于计算矩阵的每个对角线之和的问题,该矩阵的值和大小由用户选择。 我的程序的目的是通过一个函数打印每个对角线和的值,该函数的参数是矩阵及其大小。我将向您展示代码并深入描述它。

----------------
| 52 | 35 | 5  |  Example of matrix.
----------------  Imagine the first diagonal to be the one which goes right-to-left
| 2  | 71 | 1  |  and only consists in the number "47".
----------------  The second diagonal would be the one which goes right-to-left and
| 3  | 60 | 25 |  consists in the number "15" and "79".
----------------  So to get the sum of the second diagonal it would be:
| 79 | 55 | 98 |     
----------------  sum = m[n_rows - 1][diag - 2] + m[n_rows - 2][diag - 1]
| 47 | 15 | 66 | 
----------------  When diag > columns, in order to avoid error regarding matrix size,
                  I should lower the quantity "n_rows - 1" by the quantity "diag - n_columns".

这是我的想法,根据我的描述:

void diag_matrix(int** m, int righe, int colonne){//righe = rows, colonne = columns. 
//M is the matrix.

// diag is the number of the diagonal I'm considering.
    for(int diag = 1; diag < (righe + colonne); diag++){ 

        int sum = 0;// the sum
        int i = 0;// the counter of the cicle
        int l = 0;// this is the value to riallign the row in case diag > column
        int temp = diag;//I use this variable not to modify the value of diag.

        // What I want is: when the column-index/row-index of the matrix reaches 0, the cicle will interrupt (after final iteration);
        while(righe - i - l - 1 > 0 || diag - 1 - i > 0){

            if (diag > colonne){//this condition changes l-value only if diag value is greater than column. Explanation outside the code
                l = diag - colonne;//this is the value to subtract to row-index
                temp = colonne;//this position is necessary to set column-index to its maxium.
            }

            sum = sum + m[righe - 1 - l - i][temp -1 - i];//pretty clear I think.
            i++;//the i is incremented by one.

        }// end of while-statement

        cout << "Somma Diagonale " << diag << " = " << sum << ".\n";

    }// end of for-statement

}//end of function declaration

显然它不起作用,但我无法找出问题所在。

【问题讨论】:

  • 澄清一下……您想从右下角开始,然后向顶部工作,对吧?
  • 您是否坚持使用数组的表示形式和函数签名,或者您可以根据需要使用不同的数据结构吗?
  • 在这种情况下,模运算非常有用。知道您在矩阵中的起始位置后,您可以对 i、j 进行模加/减,具体取决于您要进入的四个对角线方向中的哪一个,直到检测到 i 或 j 的环绕。
  • @Davislor 如果可以提高数据管理效率,我愿意使用不同的数据结构。但是,我的主要目的是了解我的代码中的错误。
  • @CJPN 我还没有完全理解你的想法。我试图将单位减去行/列索引,但我收到了同样的错误(这是一个崩溃)。你能解释一下你的想法是什么吗?对不起。

标签: c++ matrix diagonal


【解决方案1】:

(这里曾经有一段,但仔细一看,你并没有犯它所说的错误。)

由于您没有发布到代码审查,这里有一个解决方案,而不是详细的代码审查。 (如果你想让原始方法工作,我建议在调试器中单步执行它并检查你的变量首先在哪里得到错误的值。)它有很多样板可以让它编译和运行,但是您最感兴趣的部分是 diag_sums() 及其 cmets。

这里的一个想法是使用 OOP 自动检查数组访问的边界。后者对于捕捉一个错误等非常重要。如果你愿意,你可以在生产环境中关闭它,但是当你的程序有缓冲区溢出时,你真的不想让警告静音。这里的其他优化包括数据访问的局部性,以及操作强度的降低:我们可以简单地提前计算每个对角线的长度,而不是在每次迭代时检查我们是否击中了右边缘和下边缘。 /p>

由于矩阵a的对角线数k的定义与M行等价于:所有元素a[i][j]使得M - k = i - j,该算法通过保持不变量来确保正确性,只要我们将两个 ij,当 ij 为 0 时开始,并在 i 时停止= Mj = N,即从左或上边缘到右或下边缘遍历对角线的每一步,以先到者为准。

#include <assert.h>
#include <iostream>
#include <stddef.h>
#include <stdlib.h>
#include <utility>
#include <vector>

using std::cin;
using std::cout;

template <typename T>
  class matrix {
    public:
      matrix( const ptrdiff_t rows,
              const ptrdiff_t cols,
              std::vector<T>&& elems )
        : rows_(rows), cols_(cols), elems_(elems)
      {
        assert( rows_ > 0 );
        assert( cols_ > 0 );
        assert( elems_.size() == static_cast<size_t>(rows_*cols_) );
      }

      matrix( const ptrdiff_t rows,
              const ptrdiff_t cols,
              const std::vector<T>& elems )
        : matrix( rows, cols, std::move(std::vector<T>(elems)) )
      {}

      matrix( const matrix<T>& ) = default;
      matrix( matrix<T>&& ) = default;
      matrix& operator= ( const matrix<T>& ) = default;
      matrix& operator= ( matrix<T>&& ) = default;

      T& operator() ( const ptrdiff_t m, const ptrdiff_t n )
      {
        assert( m >= 0 && m < rows_ );
        assert( n >= 0 && n < cols_ );
        return elems_[static_cast<size_t>(m*cols_ + n)];
      }

      const T& operator() ( const ptrdiff_t m, const ptrdiff_t n ) const
      {
       /* Because this call does not modify any data, and the only reason the
        * member function above cannot be const is that it returns a non-const
        * reference to an element of elems, casting away the const qualifier
        * internally and then returning a const reference is a safe way to
        * re-use the code.
        */
        matrix<T>& nonconst = *const_cast<matrix<T>*>(this);
        return nonconst(m,n);
      }

      ptrdiff_t rows() const { return rows_; }
      ptrdiff_t cols() const { return cols_; }

    private:
      ptrdiff_t rows_;
      ptrdiff_t cols_;
      std::vector<T> elems_;
  };

template<typename T>
  std::ostream& operator<< ( std::ostream& out, const matrix<T>& x )
  /* Boilerplate to print a matrix. */
  {
    const ptrdiff_t m = x.rows(), n = x.cols();

    for ( ptrdiff_t i = 0; i < m; ++i ) {
      out << x(i,0);
      for ( ptrdiff_t j = 1; j < n; ++j )
        out << ' ' << x(i,j);

      out << '\n';
    } // end for

    return out;
  }

using elem_t = int;

std::vector<elem_t> diag_sums( const matrix<elem_t>& a )
/* Return a vector of all the diagonal sums of a.
 *
 * The first diagonal sum is a(rows-1,0)
 * The second is a(rows-2,0) + a(rows-1,1)
 * The third is a(rows-3,0) + a(rows-2,1) + a(rows-1,2)
 * And so on.  I.e., the kth diagonal is the sum of all elements a(i,j) such
 * that i - j == rows - k.
 *
 * If a is a M×N matrix, there are M diagonals starting in column zero, and
 * N-1 diagonals (excluding the one containing a(0,0) so we don't count it
 * twice) starting in row 0.  We process them bottom to top, then left to
 * right.
 *
 * The number of elements in a diagonal starting at a(i,0) is min{M-i, N}.  The
 * number of elements in a diagonal starting at a(0,j) is min{M, N-j}.  This is
 * because a diagonal stops at either the bottom edge or the left edge of a.
 */
{
  const ptrdiff_t m = a.rows(), n = a.cols();
  std::vector<elem_t> result;
  result.reserve( static_cast<size_t>(m + n - 1) );

  for ( ptrdiff_t i = m-1; i > 0; --i ) {
    elem_t sum = 0;

    const ptrdiff_t nk = (m-i) < n ? (m-i) : n;
    for ( ptrdiff_t k = 0; k < nk; ++k )
      sum += a(i+k, k);

    result.emplace_back(sum);
  } // end for i

  for ( ptrdiff_t j = 0; j < n; ++j ) {
    elem_t sum = 0;

    const ptrdiff_t nk = m < (n-j) ? m : (n-j);
    for ( ptrdiff_t k = 0; k < nk; ++k )
      sum += a(k, j+k);

    result.emplace_back(sum);
  } // end for j

  return result;
}

matrix<elem_t> read_input_matrix( const int row, const int column )
/* Reads in row*column consecutive elements from cin and packs them into a
 * matrix<elem_t>.
 */
{
  assert(row > 0);
  assert(column > 0);

  const ptrdiff_t nelements = row*column;
  assert(nelements > 0); // Check for overflow.

  std::vector<elem_t> result;
  result.reserve(static_cast<size_t>(nelements));

  for ( ptrdiff_t i = nelements; i > 0; --i ) {
    int x;
    cin >> x;
    assert(cin.good());
    result.push_back(x);
  }

  return matrix<elem_t>( row,
                         column,
                         std::move(result) );
}

template<typename T>
  bool print_sequence( const T& container )
  /* Prints the contents of a container in the format
   * "{47, 94, 124, 160, 148, 36, 5}".
   */
  {
    cout << "{";

    if ( container.begin() != container.end() )
      cout << *container.begin();

    for ( auto it = container.begin() + 1; it < container.end(); ++it )
      cout << ", " << *it;

    cout << "}\n";

    return cout.good();
  }

/* A simple test driver that reads in the number of rows, the number of
 * columns, and then row*columns int values, from standard input.  It
 * then passes the result to diag_matrix(), E.g.:
 *
 * 5 3
 * 52 35  5
 *  2 71  1
 *  3 60 25
 * 79 55 98
 * 47 15 66
 */
int main()
{
  int rows, columns;
  cin >> rows;
  cin >> columns;

  assert(cin.good());

  const matrix<elem_t> input_matrix = read_input_matrix( rows, columns );
  // cout << input_matrix; // Instrumentation.
  const std::vector<elem_t> sums = diag_sums(input_matrix);

  print_sequence(sums);

  return EXIT_SUCCESS;
}

你也可以只做print_sequence(diag_sums(read_input_matrix( rows, columns )))

【讨论】:

  • 非常感谢您的时间和耐心。我是这个论坛的新手,可能我应该在你指出的部分发帖。无论如何,您的解决方案涉及课程,这不是我考试的一部分。我应该指出的。但是,我会尝试代码并研究它以理解结构,这似乎并不难完全理解。再次感谢您的耐心等待,并对我的错误深表歉意。
  • 为什么要使用模板?是不是有点矫枉过正?
  • @Sugar 可能!
  • @KingPowa 你问了一个好问题。如果有帮助,您几乎可以忽略解决方案中使用模板的部分。也许我应该将其拆分为模块,以更加强调与您的问题相关的部分。基本上,该类是一种非常奇特的方式,可以编写a(i,j) 而不是`a[i][j]。虽然我这样写是因为我认为它有优势,尤其是在捕捉错误方面。
  • 比如我第一次写diag_sums()的时候,我原来在两个地方的一个地方输入m-1换成m-i。 (这可能意味着我应该写 min() 而不是重复自己。)这立即且可重复地提出了一个断言,我可以将其捕获在调试器和回溯中。同样,当我一次切换rowscols 时。也许有些 C++ 程序员不会那样自欺欺人,也不需要进行防御性编码,但我不是。
【解决方案2】:

只要坐标保持在矩阵内,您就可以简化代码,找到每个对角线的起始位置,然后逐步遍历矩阵。 像这样的:

#include <iostream>

using namespace std;

void diag_matrix(int** m, int rows, int cols)
{
    for (int diag = 1; diag < rows + cols; diag++)
    {
        int x, y;
        if (diag < rows)
        {
            y = rows - diag;
            x = 0;
        }
        else
        {
            y = 0;
            x = diag - rows;
        }
        int sum = 0;
        cout << "Summing diagonal #" << diag << ":";
        while ((x < cols) && (y < rows))
        {
            sum += m[y][x];
            cout << " " << m[y][x];
            x++;
            y++;
        }
        cout << " result: " << sum << "." << endl;
    }
}

int main(int argc, char* argv[])
{
    int rows = 5, cols = 3;
    int **m = new int*[rows];
    for (int i = 0; i < rows; i++)
        m[i] = new int[cols];
    m[0][0] = 52; m[0][1] = 35; m[0][2] =  5;
    m[1][0] =  2; m[1][1] = 71; m[1][2] =  1;
    m[2][0] =  3; m[2][1] = 60; m[2][2] = 25;
    m[3][0] = 79; m[3][1] = 55; m[3][2] = 98;
    m[4][0] = 47; m[4][1] = 15; m[4][2] = 66;
    diag_matrix(m, rows, cols);
    for (int i = 0; i < rows; i++)
        delete[] m[i];
    delete[] m;
    return 0;
}

【讨论】:

  • 这是否可以添加任意数量的相邻对角线行?
  • @KingPowa 我认为您的矩阵可能需要 sum+=m[y][x]。
  • @CJPN 它计算每个“主”对角线的总和,一个接一个(所以那些在左上 - 右下方向运行的),从单元素对角线开始左下角并以右上角的另一个单元素对角线结束。 TBH 我不确定这是否是您所要求的。
  • 这正是我所要求的,但是在使用您的代码时我得到了错误的值并且有时会崩溃(基于矩阵的任意大小);
  • @tevemadar 我刚刚意识到您的 while 条件应该被“交换”。现在代码有效,但似乎无法正确计算第一条和最后两条对角线。它们总是等于 0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 2016-02-10
  • 2014-12-30
  • 1970-01-01
相关资源
最近更新 更多