Сумма независимых диагоналей в матрице

В настоящее время я готовлюсь к экзамену и пытаюсь разобраться с динамической матрицей. Я столкнулся с проблемой вычисления суммы каждой диагонали матрицы, значения и размер которой выбирает пользователь. Цель моей программы состоит в том, чтобы распечатать, благодаря функции, параметрами которой являются матрица и ее размер, значение каждой диагональной суммы. Я покажу вам код и подробно опишу его.

----------------
| 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

Очевидно, что это не работает, но я не могу понять проблему.


person King Powa    schedule 01.02.2018    source источник
comment
Просто чтобы уточнить... вы хотите начать с нижнего правого угла и двигаться вверх, верно?   -  person 4386427    schedule 01.02.2018
comment
Вы застряли с этим представлением массивов и этой сигнатурой функции, или вы можете использовать другую структуру данных, если хотите?   -  person Davislor    schedule 01.02.2018
comment
В этом случае модульная арифметика чрезвычайно полезна. Зная свое начальное положение в матрице, вы можете по модулю прибавлять/вычитать из i,j, в зависимости от того, в каком из четырех диагональных направлений вы движетесь, пока не обнаружите обертывание i или j.   -  person CJPN    schedule 01.02.2018
comment
@Davislor Я готов использовать другую структуру данных, если это приведет к более эффективному управлению данными. Однако моя главная цель - понять ошибку в моем коде.   -  person King Powa    schedule 01.02.2018
comment
@CJPN Я не совсем понял твою идею. Я пытался вычесть единицы из индекса строки/столбца, но получаю ту же ошибку (это сбой). Можете ли вы объяснить, в чем заключается ваша идея? Извини.   -  person King Powa    schedule 01.02.2018
comment
Модульная математика Google. Может показаться, что это и есть та цель, к которой вас направляет подготовка к экзамену. Мы здесь не для того, чтобы выполнять вашу домашнюю работу/изучение от вашего имени. :)   -  person CJPN    schedule 01.02.2018
comment
@Rabbid76 Ваше решение работает! Но можете ли вы объяснить мне, почему мой провалился там, где ваш преуспел?   -  person King Powa    schedule 02.02.2018
comment
Общий принцип здесь таков: всегда, всегда, всегда проверяйте доступ к массиву на переполнение и опустошение буфера.   -  person Davislor    schedule 02.02.2018
comment
@CJPN Я не даю полных ответов на проблемы, которые звучат как домашнее задание (я не думаю, что это в чьих-то интересах), но в этом случае я чувствовал, что у меня есть пример кода, который стоит изучить.   -  person Davislor    schedule 02.02.2018
comment
@CJPN Во-первых, это не было домашним заданием; Я не хотел, чтобы ты делал упражнение вместо меня. Более того, это всего лишь часть большой программы, которую я разрабатываю только для упражнений. Я просто хотел знать, где я делаю ошибку. В любом случае, спасибо за подсказку, но это было не то, о чем я просил, и не тот предмет, который я должен изучить для своего экзамена.   -  person King Powa    schedule 02.02.2018
comment
@KingPowa Верно, я дал полный ответ, потому что ты сказал, что это не домашнее задание. К сожалению, дополнительный шаблон, чтобы сделать его полноценной программой, которая компилируется и запускается, похоже, похоронил единственную функцию, которая полностью решает вопрос, хотя и по-другому. Я, вероятно, должен был отделить это. Извините, вы не нашли мой ответ более полезным.   -  person Davislor    schedule 03.02.2018


Ответы (2)


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

Поскольку вы не писали в Code Review, вот решение вместо подробного обзора кода. (Если вы хотите, чтобы первоначальный подход работал, я бы предложил выполнить его пошагово в отладчике и проверить, где ваши переменные впервые получают неправильное значение.) Для его компиляции и запуска требуется много шаблонного кода, но вас больше всего заинтересует diag_sums() и его комментарии.

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

Поскольку определение числа диагоналей k матрицы a с M строк эквивалентно: все элементы a[i][j] такие, что такие, что M - k = i - j, алгоритм гарантирует правильность, сохраняя инвариант, который выполняется всякий раз, когда мы добавляем 1 к i и j, начиная, когда значение i или j равно 0, и заканчивая, когда i = M или j = 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 ))).

person Davislor    schedule 02.02.2018
comment
Большое спасибо за ваше время и терпение. Я новичок на этом форуме, и, вероятно, я должен был написать в разделе, который вы указали. В любом случае ваше решение касается классов, которые не являются частью моего экзамена. Я должен был указать. Тем не менее, я попробую код и изучу его, чтобы понять структуру, которая не кажется сложной для полного понимания. Еще раз спасибо за терпение и извините за мою ошибку. - person King Powa; 02.02.2018
comment
Зачем использовать шаблон? Разве это не перебор? - person Sugar; 02.02.2018
comment
@Сахар Наверное! - person Davislor; 02.02.2018
comment
@KingPowa Вы задали хороший вопрос. Если это поможет, вы можете в значительной степени игнорировать часть решения, использующую шаблоны. Возможно, мне следовало разбить его на модули, чтобы уделить больше внимания той части, которая имела отношение к вашему вопросу. По сути, класс — это действительно причудливый способ писать a(i,j) вместо `a[i][j]. Хотя я так написал, потому что считаю, что в этом есть плюсы, особенно в отлове багов. - person Davislor; 02.02.2018
comment
Например, когда я впервые написал diag_sums(), я первоначально набрал m-1 вместо m-i в одном из двух мест. (Что, возможно, означает, что я должен был написать min() вместо того, чтобы повторяться.) Это немедленно и воспроизводимо вызвало утверждение, которое я мог перехватить в отладчике и отследить. Точно так же, когда я переключил rows и cols в какой-то момент. Может быть, есть какой-нибудь программист на C++, который не стреляет себе в ногу таким образом и ему не нужно программировать в обороне, но только не я. - person Davislor; 02.02.2018

Вы можете упростить свой код, найдя начальную позицию каждой диагонали, а затем пройдясь по матрице, пока координаты остаются внутри матрицы. Что-то вроде этого:

#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;
}
person tevemadar    schedule 01.02.2018
comment
Позволяет ли это складывать произвольное количество смежных диагональных строк? - person CJPN; 02.02.2018
comment
@KingPowa Я думаю, вам может понадобиться sum+=m[y][x] для вашей матрицы. - person tevemadar; 02.02.2018
comment
@CJPN Он вычисляет сумму каждой основной диагонали, одну за другой (то есть те, которые идут в направлении вверх-влево-вниз-вправо), начиная с одноэлементной диагонали в нижнем левом углу и заканчивая другим одиночным. диагональ элемента в правом верхнем углу. TBH Я не уверен, что это то, о чем вы просили. - person tevemadar; 02.02.2018
comment
Это именно то, о чем я спрашивал, но я получаю неправильные значения и иногда вылетаю при использовании вашего кода (в зависимости от произвольного размера матрицы); - person King Powa; 02.02.2018
comment
@tevemadar Я только что понял, что условие в вашем while должно быть заменено. Теперь код работает, но кажется, что первые и последние 2 диагонали не могут быть правильно рассчитаны. Они всегда равны 0. - person King Powa; 02.02.2018
comment
@KingPowa это было только что набрано на телефоне, сегодня позже я соберу полный пример с матрицей, которая у вас есть в вопросе. - person tevemadar; 02.02.2018
comment
@Bob__ спасибо за исправление проблемы с столбцами-столбцами. Однако я не уверен, почему вы изменили условные обозначения скобок, они были законными и также соответствовали тем, которые использовались в вопросе. - person tevemadar; 02.02.2018
comment
Упс... Извините за это, это дело привычки. - person Bob__; 02.02.2018