Как разбить вектор на n «почти равных» частей - PullRequest
16 голосов
/ 28 июля 2011

У меня проблема с тем, что я хотел бы объединить большое количество изображений с помощью ImageMagick's convert.exe, но под Windows у меня есть ограничение командной строки длиной 8192 байта.

Мое решение этого состоит в том, чтобы разделить задачу на подзадачу меньшего размера, запустить ее и выполнить заключительную задачу, которая объединяет их вместе.

Моя идея состоит в том, чтобы написать функцию, которая берет вектор изображений и целое число и разбивает вектор на n подвекторов, все из которых имеют "почти равные" части.

Так, например, если бы я хотел разделить 11 на 3 группы, это было бы 4-4-3.

Можете ли вы сказать мне, как я могу сделать это в C ++? Я имею в виду, чтобы написать функцию

split_vec( const vector<image> &images, int split )

что делает расщепление?

Кроме того, можете ли вы сказать мне, какой самый эффективный способ сделать , если мне не нужно создавать новые векторы, просто пройтись по частям ? Как функция std::substr с std::string?

Примечание: я уже использую Boost в проекте, поэтому, если в Boost есть какой-нибудь хороший инструмент для этого, он идеально подходит для меня.

Ответы [ 7 ]

11 голосов
/ 28 июля 2011

Чтобы получить базовое число для размера каждой части, просто разделите общее количество на количество частей: 11/3 = 3. Очевидно, что некоторые части должны быть больше, чем это, чтобы получить правильное общее количество, ноэто всего лишь остаток: 11% 3 = 2. Итак, теперь вы знаете, что 2 части будут размером 3 + 1, а все остальное будет 3.

5 голосов
/ 08 июня 2016

Вот мое решение:

template<typename T>
std::vector<std::vector<T>> SplitVector(const std::vector<T>& vec, size_t n)
{
    std::vector<std::vector<T>> outVec;

    size_t length = vec.size() / n;
    size_t remain = vec.size() % n;

    size_t begin = 0;
    size_t end = 0;

    for (size_t i = 0; i < std::min(n, vec.size()); ++i)
    {
        end += (remain > 0) ? (length + !!(remain--)) : length;

        outVec.push_back(std::vector<T>(vec.begin() + begin, vec.begin() + end));

        begin = end;
    }

    return outVec;
}
1 голос
/ 30 июля 2011

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

size_t ProcessSubVec(const vector<Image>& images, size_t begin, size_t end)
{
    // your processing logic
}

void SplitVec(const vector<Image>& images, int cnt)
{
    size_t SubVecLen = images.size() / cnt,
           LeftOvers = images.size() % cnt,
           i = 0;

    // Split into "cnt" partitions
    while(i < images.size())
        i += ProcessSubVec(images, i, i + SubVecLen + (LeftOvers-- == 0 ? 0 : 1));
}

Надеюсь, это поможет.

1 голос
/ 28 июля 2011

Задумывались ли вы об использовании программы xargs. Возможно, это решение проблемы высокого уровня.

0 голосов
/ 12 февраля 2018

Вы можете создать шаблон, который возвращает std :: vector и получает вектор, который вы хотите разделить, и количество делений.использовать for и итератор очень просто.

#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>

template<typename T>
std::vector< std::vector<T> > split(std::vector<T> vec, uint64_t n) {
  std::vector< std::vector<T> > vec_of_vecs(n);

  uint64_t quotient = vec.size() / n;
  uint64_t reminder = vec.size() % n;
  uint64_t first = 0;
  uint64_t last;
  for (uint64_t i = 0; i < n; ++i) {
    if (i < reminder) {
      last = first + quotient + 1;
      vec_of_vecs[i] = std::vector<T>(vec.begin() + first, vec.begin() + last);
      first = last;
  }
    else if (i != n - 1) {
    last = first +  quotient;
    vec_of_vecs[i] = std::vector<T>(vec.begin() + first, vec.begin() + last);
    first = last;
  }
    else
    vec_of_vecs[i] = std::vector<T>(vec.begin() + first, vec.end());
}

return vec_of_vecs;
}

#define ONE_DIMENSION 11
#define SPLITS 3

int main(void)
{
  std::vector<uint64_t> vector(ONE_DIMENSION);
  std::iota(std::begin(vector), std::end(vector), 1);

  std::vector<std::vector<uint64_t>> vecs(SPLITS);
  vecs = split(vector, SPLITS);

  for (uint64_t m = 0; m < vecs.size(); ++m) {
    for (auto i : vecs[m])
      std::cout << std::setw(3) << i << " ";
    std::cout << std::endl;
  }


  return 0;
}
0 голосов
/ 28 июля 2011

Вы можете использовать iterators для итераций по частям проблемы.Использование итераторов аналогично указателям на элементы vector

То, что вы хотите на изображениях, может быть реализовано как функция

using namespace std; 
void do_some_work(vector<image>::iterator begin, vector<image>::iterator end) {
    vector<image>::iterator i = begin ;
    while(i != end) {
        // do something using *i , which will be of type image
        ++i ;
    }
}
0 голосов
/ 28 июля 2011

CreateProcess имеет ограничение в 32 КБ

Или, если вы хотите пройти через оболочку,

vec::const_iterator i = vec .begin ();
vec::const_iterator j = i + stride;

while (j < vec .end ()) {
    do_range (i, j);
    i = j;
    j += stride;
}

do_range (i, vec .end ());
...