问题来源:http://stackoverflow.com/questions/25391423/ordered-cartesian-product-of-arrays
2个序列时的情形已解决http://stackoverflow.com/questions/4299458/efficient-sorted-cartesian-product-of-2-sorted-array-of-integers,本文将其扩展到一般情形。
#ifndef MY_cartesian_HEADER
#define MY_cartesian_HEADER
#include
#include
#include
#include
#include
#include
#include
template
class ordered_CP { static_assert(std::is_convertible
::iterator_category, std::input_iterator_tag>::value, "It1 should be input iterator"); static_assert(std::is_convertible
::iterator_category, std::input_iterator_tag>::value, "It2 should be input iterator"); public: using value_type1 = typename std::iterator_traits
::value_type; static_assert(std::is_copy_constructible
::value, "value_type1 should be copy constructible"); using value_type2 = typename std::iterator_traits
::value_type; static_assert(std::is_copy_constructible
::value, "value_type2 should be copy constructible"); using value_type = std::result_of_t
; static_assert(std::is_copy_constructible
::value, "value_type should be copy constructible"); private: It1 b1, e1; size_t s2{};// 第二个序列的长度 size_t to_remove{};// backup中需要移除的元素个数 Prod prod; struct backup_element { value_type1 value; size_t count;// 引用计数 }; using backup_t = std::vector
; backup_t backup;// 因为It1是input iterator,所以需要将数据进行备份 struct heap_element { value_type value;// 笛卡尔积 typename backup_t::size_type pos1; value_type2 v2; }; struct HeapPred { Pred pred; HeapPred(Pred pred_) : pred{ std::move(pred_) } {} bool operator()(heap_element const &a, heap_element const &b) { return pred(a.value, b.value); } } pred; std::vector
heap;// 最大堆 public: ordered_CP(It1 b1_, It1 e1_, It2 b2, It2 e2, Prod prod_, Pred pred_) : b1{ std::move(b1_) }, e1{ std::move(e1_) }, prod( std::move(prod_) ), pred{ std::move(pred_) } { if (b1 != e1 && b2 != e2) { auto head = *b1++; backup.push_back(backup_element{ head, 0 });// 不知道第二个序列的长度,只好先初始化为0 while (b2 != e2) { ++s2; auto tmp = *b2++; heap.push_back( heap_element{ prod(head, tmp), 0, std::move(tmp) } );// 插入乘积 std::push_heap(heap.begin(), heap.end(), pred); } backup.front().count = s2;// 更新引用计数 } } // 可以move,不可复制,因为typename backup_t::iterator复制之后失去意义 ordered_CP(ordered_CP const &) = delete; ordered_CP(ordered_CP &&) = default; ordered_CP& operator=(ordered_CP const &) = delete; ordered_CP& operator=(ordered_CP &&) = default; auto&& get() const noexcept { return heap.front().value; } void next() { assert(!heap.empty()); // 去掉堆顶的元素 std::pop_heap(heap.begin(), heap.end(), pred); heap_element tmp = std::move(heap.back()); heap.pop_back(); assert(tmp.pos1 < backup.size()); if (--backup[tmp.pos1++].count == 0) {// 该元素不再需要 ++to_remove; assert(tmp.pos1 == to_remove); } // 添加新元素 if (tmp.pos1 != backup.size()) { heap.push_back( heap_element{ prod(backup[tmp.pos1].value, tmp.v2), tmp.pos1, std::move(tmp.v2) } );// 插入乘积 } else if (b1 != e1) { auto head = *b1++; try { backup.push_back(backup_element{ head, s2 }); } catch (std::bad_alloc const &) {// 内存不足,将该删除的元素删除 if (to_remove) { backup.erase(backup.begin(), std::next(backup.begin(), to_remove)); for (ele : heap) {// 修改索引 assert(ele.pos1 >= to_remove); ele.pos1 -= to_remove; } to_remove = 0; backup.push_back(backup_element{ head, s2 }); } else throw;// 真的没有内存了 } heap.push_back( heap_element{ prod(head, tmp.v2), backup.size() - 1, std::move(tmp.v2) });// 插入乘积 } else { return; } std::push_heap(heap.begin(), heap.end(), pred); } explicit operator bool() const noexcept { return !heap.empty(); } }; // 输入迭代器 // 若a = b,*++a不一定等于*++b template
class ordered_CPI : public std::iterator
::value_type> { using ordered_cartesian_product_t = ordered_CP
; std::shared_ptr
ptr{}; std::aligned_storage_t
::value> next; auto&& get_next() const noexcept { assert(ptr);// ptr不空时才有意义 return reinterpret_cast
(next); } auto&& get_next() noexcept { assert(ptr);// ptr不空时才有意义 return reinterpret_cast
(next); } public: ordered_CPI() = default; ordered_CPI(ordered_cartesian_product_t obj) : ptr{ std::make_shared
(std::move(obj)) } { if (*ptr) new(&next) value_type(ptr->get()); } ordered_CPI(ordered_CPI const &other) : ptr{ other.ptr } { if (other.ptr) new(&next) value_type(other.get_next());// copy construct } ordered_CPI(ordered_CPI &&other): ptr{ other.ptr } { if (other.ptr) new(&next) value_type(std::move(other.get_next()));// move construct } ordered_CPI& operator=(ordered_CPI const &other) { if (other.ptr) {// copy other.get_next() if (ptr) { get_next() = other.get_next();// copy assign } else { new(&next) value_type(other.get_next());// copy construct } } else { if (ptr) get_next().~value_type();// destruct } ptr = other.ptr; } ordered_CPI& operator=(ordered_CPI &&other) { if (other.ptr) {// move other.get_next() if (ptr) { get_next() = std::move(other.get_next());// move assign } else { new(&next) value_type(std::move(other.get_next()));// move construct } } else { if (ptr) get_next().~value_type();// destruct } ptr = other.ptr; } ~ordered_CPI() { if(ptr) get_next().~value_type(); } auto&& operator*() noexcept { return get_next(); } auto&& operator*() const noexcept { return get_next(); } auto&& operator->() noexcept { return &get_next(); } auto&& operator->() const noexcept { return &get_next(); } ordered_CPI& operator++() { assert(ptr && *ptr); ptr->next(); if (*ptr) { get_next() = ptr->get(); } else { get_next().~value_type(); ptr = nullptr;// 迭代完毕 } return *this; } ordered_CPI operator++(int) { auto res = *this; operator++(); return res; } // ptr相同则认为相等 bool operator==(ordered_CPI const &other) const noexcept { return ptr == other.ptr; } bool operator!=(ordered_CPI const &other) const noexcept { return !operator==(other); } }; // [b1, e1), [b2, e2)应当是排过序的 // prod用于做笛卡尔积 // pred用于排列笛卡尔积的结果 template
auto cartesian_product_iterator(ordered_CP
obj) { using iterator = ordered_CPI
; return std::make_pair(iterator{ std::move(obj) }, iterator{}); } // Group1 template
auto cartesian_product(It1 b1, It1 e1, It2 b2, It2 e2, Prod prod, Pred pred) { return ordered_CP
{ std::move(b1), std::move(e1), std::move(b2), std::move(e2), std::move(prod), std::move(pred) }; } template
auto cartesian_product(std::pair
r1, It2 b2, It2 e2, Prod prod, Pred pred) { return cartesian_product(std::move(r1.first), std::move(r1.second), std::move(b2), std::move(e2), std::move(prod), std::move(pred)); } template
auto cartesian_product(ordered_CP
obj, It2 b2, It2 e2, Prod prod, Pred pred) { return cartesian_product(cartesian_product_iterator(std::move(obj)), std::move(b2), std::move(e2), std::move(prod), std::move(pred)); } template
auto cartesian_product(C1 const &c1, It2 b2, It2 e2, Prod prod, Pred pred) { return cartesian_product(std::cbegin(c1), std::cend(c1), std::move(b2), std::move(e2), std::move(prod), std::move(pred)); } // Group2 template
auto cartesian_product(It1 b1, It1 e1, std::pair
r2, Prod prod, Pred pred) { return ordered_CP
{ std::move(b1), std::move(e1), std::move(r2.first), std::move(r2.second), std::move(prod), std::move(pred) }; } template
auto cartesian_product(std::pair
r1, std::pair
r2, Prod prod, Pred pred) { return cartesian_product(std::move(r1.first), std::move(r1.second), std::move(r2.first), std::move(r2.second), std::move(prod), std::move(pred)); } template
auto cartesian_product(ordered_CP
obj, std::pair
r2, Prod prod, Pred pred) { return cartesian_product(cartesian_product_iterator(std::move(obj)), std::move(r2.first), std::move(r2.second), std::move(prod), std::move(pred)); } template
auto cartesian_product(C1 const &c1, std::pair
r2, Prod prod, Pred pred) { return cartesian_product(std::cbegin(c1), std::cend(c1), std::move(r2.first), std::move(r2.second), std::move(prod), std::move(pred)); } // Group3 template
auto cartesian_product(It1 b1, It1 e1, ordered_CP
obj, Prod prod, Pred pred) { return ordered_CP
{ std::move(b1), std::move(e1), cartesian_product_iterator(std::move(obj)), std::move(prod), std::move(pred) }; } template
auto cartesian_product(std::pair
r1, ordered_CP
obj, Prod prod, Pred pred) { return cartesian_product(std::move(r1.first), std::move(r1.second), cartesian_product_iterator(std::move(obj)), std::move(prod), std::move(pred)); } template
auto cartesian_product(ordered_CP
obj1, ordered_CP
obj2, Prod prod, Pred pred) { return cartesian_product(cartesian_product_iterator(std::move(obj1)), cartesian_product_iterator(std::move(obj2)), std::move(prod), std::move(pred)); } template
auto cartesian_product(C1 const &c1, ordered_CP
obj, Prod prod, Pred pred) { return cartesian_product(std::cbegin(c1), std::cend(c1), cartesian_product_iterator(std::move(obj)), std::move(prod), std::move(pred)); } // Group4 template
auto cartesian_product(It1 b1, It1 e1, C2 const &c2, Prod prod, Pred pred) { return ordered_CP
{ std::move(b1), std::move(e1), std::cbegin(c2), std::cend(c2), std::move(prod), std::move(pred) }; } template
auto cartesian_product(std::pair
r1, C2 const &c2, Prod prod, Pred pred) { return cartesian_product(std::move(r1.first), std::move(r1.second), std::cbegin(c2), std::cend(c2), std::move(prod), std::move(pred)); } template
auto cartesian_product(ordered_CP
obj1, C2 const &c2, Prod prod, Pred pred) { return cartesian_product(cartesian_product_iterator(std::move(obj1)), std::cbegin(c2), std::cend(c2), std::move(prod), std::move(pred)); } template
auto cartesian_product(C1 const &c1, C2 const &c2, Prod prod, Pred pred) { return cartesian_product(std::cbegin(c1), std::cend(c1), std::cbegin(c2), std::cend(c2), std::move(prod), std::move(pred)); } // for chain(chain_CP(), ...) struct CP_chain { template
auto operator()(ordered_CP
a, C const &b, Prod prod, Pred pred) const noexcept { return cartesian_product(std::move(a), b, std::move(prod), std::move(pred)); } template
auto operator()(ordered_CP
a, ordered_CP
b, Prod prod, Pred pred) const noexcept { return cartesian_product(std::move(a), std::move(b), std::move(prod), std::move(pred)); } template
auto operator()(C const &b, ordered_CP
a, Prod prod, Pred pred) const noexcept { return cartesian_product(b, std::move(a), std::move(prod), std::move(pred)); } template
auto operator()(C1 const &a, C2 const &b, Prod prod, Pred pred) const noexcept { return cartesian_product(a, b, std::move(prod), std::move(pred)); } }; #endif
测试代码
#include <cartesian.h>
#include <iostream>
#include <functional>
int main() {
using type = int;
std::vector<type> a = { 10, 9, 2, 1, 1 }, b, c;
{
for (a1 : a) for (a2 : a) for (a3 : a) for (a4 : a) b.push_back(a1*a2*a3*a4);
std::sort(b.begin(), b.end(), std::greater<type>());
}
{
auto prod = std::multiplies<type>();
auto pred = std::less<type>();
auto it = cartesian_product_iterator(cartesian_product(cartesian_product(cartesian_product(a, a, prod, pred), a, prod, pred), a, prod, pred));
std::copy(it.first, it.second, std::back_inserter(c));
}
std::cout << "equal? " << (b == c) << "\n";
for (val : c) std::cout << val << " ";
std::cout << "\n";
return 0;
}
运行结果
利用如下的chain函数,代码可以更加紧凑。
#ifndef MY_chain_HEADER
#define MY_chain_HEADER
#include
#include
namespace {
template
auto chain_(std::enable_if_t<(i > 1)>*, Lambda&& lambda, T&& arg, FixArgs&&... args) {
return lambda(
chain_
(nullptr, std::forward
(lambda), std::forward
(arg), std::forward
(args)...), std::get
(arg), std::forward
(args)... ); } template
auto chain_(std::enable_if_t<(i == 1)>*, Lambda&& lambda, T&& arg, FixArgs&&... args) { return lambda(std::get<0>(arg), std::get<1>(arg), std::forward
(args)...); } } // return lambda(...lambda(lambda(arg[0], arg[1], args...), arg[2], args...), arg[-1], args) template
auto chain(Lambda&& lambda, Arg&& arg, Args&&... args) { size_t constexpr N = std::tuple_size
>::value; static_assert(N >= 2, "tuple size at least two"); return chain_
(nullptr, std::forward
(lambda), std::forward
(arg), std::forward
(args)...); } #endif
测试代码
#include <cartesian.h>
#include <iostream>
#include <functional>
#include <chain.h>
int main() {
using type = int;
std::vector<type> a = { 10, 9, 2, 1, 1 }, b, c;
{
for (a1 : a) for (a2 : a) for (a3 : a) for (a4 : a) b.push_back(a1*a2*a3*a4);
std::sort(b.begin(), b.end(), std::greater<type>());
}
{
auto it = cartesian_product_iterator(
chain(CP_chain(), std::forward_as_tuple(a, a, a, a), std::multiplies<type>(), std::less<type>())
);
std::copy(it.first, it.second, std::back_inserter(c));
}
std::cout << "equal? " << (b == c) << "\n";
for (val : c) std::cout << val << " ";
std::cout << "\n";
return 0;
}
由于GCC对c++的支持不够,部分代码需要修改。