Generate ordered cartesian product of sequences

问题来源: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++的支持不够,部分代码需要修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cqdjyy01234

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值