Вектор векторной инициализации - PullRequest
9 голосов
/ 01 декабря 2010

Мне тяжело запутаться, как инициализировать вектор векторов.

typedef vector >>> DataContainer;

Я хочу, чтобы это соответствовало

level_1 (2 elements/vectors)
   level_2 (7 elements/vectors)
      level_3 (480 elements/vectors)
         level_4 (31 elements of float)

Адресация к элементам не являетсявопрос.Это должно быть так же просто, как что-то вроде

dc[0][1][2][3];

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

dc[0][3][230][22];
dc[1][3][110][6]; //...etc

Так что мне нужно заранее инициализировать V of V.

Я психую себя или это так просто, как

for 0..1
    for 0..6
        for 0..479
           for 0..30
               dc[i][j][k][l] = 0.0;

Не похоже, что это должноРабота.Каким-то образом векторы верхнего уровня должны быть сначала инициализированы.

Любая помощь приветствуется.Я уверен, что это должно быть проще, чем я себе представляю.

Ответы [ 3 ]

19 голосов
/ 01 декабря 2010
  • Пожалуйста, не используйте вложенные векторы , если размер вашего хранилища известен заранее , т. Е. Есть особая причина почему, например, первый индекс должен иметь размер 6 и никогда не изменится. Просто используйте простой массив. Еще лучше, используйте boost::array. Таким образом, вы получаете все преимущества наличия простого массива (экономьте огромные объемы пространства при переходе в многомерное пространство) и преимущества создания реального объекта.

  • Пожалуйста, не используйте вложенные векторы , если ваше хранилище должно быть прямоугольным , т.е. вы можете изменить размер одного или нескольких измерений, но каждая "строка" должна быть такой же длины в какой-то момент. Используйте boost::multi_array. Таким образом, вы документируете «это хранилище прямоугольное», сохраняете огромное количество места и при этом получаете возможность изменять размеры, преимущества наличия реального объекта и т. Д.

Суть std::vector в том, что он (а) предназначен для изменения размера и (б) не заботится о его содержимом, если они имеют правильный тип. Это означает, что если у вас есть vector<vector<int> >, то все «векторы строк» ​​должны хранить свою собственную отдельную бухгалтерскую информацию о том, как долго они хранятся - даже если вы хотите, чтобы они имели одинаковую длину. Это также означает, что все они управляют отдельными выделениями памяти, что снижает производительность (поведение кэша) и тратит еще больше места из-за перераспределения std::vector. boost::multi_array разработан с учетом того, что вы, возможно, захотите изменить его размер, но не будете постоянно изменять его, добавляя элементы (строки, для 2-мерного массива / граней, для 3-мерного массива / и т. Д.) конец. std::vector предназначен для (потенциально) бесполезного использования пространства, чтобы обеспечить медленную работу. boost::multi_array предназначен для экономии места и аккуратной организации всего в памяти.

Сказано :

Да, вам нужно что-то сделать, прежде чем индексировать вектор. std::vector не будет волшебным образом вызывать появление индексов, потому что вы хотите что-то там хранить. Однако с этим легко справиться:

Вы можете по умолчанию инициализировать вектор с соответствующим количеством нулей, а затем заменить их с помощью конструктора (size_t n, const T& value = T()). То есть

std::vector<int> foo(10); // makes a vector of 10 ints, each of which is 0

потому что int, созданный по умолчанию, имеет значение 0.

В вашем случае нам нужно указать размер каждого измерения, создав подвекторы соответствующего размера и разрешив конструктору скопировать их. Это выглядит так:

typedef vector<float> d1;
typedef vector<d1> d2;
typedef vector<d2> d3;
typedef vector<d3> d4;
d4 result(2, d3(7, d2(480, d1(31))));

То есть безымянный d1 имеет размер 31, который используется для инициализации по умолчанию d2, который используется для инициализации по умолчанию d3, который используется для инициализации result.

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

  • Вы можете использовать .push_back() для добавления к вектору. Сделайте пустой d1 непосредственно перед самым внутренним циклом, в котором вы неоднократно .push_back() заполняете его. Сразу после цикла вы .push_back() получаете результат на d2, который вы создали непосредственно перед самым внутренним циклом, и т. Д.

  • Вы можете предварительно изменить размер вектора с помощью .resize(), а затем индексировать его как обычно (вплоть до суммы, до которой вы изменили размер).

2 голосов
/ 01 декабря 2010

РЕДАКТИРОВАТЬ: я признаю, что этот код не элегантный. Мне нравится ответ @Karl, который является правильным путем.

Этот код скомпилирован и протестирован. На нем напечатано 208320 нулей, которые ожидаются (2 * 7 * 480 * 31)

#include <iostream>
#include <vector>

using namespace std;

typedef vector< vector < vector < vector< float > > > > DataContainer;

int main()
{
    const int LEVEL1_SIZE = 2;
    const int LEVEL2_SIZE = 7;
    const int LEVEL3_SIZE = 480;
    const int LEVEL4_SIZE = 31;

    DataContainer dc;

    dc.resize(LEVEL1_SIZE);
    for (int i = 0; i < LEVEL1_SIZE; ++i) {
        dc[i].resize(LEVEL2_SIZE);
        for (int j = 0; j < LEVEL2_SIZE; ++j) {
            dc[i][j].resize(LEVEL3_SIZE);
            for (int k = 0; k < LEVEL3_SIZE; ++k) {
                dc[i][j][k].resize(LEVEL4_SIZE);
            }
        }
    }

    for (int i = 0; i < LEVEL1_SIZE; ++i) {
        for (int j = 0; j < LEVEL2_SIZE; ++j) {
            for (int k = 0; k < LEVEL3_SIZE; ++k) {
                for (int l = 0; l < LEVEL4_SIZE; ++l) {
                    dc[i][j][k][l] = 0.0;
                }
            }
        }
    }

    for (int i = 0; i < LEVEL1_SIZE; ++i) {
        for (int j = 0; j < LEVEL2_SIZE; ++j) {
            for (int k = 0; k < LEVEL3_SIZE; ++k) {
                for (int l = 0; l < LEVEL4_SIZE; ++l) {
                    cout << dc[i][j][k][l] << " ";
                }
            }
        }
    }

    cout << endl;
    return 0;
}
2 голосов
/ 01 декабря 2010

Возможно, вам придется установить размер или зарезервировать память

Не могли бы вы сделать для каждого или вложенного для этого вызовет

myVector.resize(x); //or size

на каждом уровне.

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