Обсуждение - Массивы против вектора векторов при создании матриц - какой вариант наиболее практичный? - PullRequest
1 голос
/ 10 июля 2020

Итак, я наткнулся на упражнение по книге C ++ под названием - «Руководство по науке c Вычисления на C ++»

Вот упражнение: «Написать код который динамически выделяет память для трех матриц 2 × 2 с номерами плавающих точек двойной точности, A, B, C, и присваивает значения записям A и B. Пусть C = A + B. Расширьте свой код так, чтобы он вычисляет записи C, а затем выводит записи C на экран. Наконец, освободите память. Опять же, проверьте, правильно ли вы освободили память, используя a для l oop, как в предыдущем упражнение. "

Это привлекло мое внимание, я попытался решить эту задачу, используя как 2D-массивы (мне удалось это сделать очень легко), так и вектор векторов (я потерпел неудачу).

Я провел много исследований и прочитал пост на StackOverFlow, и в целом мнение было универсальным - при работе с матрицами всегда выбираются 2D-массивы.

Но зная, что здесь много программистов ( а и что я новичок ie с C ++), я бы очень хотел прочитать больше мнений об этом топи c!

PS: Вот небольшой фрагмент моей неудачной попытки создать матрицу с использованием вектора векторов:

for (int row{ 0 }; row < 2; row++) { // Create Matrix A - goes through the matrix rows
    
    for (int col{ 0 }; col < 2; col++) { // goes through the matrix columns
        temp.push_back(rand() % 201); // add random number to the temporary vector
    }

    matrixA.push_back(temp);
}

// Outputing 
for (int row{ 0 }; row < matrixA.size(); row++) { // goes through the matrix rows

    for (int col{ 0 }; col < matrixA.at(row).size(); col++) { // goes through the vectors inside matrixA
        cout << matrixA.at(row).at(col) << "\t";
    }
    cout << endl;
}

Это результат: введите описание изображения здесь

1 Ответ

1 голос
/ 10 июля 2020

Проблема root в том, что C ++ не имеет встроенной поддержки многомерных массивов, а

Матрица - это не массив массивов

Так что, хотя можно иметь в C / C ++ это массив массива, например, в double M[3][3], это не лучший способ представления матрицы. Даже если в фиксированном случае времени компиляции это может быть нормально, способ vector< vector<double> > определенно не подходит.

Почему:

  • Синтаксис нижнего индекса M[i][j] все еще отражает тот факт, что это массив массивов, а не настоящий многомерный массив (M[i,j] допустимо в C / C ++, но означает нечто совершенно иное). Синтаксис M[i][j] также не совсем соответствует математическому использованию. Чтобы получить доступ к элементу i, j матрицы, математики обычно пишут

    A i, j

    , а не

    (A i ) j

    (что соответствует M[i][j])

  • Опыт в математике и науке c вычислений показывает, что большая часть время оба измерения обычно одинаково важны. На самом деле нет внешнего и внутреннего измерения. Нарезка часто выполняется как по строкам, так и по столбцам. Также довольно распространены более сложные образцы нижнего индекса (такие как подматрица или шаг).

  • Часто требуется операция выравнивания, то есть реорганизация элементов матрицы в линейный вектор.

    * Матрицы 1040 *
  • 0 x N или M x 0 не реализованы. Это относится к stati c case double A[N][M], но также и к vector< vector<double> >, где at 0 x N не может быть представлен. Правильное обращение с пустыми матрицами чрезвычайно важно. Нижний индекс матрицы может генерировать пустую матрицу в качестве допустимого случая. С другой стороны, умножение матриц всегда требует согласования внутренних размеров. Таким образом, 0xN * NxM - допустимый случай и не должен вызывать ошибок.

  • vector< vector<double> > тратит память. Кроме того, он медленнее, чем должен быть (подумайте о матрице Nx2 с большим N. Это связано с большими накладными расходами). То же самое относится к double **M в чистом виде C.

  • vector< vector<double> > несовместимо с внешними библиотеками линейной алгебры (такими как LAPACK или иначе)

  • vector< vector<double> > может быть непрямым angular, т.е. нужно убедиться, что все внутренние векторы имеют одинаковую длину.

Матрица - это объект свой собственный

Именно так это часто делается в пакетах линейной алгебры. Есть различия, но, по сути, все они очень похожи.

  • Наиболее разумно рассматривать многомерный массив как отдельную сущность. Это не массив массивов или около того, хотя на первый взгляд это может показаться хорошей идеей.

  • Хранение данных всегда линейно

  • Доступ элемента матрицы отображается в эту линейную память. На самом деле это довольно просто. Чтобы получить доступ к элементу i, j, используйте A[i*lda + j] с lda длиной ведущего измерения. Не то чтобы это подразумевает определенный порядок элементов в памяти. Этот случай обычно обозначается как заказ C. Противоположный порядок FORTRAN просто поменял бы местами значения i и j или, в случае более высокой размерности, изменил бы порядок индексов.

  • Чтобы быть действительно общим c, nd-массив состоит из

    struct ndarray {
      double *buffer;
      size_t  size[DIMS];
      size_t strides[DIMS]
    };
    

    Это может реализовывать владение (то есть владение буфером) или матрицу без владения. Последнее может быть важно для реализации подматрицы с нулевыми накладными расходами или индексации столбца / строки.

    Доступ к элементу i,j матрицы (DIM == 2) равен

    buffer[ i*strides[0] + j*strides[1] ]
    

    Это легко обобщается на более высокие измерения.

    Кроме того, транспонирование матрицы просто требует обращения массива strides. Копирование не требуется.

    Нижний индекс строки или столбца, подматрица или получение разбитой подматрицы может быть реализовано как операция с нулевыми издержками. Просто нужно правильно заполнить массив strides и установить соответственно buffer и size.

    strides должно быть правильно инициализировано при создании матрицы, в зависимости от того, будет ли использоваться макет C или FORTRAN.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...