我正在尝试使用vacancy tracking algorithm在C ++中执行多维数组的转置。数组作为void指针,所以我使用地址操作来执行副本。
基本上,有一种算法以偏移量开始,并且像瑞士奶酪一样在数组的整个1-d表示中运行,淘汰其他偏移量直到它回到原始偏移量。然后,你必须从下一个未受影响的偏移开始并再次进行。重复,直到触摸了所有偏移。
现在,我正在使用std :: set来填充所有可能的偏移量(0到阵列尺寸的乘法折叠)。然后,当我浏览算法时,我从集合中删除。我认为这将是最快的,因为我需要随机访问树/集中的偏移并删除它们。然后我需要快速找到下一个未触及/未删除的偏移。
首先,填充集合非常缓慢,似乎必须有更好的方法。它为每个插入单独调用new []。因此,如果我有500万个偏移量,那么就会有500万个新闻,并且会不断地对树进行重新平衡,正如您所知,对于预先排序的列表并不快。
其次,删除也很慢。
第三,假设像int和float这样的4字节数据类型,我实际上使用了与数组本身相同的内存量来存储这个未触及的偏移列表。
第四,确定是否有任何未触及的偏移并且让其中一个偏移很快 - 这是一件好事。
有没有人对这些问题有任何建议?
答案 0 :(得分:1)
没看过那篇论文,
set::insert
之前访问set
,insert
可能是最有效的数据添加方式
vector
和sort
。next = NULL
。如果next == NULL
,则元素有效(尚未删除)。next = this+1
。this+1
中的向量元素迭代到iter->next != iter+1
的第一个元素。然后if ( iter->next == NULL ) return iter; else return iter->next;
(this+1)->next = iter (or) iter->next
之前更新return
以实现摊还的常量时间。next == this
的最后添加一个警卫元素。这不是vector::end
,标志着序列的结束。这是初稿,我把它编码了。未经测试;随时编辑它或让我把它变成一个维基。或者让我知道错误...我不能保证花更多的时间在它上面。我没有在排序版本上完成clear
。并且erase
不会销毁已排序的对象;在sorted_skip_array
被销毁之前不会发生这种情况。
#include <vector>
template< class T, class Alloc >
class skip_array_base {
protected:
struct node {
node *prev, *next;
T val;
node( T const &x = T() ) : prev(), next(), val(x) {}
};
typedef typename Alloc::template rebind< node >::other allocator_type;
typedef std::vector< node, allocator_type > vector_type;
typedef typename vector_type::iterator vector_iterator;
vector_type v;
skip_array_base( allocator_type const &a = allocator_type() ) : v( a ) {}
skip_array_base( skip_array_base const &in ) : v( in.v ) {}
skip_array_base( typename vector_type::size_type s,
typename vector_type::value_type const &x, allocator_type const &a )
: v( s, x, a ) {}
template< class Tcv >
struct iter : vector_iterator {
typedef T value_type;
typedef Tcv &reference;
typedef Tcv *pointer;
iter() {}
iter( vector_iterator const &in )
: vector_iterator( in ) {}
reference operator*() { return vector_iterator::operator*().val; }
pointer operator->() { return &vector_iterator::operator*().val; }
reference operator[]( typename vector_iterator::difference_type n )
{ return vector_iterator::operator[]( n ).val; }
iter &operator++() { vector_iterator::operator++(); return *this; }
iter operator++(int) { return vector_iterator::operator++(0); }
iter &operator--() { vector_iterator::operator--(); return *this; }
iter operator--(int) { return vector_iterator::operator--(0); }
iter &operator+=( typename vector_iterator::difference_type n )
{ vector_iterator::operator+=( n ); return *this; }
iter operator+( typename vector_iterator::difference_type n )
{ return vector_iterator::operator+( n ); }
iter &operator-=( typename vector_iterator::difference_type n )
{ vector_iterator::operator-=( n ); return *this; }
iter operator-( typename vector_iterator::difference_type n )
{ return vector_iterator::operator-( n ); }
};
public:
typedef typename vector_type::size_type size_type;
void swap( skip_array_base &r ) { v.swap( r.v ); }
skip_array_base &operator=( skip_array_base const &x ) {
v = x.v;
return *this;
}
size_type size() const { return v.size() - 2; }
size_type max_size() const { return v.max_size() - 2; }
bool empty() const { return v.size() > 2; }
bool operator== ( skip_array_base const &r ) const { return v == r.v; }
bool operator!= ( skip_array_base const &r ) const { return v != r.v; }
bool operator< ( skip_array_base const &r ) const { return v < r.v; }
bool operator> ( skip_array_base const &r ) const { return v > r.v; }
bool operator<= ( skip_array_base const &r ) const { return v <= r.v; }
bool operator>= ( skip_array_base const &r ) const { return v >= r.v; }
void clear() { v.erase( ++ v.begin(), -- v.end() ); }
};
template< class T, class Alloc >
class sorted_skip_array;
template< class T, class Alloc = std::allocator<T> >
class skip_array_prelim : public skip_array_base< T, Alloc > {
typedef skip_array_base< T, Alloc > base;
typedef typename base::vector_type vector_type;
using skip_array_base< T, Alloc >::v;
public:
typedef T value_type;
typedef typename Alloc::reference reference;
typedef typename Alloc::const_reference const_reference;
typedef typename base::template iter< value_type > iterator;
typedef typename base::template iter< const value_type > const_iterator;
typedef typename vector_type::difference_type difference_type;
typedef typename vector_type::size_type size_type;
typedef typename vector_type::allocator_type allocator_type;
skip_array_prelim( allocator_type const &a = allocator_type() )
: base( 2, value_type(), a ) {}
skip_array_prelim( skip_array_prelim const &in )
: base( in ) {}
skip_array_prelim( size_type s, value_type const &x = value_type(),
allocator_type const &a = allocator_type() )
: base( s + 2, x, a ) {}
template< class I >
skip_array_prelim( I first, I last,
allocator_type const &a = allocator_type(),
typename I::pointer = typename I::pointer() )
: base( 1, value_type(), a ) {
v.insert( v.end(), first, last );
v.push_back( value_type() );
}
iterator begin() { return ++ v.begin(); }
iterator end() { return -- v.end(); }
const_iterator begin() const { return ++ v.begin(); }
const_iterator end() const { return -- v.end(); }
reference operator[]( size_type n ) { return v[ n + 1 ]; }
const_reference operator[]( size_type n ) const { return v[ n + 1 ]; }
iterator insert( iterator pos, value_type const &x )
{ return v.insert( pos, x ); }
iterator insert( iterator pos, size_type n, value_type const &x )
{ return v.insert( pos, n, x ); }
template< class I >
iterator insert( iterator pos, I first, I last,
typename I::pointer = typename I::pointer() )
{ return v.insert( pos, first, last ); }
iterator erase( iterator i ) { return v.erase( i ); }
iterator erase( iterator first, iterator last )
{ return v.erase( first, last ); }
};
template< class T, class Alloc = std::allocator<T> >
class sorted_skip_array : public skip_array_base< T, Alloc > {
typedef skip_array_base< T, Alloc > base;
typedef typename base::vector_type vector_type;
typedef typename vector_type::iterator vector_iterator;
typedef typename base::node node;
using skip_array_base< T, Alloc >::v;
template< class Tcv >
struct iter : base::template iter< Tcv > {
typedef std::bidirectional_iterator_tag iterator_category;
typedef Tcv &reference;
typedef Tcv *pointer;
iter() {}
iter( vector_iterator const &x ) : base::template iter< Tcv >( x ) {}
iter &operator++() { increment< &node::next, 1 >(); return *this; }
iter operator++(int)
{ iter r = *this; increment< &node::next, 1 >(); return r; }
iter &operator--() { increment< &node::prev, -1 >(); return *this; }
iter operator--(int)
{ iter r = *this; increment< &node::prev, -1 >(); return r; }
private:
template< node *node::*link, int inc >
void increment() {
vector_iterator memo = *this; // un-consts a const_iterator
node *pen = &*( memo += inc );
while ( pen->*link && pen->*link != pen ) pen = pen->*link;
*this = iter( vector_iterator( (*memo).*link = pen ) );
}
};
public:
typedef T value_type;
typedef typename Alloc::reference reference;
typedef typename Alloc::const_reference const_reference;
typedef iter< T > iterator;
typedef iter< const T > const_iterator;
typedef typename vector_type::difference_type difference_type;
typedef typename vector_type::size_type size_type;
sorted_skip_array( skip_array_prelim<T,Alloc> &x ) {
sort( x.begin(), x.end() );
swap( x );
}
iterator begin() { return ++ iterator( v.begin() ); }
iterator end() { return iterator( -- v.end() ); }
const_iterator begin() const { return ++ const_iterator( v.begin() ); }
const_iterator end() const { return const_iterator( -- v.end() ); }
iterator erase( iterator i ) {
vector_iterator vi = i;
vi->prev = &* vi[-1];
vi->next = &* vi[1];
//vi->val->~value_type(); // don't bother with allocator rigmarole
return ++ i;
}
iterator erase( iterator first, iterator last ) {
if ( first != last ) {
vector_iterator vf = first, vl = last - 1;
vl->prev = &* vf[-1];
vf->next = &* vl[1];
}
return last;
}
};
答案 1 :(得分:0)
我不是100%肯定在这里,但您可以动态使用std::next_permutation
来找出您存储在集合中的信息吗?链接中的算法看起来不像需要像std :: set这样的大型数据结构来处理这些事情...
您可能还想考虑创建固定数组而不是集合。即使该数组需要存储的元素数量是该集合的3倍,但请记住,除了所讨论的数据元素之外,std :: set中的每个节点可能至少占用两个指针的空间。因此,您应该节省空间并在动态分配中加快速度。
对于插入大量元素然后稍后读取大量元素的情况,与std::binary_search
组合的有序向量将比std::set
执行得更好。相比之下,std::set
针对交错插入和移除进行了优化。如果您的插入和删除是分开的,只需对向量进行排序并使用二进制搜索。您可能希望使用某种标记来标记从向量中删除而不是实际删除每次以减少复制。然后可以立即销毁整个载体。
希望有所帮助:)
答案 2 :(得分:0)
我发现最好的方式比套装快12倍。我使用boost dynamic_bitset,它允许我使用bitset并决定运行时的位数。
编辑:如果将来有人读到这个...这个算法并不比使用正常大小(4-8个字节)的数据元素的标准复制和回写转换方法快。它具有较大的数据大小(例如,如果您复制大型结构,例如128字节)。