3

空室追跡アルゴリズムを使用して、C++ で多次元配列の転置を実行しようとしています。配列は void ポインターとして提供されるため、アドレス操作を使用してコピーを実行しています。

基本的に、オフセットから始まり、スイス チーズのように配列の 1 次元表現全体を処理し、元のオフセットに戻るまで他のオフセットをノックアウトするアルゴリズムがあります。次に、次の手つかずのオフセットから始めて、もう一度やり直さなければなりません。すべてのオフセットに触れるまで繰り返します。

現在、std::set を使用して、考えられるすべてのオフセット (0 から配列の次元の倍数まで) を埋めています。次に、アルゴリズムを実行しながら、セットから消去します。ツリー/セット内のオフセットにランダムにアクセスして削除する必要があるため、これが最速だと思います。次に、手つかず/削除されていない次のオフセットをすばやく見つける必要があります。

まず第一に、セットを埋めるのは非常に遅く、もっと良い方法があるに違いないと思われます. すべての挿入に対して個別に new[] を呼び出しています。したがって、500 万のオフセットがある場合、500 万のニュースがあり、さらにツリーのバランスを常に再調整しますが、事前に並べ替えられたリストでは高速ではありません。

第二に、削除も遅いです。

第 3 に、int や float などの 4 バイトのデータ型を想定すると、実際には配列自体と同じ量のメモリを使用して、この変更されていないオフセットのリストを格納しています。

第 4 に、変更されていないオフセットがあるかどうかを判断し、そのうちの 1 つを取得するのが高速です。これは良いことです。

これらの問題のいずれかに対する提案はありますか?

4

3 に答える 3

1

その論文を読んでいなくても、

  • set::insertset次の前にアクセスする場合は、おそらくデータを追加する最も効率的な方法です。insert
  • 一方、セットを一度に作成する場合は、vectorとを使用するのが最善ですsort
  • ベクター要素に次へのポインターを追加すると、並べ替えられたベクターからの削除は簡単です。
    • 初期化しnext = NULLます。の場合next == NULL、要素は有効です (削除されていません)。
    • 削除するには、 を設定しnext = this+1ます。
    • 次に取得するには、ベクトル要素を からthis+1最初の要素 whereまで反復処理し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、シーケンスの終わりを示します。

これが最初のドラフトです。私はそれをコーディングしました。未検証; 自由に編集するか、wiki にするよう私に依頼してください。または、バグを教えてください... これ以上の時間を費やすことは保証できません。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;
    }
};
于 2010-03-24T21:25:20.870 に答える
0

セットよりも約12倍高速な最良の方法を見つけました。ビットセットを使用して、実行時にビット数を決定できるブースト dynamic_bitsetを使用します。

編集:将来誰かがこれを読む場合...このアルゴリズムは、通常のサイズ(4〜8バイト)のデータ要素を使用した転置の標準的なコピーおよび書き戻し方法よりも高速ではありません。データサイズが大きいほど高速です(128バイトなどの大きな構造をコピーする場合など)。

于 2010-03-25T15:02:01.403 に答える
0

ここで 100% 確信があるわけではありませんがstd::next_permutation、オンザフライを使用して、セットに保存していた情報を把握できますか? リンクのアルゴリズムは、これらの種類のものを処理するために std::set のような大きなデータ構造を必要とするようには見えません...

セットの代わりに固定配列の作成を検討することもできます。その配列がパフォーマンスのためにセットの 3 倍の要素を格納する必要がある場合でも、std::set 内の各ノードは、問題のデータ要素に加えて、おそらく少なくとも 2 つのポインターのスペースを占めていることに注意してください。したがって、スペースを節約し、動的割り当てで多くの速度を上げる必要があります。

多くの要素を挿入し、後で多くの要素を読み取る場合は、並べ替えられたベクトルを と組み合わせるとstd::binary_search、for よりもパフォーマンスが向上します。は、対照的に、インターリーブされた挿入と削除用に最適化されています。挿入と削除が分離されている場合は、ベクトルを並べ替えてバイナリ検索を使用してください。コピーを減らすために毎回実際に削除するのではなく、何らかのフラグを使用してベクトルから削除されたことをマークしたい場合があります。その後、ベクター全体を一度に破壊できます。std::setstd::set

それが役立つことを願っています:)

于 2010-03-24T21:24:47.070 に答える