我得到一系列范围,我需要在任何范围内迭代每个数字一次。范围可能重叠并包含相同的数字。
范围内的数字是
using Number = uint32_t;
范围是这种形式
struct Range {
Number first;
Number last;
Number interval;
};
只是为了澄清Range
的表示。
Range range = {
2, //first
14, //last
3 //interval
};
//is equivalent to...
std::vector<Number> list = {2, 5, 8, 11, 14};
我有几个Range
,我需要以任何顺序有效地迭代所有数字。
如何有效地迭代一组范围?
此外,如果间隔始终为1,是否存在更有效的算法?
答案 0 :(得分:3)
对于每个范围,请记住“当前”值(使用步长从第一个到最后一个)。将其与范围放在优先级队列中,按当前值排序。
如果当前值与上一个值不同,请使用顶部,然后使用它。然后,如果有另一步,则插入下一步。
假设正步长。
template<typename Iterator, typename Operation>
void iterate_ranges (Iterator from, Iterator to, Operation op) {
using R = typename std::iterator_traits<Iterator>::value_type;
using N = typename std::decay<decltype(std::declval<R>().first)>::type;
using P = std::pair<N, R>;
auto compare = [](P const & left, P const & right) {
return left.first > right.first;};
std::priority_queue<P, std::vector<P>, decltype(compare)> queue(compare);
auto push = [& queue] (P p) {
if (p.first < p.second.last) queue.push(p); };
auto next = [](P const & p) -> P {
assert(p.second.step > 0);
return {p.first + p.second.step, p.second}; };
auto init = [&push] (R const & r) {
push({r.first, r}); };
std::for_each(from, to, init);
if (queue.empty()) return;
N last = queue.top().first;
push(next(queue.top()));
queue.pop();
op(last);
while (! queue.empty()) {
P current = queue.top();
queue.pop();
if (current.first != last) {
op(current.first);
last = current.first;
}
push(next(current));
}
}
内存要求:范围数量呈线性。时间要求:每个范围内所有步数的总和。
struct Range {
int first;
int last;
int step; // a better name ...
};
int main() {
Range ranges [] = {
{1, 10, 2},
{2, 50, 5}};
auto print = [](auto n) { std::cout << n << std::endl; };
iterate_ranges(std::begin(ranges), std::end(ranges), print);
}
要获取向量中的所有数字,请使用带有向量引用的lambda并向后推回每个数字。
如果间隔始终为1,是否存在更有效的算法?
您可以将其添加为特例,但我认为没有必要。如果你只有50个范围,那么上面的推力就不会那么贵了。但是,通过所有优化:首先个人资料!
答案 1 :(得分:0)
如果序列很长,您可能只想按顺序获取每个结果,而不存储列表,随时丢弃重复项。
#include <vector>
// algorithm to interpolate integer ranges/arithmetic_sequences
template<typename ASqs, typename Action>
void arithmetic_sequence_union(ASqs arithmetic_sequences, Action action)
{
using ASq = ASqs::value_type;
using T = ASq::value_type;
std::vector<ASq> remaining_asqs(begin(arithmetic_sequences), end(arithmetic_sequences));
while (remaining_asqs.size()) {
// get next value
T current_value = **std::min_element(begin(remaining_asqs), end(remaining_asqs),
[](auto seq1, auto seq2) { return *seq1 < *seq2; }
);
// walk past this value and any duplicates, dropping any completed arithmetic_sequence iterators
for (size_t seq_index = 0; seq_index < remaining_asqs.size(); )
{
ASq &asq = remaining_asqs[seq_index];
if (current_value == *asq // do we have the next value in this sequence?
&& !++asq) { // consume it; was it the last value in this sequence?
remaining_asqs.erase(begin(remaining_asqs) + seq_index);//drop the empty sequence
}
else {
++seq_index;
}
}
action(current_value);
}
}
这需要在&#34;生成器&#34; -type对象中显示的范围。可能看起来非常像检查迭代器的实现,但是迭代器不会暴露知道它们在序列末尾的概念,所以我们可能必须滚动我们自己的简单生成器。
template <typename ValueType, typename DifferenceType>
class arithmetic_sequence {
public:
using value_type = ValueType;
using difference_type = DifferenceType;
arithmetic_sequence(value_type start, difference_type stride, value_type size) :
start_(start), stride_(stride), size_(size) {}
arithmetic_sequence() = default;
operator bool() { return size_ > 0; }
value_type operator*() const { return start_; }
arithmetic_sequence &operator++() { --size_; start_ += stride_; return *this;}
private:
value_type start_;
difference_type stride_;
value_type size_;
};
测试示例:
#include "sequence_union.h"
#include "arithmetic_sequence.h"
#include <cstddef>
#include <array>
#include <algorithm>
#include <iostream>
using Number = uint32_t;
struct Range {
Number first;
Number last;
Number interval;
};
using Range_seq = arithmetic_sequence<Number, Number>;
Range_seq range2seq(Range range)
{
return Range_seq(range.first, range.interval, (range.last - range.first) / range.interval + 1 );
}
int main() {
std::array<Range, 2> ranges = { { { 2,14,3 },{ 2,18,2 } } };
std::array<Range_seq, 2> arithmetic_sequences;
std::transform(begin(ranges), end(ranges), begin(arithmetic_sequences), range2seq);
std::vector<size_t> results;
arithmetic_sequence_union(
arithmetic_sequences,
[&results](auto item) {std::cout << item << "; "; }
);
return 0;
}
// output: 2; 4; 5; 6; 8; 10; 11; 12; 14; 16; 18;