我有一个问题,我想使用ImageMagick的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 中有一些不错的工具,那么它对我来说是完美的。
要获得每个零件尺寸的基数,只需将总数除以零件数量即可:11/3 = 3。显然,某些零件需要大于该尺寸才能获得正确的总数,但这只是余数:11 % 3 = 2。所以现在您知道其中 2 个部分的尺寸为 3+1,剩下的就是 3。
这是我的解决方案:
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;
}
您是否考虑过使用
xargs
程序。 这可能是问题的高级解决方案。
或者,如果您想通过 shell,
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 ());
您不必创建新的子向量,请使用如下所示的内容:
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));
}
希望这有帮助。
您可以创建一个返回 std::vector 的模板 < 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;
}
这就是我的做法(我知道它与答案非常相似,但那是我的实际代码哈哈):
template<typename T>
std::vector<std::vector<T>> splitVector(const std::vector<T>& vec, size_t n)
{
std::vector<std::vector<T>> out_vec;
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 < n; ++i)
{
end += length + (i < remain);
out_vec.emplace_back(vec.begin() + begin, vec.begin() + end);
begin = end;
}
return out_vec;
}
如果您不喜欢复制,也可以返回一对迭代器等。
您可以使用
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 ;
}
}
如果其他人仍在寻找其他解决方案,您可以使用 boost::iterator_range
template <typename Container>
auto split(const Container& in, std::size_t parts)
{
using Range = boost::iterator_range<decltype(std::begin(in))>;
auto size = std::distance(std::begin(in), std::end(in));
if (parts == 0 || size < parts)
{
throw std::runtime_error(boost::str(boost::format("Cannot split %1% element container into %2% parts!") % size % parts));
}
const auto min_part_size = size / parts;
auto begin = std::begin(in);
std::vector<Range> out(parts);
std::for_each(out.begin(), out.end() - 1, [&](auto& out_range)
{
out_range = boost::make_iterator_range_n(begin, min_part_size);
begin = std::end(out_range);
});
out.back() = boost::make_iterator_range(begin, std::end(in));
return out;
}