2

説明 :

次のクラス LabSetInt64 を実装しました (以下のコードを参照)。

ここでの目標は、大きな整数 (最大 10M の値) の大きなセットをできるだけ速く操作することです。私の主な要件は次のことに焦点を当てています。

  • !Crucial : セットのサイズ/カーディナリティをできるだけ速く取得する
  • !重要: セットを非常に高速に反復できること

では、以下の実装から始めて、まだ 2 つの点についてお話したいと思います。

「popcount()」遅延実装

int LabSetInt64::popcount(uint64_t x) {
    int count;
    for (count=0; x; count++)
        x &= x-1;
    return count;
}

私が選択した実装が市場で最も遅いものの 1 つであることはわかっていますが、自分でより良い実装を見つけることができませんでした。したがって、より良いもの(もちろん64ビット)を教えていただければ...

カーディナリティを計算するための非常に効率的なアルゴリズムについて聞きました: "MIT HAKMEM 169" アルゴリズム >>

// MIT HAKMEM.
// MIT HAKMEM Count is funky. Consider a 3 bit number as being 4a+2b+c. If we
// shift it right 1 bit, we have 2a+b. Subtracting this from the original gives
// 2a+b+c. If we right-shift the original 3-bit number by two bits, we get a,
// and so with another subtraction we have a+b+c, which is the number of bits
// in the original number. How is this insight employed? The first assignment
// statement in the routine computes tmp. Consider the octal representation of
// tmp. Each digit in the octal representation is simply the number of 1¡äs in
// the corresponding three bit positions in n. The last return statement sums
// these octal digits to produce the final answer. The key idea is to add
// adjacent pairs of octal digits together and then compute the remainder
// modulus 63. This is accomplished by right-shifting tmp by three bits, adding
// it to tmp itself and ANDing with a suitable mask. This yields a number in
// which groups of six adjacent bits (starting from the LSB) contain the number
// of 1¡äs among those six positions in n. This number modulo 63 yields the
// final answer. For 64-bit numbers, we would have to add triples of octal
// digits and use modulus 1023. This is HACKMEM 169, as used in X11 sources.
// Source: MIT AI Lab memo, late 1970¡äs.
// http://gurmeet.net/2008/08/05/fast-bit-counting-routines/
int CheckMIT(unsigned int n) 
{
   /* works for 32-bit numbers only    */
   /* fix last line for 64-bit numbers */
   register unsigned int tmp;

   tmp = n - ((n >> 1) & 033333333333)
           - ((n >> 2) & 011111111111);

   // the remainder of 63 for i equals the sum of 7 octal numbers which
   // consititutes the 32-bit integer.
   // For example, 03456 % 63 == 034 + 056
   return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}

上記のアルゴリズムに関する私の問題は、それを "uint64_t" (64 ビット) バージョンに変換するのに十分なほど理解していないことです (それを行うためのいくつかの指示にもかかわらず)。

ですから、あなたの 1 人がその作業を手伝ってくれる (または少なくとも理解するのを手伝ってくれる) なら、それは素晴らしいことです。

ここにLabSetInt64.hがあります:

/*
 * LabSetInt64.h
 *
 *  Created on: Feb 20, 2013
 *      Author: golgauth
 */

#ifndef LABSETINT64_H_
#define LABSETINT64_H_

#include <ctype.h>
#include <stdio.h>
#include <string.h>

#include <iostream>
#include <stdint.h>
#include <limits>
#include <algorithm>

#include <LabTimeUtils.h>

using namespace std;


namespace elps {


// Lets assume we need at most 1M distinct indices in our sets
#define DEFAULT_SIZE_IN_BITS 1000000


class LabSetInt64
{
public:

    LabSetInt64();
    LabSetInt64(int sizeinbits);
    LabSetInt64(const LabSetInt64 &);
    ~LabSetInt64();


    void Clear();
    void Resize(int sizeinbits);
    void Set(int i);
    void UnSet(int i);
    void Set(int i, bool b);
    bool Find(int i);
    int  Cardinality();
    int  NextSetBit(int i);

    void Print();

    /**
     * Should have been "=" operator, but this overload is not compatible
     * with Cython, so we'll use "<<" instead.
     * @param
     * @return
     */
    const LabSetInt64 & operator << ( const LabSetInt64 & );
    LabSetInt64         operator ~  () const;
    LabSetInt64         operator +  ( const LabSetInt64 & );
    LabSetInt64         operator *  ( const LabSetInt64 & );
    LabSetInt64         operator -  ( const LabSetInt64 & );
    LabSetInt64         operator ^  ( const LabSetInt64 & );

private:
    uint64_t *data;
    int data_len;
    int bits_size;

    int popcount(uint64_t x);
    int nb_trailing_zeros(uint64_t v);
};


} /* namespace elps */
#endif /* LABSETINT64_H_ */

そして、ここではLabSetInt64.cpp :

/*
 * LabSetInt64.cpp
 *
 *  Created on: Feb 20, 2013
 *      Author: golgauth
 */

#include "LabSetInt64.h"

namespace elps {


/** PUBLICS **/


LabSetInt64::LabSetInt64() : LabSetInt64(DEFAULT_SIZE_IN_BITS) {
}

LabSetInt64::LabSetInt64(int sizeinbits) {
    bits_size = sizeinbits;
    data_len = (bits_size + 63) / 64;
    data = new uint64_t[data_len];
}

LabSetInt64::LabSetInt64(const LabSetInt64& src) {
    bits_size = src.bits_size;
    data_len = src.data_len;
    data = new uint64_t[data_len];
    for( int i = 0; i < data_len; i++ )            // copy into this set
        data[i] = src.data[i];
}

LabSetInt64::~LabSetInt64() {
}


void LabSetInt64::Clear() {
    std::fill_n(data, data_len, 0);
}

void LabSetInt64::Resize(int sizeinbits) {
    bits_size = sizeinbits + 1;
    int size = (bits_size + 63) / 64;
    uint64_t *new_data = new uint64_t[size];

    memcpy( new_data, data, data_len * sizeof(uint64_t) );

    data_len = size;
    delete [] data;
    data = new_data;
}


void LabSetInt64::Set(int i) {
    data[i / 64] |= (1l << (i % 64));
}

void LabSetInt64::UnSet(int i) {
    data[i / 64] &= ~(1l << (i % 64));
}

void LabSetInt64::Set(int i, bool b) {
    if(b) Set(i); else UnSet(i);
}

bool LabSetInt64::Find(int i) {
    return ((data[i / 64]) & (1l << (i % 64)));           // binary AND;
}


int LabSetInt64::Cardinality() {
    int sum = 0;
    for(int i=0; i<data_len; i++)
        sum += popcount(data[i]);
    //sum += __builtin_popcount(data[i]);
    return sum;
}

int LabSetInt64::NextSetBit(int i) {
    int x = i / 64;
    long w = data[x];
    w >>= (i % 64);
    if (w != 0) {
        return i + nb_trailing_zeros(w);
    }
    ++x;
    for (; x < data_len; ++x) {
        if (data[x] != 0) {
            return x * 64 + nb_trailing_zeros(data[x]);
        }
    }
    return -1;
}


void LabSetInt64::Print() {
    int cur_id = this->NextSetBit(0);
    cout << "\nSet size : " << this->Cardinality() << endl;
    cout << "{ ";
    while (cur_id != -1)
    {
        cout << (cur_id) << " ";
        cur_id = this->NextSetBit(cur_id+1);
    }
    cout << "}" << endl;;
}


/** PRIVATES **/

//This is better when most bits in x are 0
//It uses 3 arithmetic operations and one comparison/branch per "1" bit in x.
int LabSetInt64::popcount(uint64_t x) {
    int count;
    for (count=0; x; count++)
        x &= x-1;
    return count;
}

// output: c will count v's trailing zero bits,
// so if v is 1101000 (base 2), then c will be 3
int LabSetInt64::nb_trailing_zeros(uint64_t v) {
    int c;
    if (v)
    {
        v = (v ^ (v - 1)) >> 1;         // Set v's trailing 0s to 1s and zero rest
        for (c = 0; v; c++) {
            v >>= 1;
        }
    }
    else
        c = 8 * sizeof(v);

    return c;
}


/** ***************************************************************************
*********************************   OPERATORS   *******************************
*******************************************************************************/

/** PUBLICS **/


/*******************************************************************************
 * TODO >> May be better to use  "DEFAULT_SIZE_IN_BITS" instead of "data_len"  *
 *         in all the operators !!!                                            *
 *                                                                             *
 * => For now, we assume that all the Sets are created with the default        *
 *    constructor (aka : bit_size = DEFAULT_SIZE_IN_BITS)                      *
 *******************************************************************************/


/**
 *  "operator = " assigns the right hand side to this set.
 *  returns: nothing
 */
const LabSetInt64 &LabSetInt64::operator << ( const LabSetInt64 &rhs )
{
    if( &rhs != this )                              // avoid self assignment
    {
        this->Resize(rhs.bits_size - 1);
        for( int i = 0; i < data_len; i++ )         // copy into this set
            data[i] = rhs.data[i];
    }
    return *this;                                   // enable x << y << z;
}


/**
 * "operator ~ " performs set complement operation (not).
 * returns: pointer to set
 */
LabSetInt64 LabSetInt64::operator ~ () const
{
    LabSetInt64 rv;
    for( int i = 0; i < data_len; i++ )
        rv.data[i] = ~data[i];                      // bitwise complement
    return rv;
}


/**
 * "operator + " performs set union operation (or).
 * returns: pointer to set
 */
LabSetInt64 LabSetInt64::operator + ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;
    for( int i = 0; i < data_len; i++ )
        rv.data[i] = data[i] | rhs.data[i];         // bitwise OR
    return rv;
}


/**
 * "operator * " performs set intersection operation (and).
 * returns: pointer to set
 */
LabSetInt64 LabSetInt64::operator * ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;
    for( int i = 0; i < data_len; i++ )
        rv.data[i] = data[i] & rhs.data[i];         // bitwise AND
    return rv;
}


/**
 * "operator - " performs set difference operation.
 * returns: pointer to set
 */
LabSetInt64 LabSetInt64::operator - ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;
    for( int i = 0; i < data_len; i++ )
        rv.data[i] = data[i] & ( ~rhs.data[i] );    // bitwise a AND ~b
    return rv;
}


/**
 * "operator ^ " performs set symmetric difference operation (xor).
 * returns: pointer to set
 */
LabSetInt64 LabSetInt64::operator ^ ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;
    for( int i = 0; i < data_len; i++ )
        rv.data[i] = data[i] ^ rhs.data[i];         // bitwise XOR
    return rv;
}


/** *****************************************************************************
*********************************************************************************/


} /* namespace elps */

実装の残りの部分については、パフォーマンスに非常に満足していますが、もう一度、良いコメントをいただければ幸いです。

お時間をいただきありがとうございます。

編集: まあ、結局のところ、「スパース」ソリューションには完全には納得していません。なぜなら、ユニオン、インターセクション、差分機能が必要だからです。これは、「ビットごと」のバージョンよりもはるかにコストがかかると思います潜在的に膨大な量のアイテムを介して)、上記の「MIT HAKMEM」アルゴリズムを 64 ビットに変換する方法についてのヘルプを得ることにまだ興味があります。どうもありがとう。

編集: このバージョンにはいくつかの小さな欠陥が含まれています。以下のソースコードをよく見てください。

4

2 に答える 2

4

Russ Cox は彼の Web サイトで、O(n) 反復 (U が最大値である O(U) とは対照的) と O(1) 挿入、削除、カウントを持つ非常に単純な整数セット データ構造について説明しています。それは少しいじる必要はありません。std::vectorまた、より多くのスペースを使用する傾向がありますが、(単純な配列の代わりに単純に使用することで) O(1) 償却追加を行うように拡張できます。

(サンプルコードを貼り付けますが、Cox の説明は十分に明快で、バグを紹介するだけです。)

于 2013-02-20T13:38:43.377 に答える
1

したがって、他のいくつかのライブラリ ( BitMagicおよびいくつかの C++ と Java の実装) と多くのベンチマークを検討した後:

(高速な組み合わせ演算子が必要なため、「スパース」オプションを拒否しました)...

私はいくつかの興味深い改善に行きました。

主な改善点は、カーディナリティをオンザフライで維持するようになったことです (つまり、高速カウントはもはや大きな問題ではありません)。

改良点:

  • 反復 (これが私の最初の問題でした) は (BitMagic) よりも少し遅いですが、問題ありません。BitMagic とのパフォーマンスを比較すると、やはり私のクラスはそれほど悪くないことが確認されました... しかし、BitMagic の目的は主に「圧縮によるメモリ節約」であり、処理時にブースト (ここでは避けたい) よりもはるかに遅いです。大きなベクトルで。
  • カーディナリティの計算方法が大幅に改善されました。

より良い反復:

inline int nb_trailing_zeros(uint64_t v) {
    if (v & 1l) return 0;           // Fast check if no trailing zeros
                                    // Avoids a useless call to __builtin_ctzl()
    else return __builtin_ctzl(v);
}

2 つの改善: gcc の組み込み関数を使用して末尾のゼロをカウントし、ブロックが 1 で始まるときに関数を呼び出さないようにすることで、反復中のパフォーマンスが大幅に改善されました。

より良いカウント:

組み込みsse4.2popcount 関数の使用法:

inline int popcount(uint64_t x) {
//      int count;
//      for (count=0; x; count++)
//          x &= x-1;
//      return count;
    return _mm_popcnt_u64(x); //__builtin_popcount(x) is ok too
// Thanks to @cschwan for pointing me on this
}

「_mm_popcnt_u64」には追加のコンパイル オプション「-m64 -msse4.2」が必要です (「__builtin_popcount」ではありません)。

次のような機能が追加されました。

int LabSetInt64::CU ( const LabSetInt64 &rhs ) {
    int sum = 0;
    for(int i=0; i<data_len; i++)
        sum += popcount(data[i] | rhs.data[i]);
    return sum;
}

操作を行ってからカウントするよりも高速です。

「最適カウント」オプション ( CNT_OPTmode) : 以下の新しいクラス バージョンを参照してください。

組み合わせ操作が少し遅くなります(ほんの数、本当の心配はありません...)

より速い挿入:

見つからない場合にのみ挿入することにより:

void LabSetInt64::Set(int i) {
    int n = i / 64;
    uint64_t shift1l = 1l << (i % 64);
    if ( !(data[n] & shift1l) ) {               // Not found
        data[n] |= shift1l;
        if (CNT_OPT) ++cardinality;
    }
}

どのような場合でもビットを設定するよりも、ビットが既に設定されているかどうかを最初に確認する方が高速です。(挿入しようとするゼロの数が最も多く、時間を節約できます)。設定されていないビットを設定した場合、パフォーマンスはまったく変わりません。

いくつかのより高速な演算子を追加しました:

const LabSetInt64 & LabSetInt64::operator +=  ( const LabSetInt64 & rhs) {
    for( int i = 0; i < data_len; i++ )
        data[i] = data[i] | rhs.data[i];            // bitwise OR
    return *this;
}

A += B は A = A + B よりもはるかに高速になりました (返される新しいベクトルのインスタンス化を保存します)

いくつかの便利な関数と演算子が追加されました: 以下の新しいクラス バージョンを参照してください

[ Header]

/**
 * If the set to 1, will be optimized for counting (cardinality updated on the fly).
 * WARNING : Optimizing for counting will slow down a little the following features :
 *              - Binary operation : (~, +, *, -, ^)
 */
#define CNT_OPT                 1

#define DEFAULT_SIZE_IN_BITS    1000000

//This is better when most bits in x are 0
inline unsigned int popcount(uint64_t x) {
    //      // It uses 3 arithmetic operations and one comparison/branch per "1" bit in x.
    //      int count;
    //      for (count=0; x; count++)
    //          x &= x-1;
    //      return count;
    return _mm_popcnt_u64(x); //__builtin_popcountl(x); //
}

/**
 * Counts trailing zeros starting from a given point
 * @param v Input to count trailing zero bits
 * @return c will count v's trailing zero bits,
             so if v is 1101000 (base 2), then c will be 3
 */
inline int nb_trailing_zeros(uint64_t v) {
    if (v & 1l) return 0;           // Fast check if no trailing zeros
                                    // Avoids a useless call to __builtin_ctzl()
    else return __builtin_ctzl(v);
}

class LabSetInt64
{
public:

    LabSetInt64();
    /**
     * Instantiates a bitset with a given maximum size.
     * @param sizeinbits Maximum size of the population
     */
    LabSetInt64(unsigned int sizeinbits);
    LabSetInt64(const LabSetInt64 &);

    virtual ~LabSetInt64();


    void Clear();
    void Resize(unsigned int sizeinbits);
    /**
     * WARNING : no check is perform whether i is in the correct range :
     *           i MUST be in [0, size_in_bits-1]
     */
    void Set(int i);
    void UnSet(int i);
    //    void Set(int i, bool b);
    int  Cardinality();

    bool Find(int i);
    void Print();

    int GetFirst();
    int ChooseOne();
    int GetAt(int n);

    const LabSetInt64 & operator = ( const LabSetInt64 & );

    /**
     * All the following operators assume that left and right operands
     * have the same size "size_in_bits" (enough for our needs,
     * since memory usage is not really a problem for us).
     */
    LabSetInt64         operator ~  () const;                   /** Inversion      **/
    LabSetInt64         operator +  ( const LabSetInt64 & );    /** Union          **/
    LabSetInt64         operator *  ( const LabSetInt64 & );    /** Intersection   **/
    LabSetInt64         operator -  ( const LabSetInt64 & );    /** Difference     **/
    LabSetInt64         operator ^  ( const LabSetInt64 & );    /** Symmetrical D. **/

    /**
     * Comparison.
     */
    bool                operator == ( const LabSetInt64 & ) const;
    bool                operator != ( const LabSetInt64 & ) const;

    /**
     * Convenient, moreover :
     * (A += B) is actually a lot faster than (A = A + B)...
     * [ No intermediary storage ]
     */
    const LabSetInt64 & operator +=  ( const LabSetInt64 & );
    const LabSetInt64 & operator *=  ( const LabSetInt64 & );
    const LabSetInt64 & operator -=  ( const LabSetInt64 & );
    const LabSetInt64 & operator ^=  ( const LabSetInt64 & );

    const LabSetInt64 & INV();

    /**
     * Gets the population count of the union.
     * Faster than realizing the union, then calling Cardinality().
     * [ No intermediary storage ]
     */
    int CU  ( const LabSetInt64 & );
    int CI  ( const LabSetInt64 & );
    int CD  ( const LabSetInt64 & );
    int CSD ( const LabSetInt64 & );

    /**
     * Basic iterator facility.
     */
    class iter {
    public:
        iter(LabSetInt64 & bv) { pos_ = -1; bv_ = &bv; }

        void reset() { pos_ = -1; }
        int  end()   { return -2; }
        int  next()  { pos_ = bv_->next_set_bit(pos_); return pos_; }

    private:
        int           pos_;
        LabSetInt64 * bv_;
    };

private:

    uint64_t *   data_;
    unsigned int data_len_;
    unsigned int size_in_bits_;
    unsigned int cardinality_;

    void init_bitset(unsigned int sizeinbits);
    int  calc_cardinality();
    int  next_set_bit(int i);
};

[ Implementation]

LabSetInt64::LabSetInt64() {
    init_bitset(DEFAULT_SIZE_IN_BITS);
}

LabSetInt64::LabSetInt64(unsigned int sizeinbits) {
    init_bitset(sizeinbits);
}

void LabSetInt64::init_bitset(unsigned int sizeinbits) {
    // +1 (the last higher weighted bit is 0 : allows to stop when iterating)
    // See example in "Print()" function
    size_in_bits_ = sizeinbits + 1;
    data_len_ = (size_in_bits_ + 63) / 64;
    data_ = new uint64_t[data_len_];
    if (CNT_OPT) cardinality_ = 0;
    Clear();                        // Start filled with 0s. // Necessary...    
}

LabSetInt64::LabSetInt64(const LabSetInt64& src) {
    size_in_bits_ = src.size_in_bits_;
    data_len_ = src.data_len_;
    data_ = new uint64_t[data_len_];
    memcpy( data_, src.data_, src.data_len_ * sizeof(uint64_t) );
    cardinality_ = src.cardinality_;
}

LabSetInt64::~LabSetInt64() {
    if (data_ != NULL) delete[] data_;
}

void LabSetInt64::Clear() {
    std::fill_n(data_, data_len_, 0);

    if (CNT_OPT) cardinality_ = 0;
}


void LabSetInt64::Resize(unsigned int sizeinbits) {
    bool truncated;
    unsigned int new_sizeinbits = sizeinbits + 1;

    if (size_in_bits_ != new_sizeinbits)
    {
        truncated = (size_in_bits_ > new_sizeinbits);
        size_in_bits_ = new_sizeinbits;
        unsigned int new_size = (size_in_bits_ + 63) / 64;

        if (new_size != data_len_)      // Resize only if storage size changed
        {
            uint64_t *new_data = new uint64_t[new_size];
            if (!truncated)             // Clear : required only if extended
                std::fill_n(new_data, new_size, 0);

            memcpy( new_data, data_, std::min(data_len_, new_size) * sizeof(uint64_t) );

            data_len_ = new_size;
            delete [] data_;
            data_ = new_data;
        }

        //--
        if (truncated) {
            // Clear the bits beyond the maximum size
            unsigned int begin = sizeinbits;
            unsigned int end = data_len_ * 64;
            for ( unsigned int i = begin; i<end; ++i ) UnSet(i);
            // Update cardinality
            if (CNT_OPT) cardinality_ = calc_cardinality();
        }
    }
}


void LabSetInt64::Set(int i) {

    int n = i / 64;
    uint64_t shift1l = 1l << (i % 64);
    if ( !(data_[n] & shift1l) ) {              // Not found
        data_[n] |= shift1l;
        if (CNT_OPT) ++cardinality_;
    }
}

void LabSetInt64::UnSet(int i) {

    int n = i / 64;
    uint64_t shift1l = 1l << (i % 64);
    if ( data_[n] & shift1l ) {                 // Found
        data_[n] &= ~shift1l;
        if (CNT_OPT) --cardinality_;
    }
}

//void LabSetInt64::Set(int i, bool b) {
//  if(b) Set(i); else UnSet(i);
//}

bool LabSetInt64::Find(int i) {
    return ((data_[i / 64]) & (1l << (i % 64)));
}


int LabSetInt64::Cardinality() {
    if (CNT_OPT) return cardinality_;
    else return calc_cardinality();
}


int LabSetInt64::calc_cardinality() {
    unsigned int sum = 0;

    for(unsigned int i=0; i<data_len_; i++)
        sum += popcount(data_[i]);
        //sum += bitcount64_4way(data[i], data[i+1], data[i+2], data[i+3]);

    return sum;
}

int LabSetInt64::next_set_bit(int i) {
    ++i;
    unsigned int x = i / 64;
    uint64_t w = data_[x];
    w >>= (i % 64);
    if (w != 0) {
        return i + nb_trailing_zeros(w);
    }
    ++x;
    for (; x < data_len_; ++x) {
        if (data_[x] != 0) {
            return x * 64 + nb_trailing_zeros(data_[x]);
        }
    }
    return -2;
}


void LabSetInt64::Print() {

    cout << "\nSet size : " << this->Cardinality() << endl;
    cout << "{ ";
    int cnt = 0;
    iter it(*this);
    for (int val = it.next(); val != it.end(); val = it.next())
    {
        cout << (val) << " ";
        if ((++cnt) % 30 == 0) cout << endl;
    }
    cout << "}" << endl;
}



int LabSetInt64::GetFirst() {
    return this->next_set_bit(-1);
}

int LabSetInt64::ChooseOne() {
    // Get lucky...
    int id = rand() % this->size_in_bits_;
    // Otherwise...
    if (!Find(id))
        id = GetAt(rand() % this->Cardinality());
    return id;
}

int LabSetInt64::GetAt(int n) {

    int cnt = 0, val;
    iter it(*this);
    while ((val = it.next()) != it.end() && cnt++ != n) {  }

    return val;
}

// *************************************************



/*----------------------------------------------------------------------------*
 **  "operator = " assigns the right hand side to this set.
 **
 **  returns: nothing
 */
const LabSetInt64 &LabSetInt64::operator = ( const LabSetInt64 &rhs )
{
    if( &rhs != this )                              // avoid self assignment
    {
        this->Resize(rhs.size_in_bits_ - 1);
        memcpy( data_, rhs.data_, rhs.data_len_ * sizeof(uint64_t) );

        if (CNT_OPT) cardinality_ = rhs.cardinality_;
    }
    return *this;                                   // enable x = y = z;
}


/*----------------------------------------------------------------------------*
**  "operator ~ " performs set complement operation (not).
**
**  returns: pointer to set
*/
LabSetInt64 LabSetInt64::operator ~ () const
{
    LabSetInt64 rv;

    unsigned int i;
    for( i = 0; i < data_len_; i++ ) {
        rv.data_[i] = ~data_[i];                        // bitwise complement
        if (CNT_OPT) rv.cardinality_ += popcount(rv.data_[i]);
    }

    rv.size_in_bits_ = size_in_bits_;
    // Clear the bits beyond the maximum size
    unsigned int begin = rv.size_in_bits_ - 1;
    unsigned int end = data_len_ * 64;
    for ( i = begin; i<end; ++i ) rv.UnSet(i);

    return rv;
}


/*----------------------------------------------------------------------------*
**  "operator + " performs set union operation (or).
**
**  returns: pointer to set
*/
LabSetInt64 LabSetInt64::operator + ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;

    for( unsigned int i = 0; i < data_len_; i++ ) {
        rv.data_[i] = data_[i] | rhs.data_[i];            // bitwise OR
        if (CNT_OPT) rv.cardinality_ += popcount(rv.data_[i]);
    }

    return rv;
}


/*----------------------------------------------------------------------------*
**  "operator * " performs set intersection operation (and).
**
**  returns: pointer to set
*/
LabSetInt64 LabSetInt64::operator * ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;

    for( unsigned int i = 0; i < data_len_; i++ ) {
        rv.data_[i] = data_[i] & rhs.data_[i];        // bitwise AND
        if (CNT_OPT) rv.cardinality_ += popcount(rv.data_[i]);
    }

    return rv;
}


/*----------------------------------------------------------------------------*
**  "operator - " performs set difference operation.
**
**  returns: pointer to set
*/
LabSetInt64 LabSetInt64::operator - ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;

    for( unsigned int i = 0; i < data_len_; i++ ) {
        rv.data_[i] = data_[i] & ( ~rhs.data_[i] );       // bitwise a AND ~b
        if (CNT_OPT) rv.cardinality_ += popcount(rv.data_[i]);
    }

    return rv;
}


/*----------------------------------------------------------------------------*
**  "operator ^ " performs set symmetric difference operation (xor).
**
**  returns: pointer to set
*/
LabSetInt64 LabSetInt64::operator ^ ( const LabSetInt64 &rhs )
{
    LabSetInt64 rv;

    for( unsigned int i = 0; i < data_len_; i++ ) {
        rv.data_[i] = data_[i] ^ rhs.data_[i];            // bitwise XOR
        if (CNT_OPT) rv.cardinality_ += popcount(rv.data_[i]);
    }

    return rv;
}



bool LabSetInt64::operator == ( const LabSetInt64 &rhs ) const {

    if ((CNT_OPT) && (cardinality_ != rhs.cardinality_)) return false;

    for( unsigned int i = 0; i < data_len_; i++ )
    {
        if (data_[i] != rhs.data_[i]) {
            return false;
        }
    }
    return true;
}

bool LabSetInt64::operator != ( const LabSetInt64 &rhs ) const {

    return !(*this == rhs);
}



const LabSetInt64 & LabSetInt64::operator +=  ( const LabSetInt64 & rhs) {

    if (CNT_OPT) cardinality_ = 0;
    for( unsigned int i = 0; i < data_len_; i++ ) {
        data_[i] = data_[i] | rhs.data_[i];
        if (CNT_OPT) cardinality_ += popcount(data_[i]);
    }

    return *this;
}

const LabSetInt64 & LabSetInt64::operator *=  ( const LabSetInt64 & rhs) {

    if (CNT_OPT) cardinality_ = 0;
    for( unsigned int i = 0; i < data_len_; i++ ) {
        data_[i] = data_[i] & rhs.data_[i];
        if (CNT_OPT) cardinality_ += popcount(data_[i]);
    }

    return *this;
}

const LabSetInt64 & LabSetInt64::operator -=  ( const LabSetInt64 & rhs) {

    if (CNT_OPT) cardinality_ = 0;
    for( unsigned int i = 0; i < data_len_; i++ ) {
        data_[i] = data_[i] & ( ~rhs.data_[i] );
        if (CNT_OPT) cardinality_ += popcount(data_[i]);
    }

    return *this;
}

const LabSetInt64 & LabSetInt64::operator ^=  ( const LabSetInt64 & rhs) {

    if (CNT_OPT) cardinality_ = 0;
    for( unsigned int i = 0; i < data_len_; i++ ) {
        data_[i] = data_[i] ^ rhs.data_[i];
        if (CNT_OPT) cardinality_ += popcount(data_[i]);
    }

    return *this;
}


// *************************************************

const LabSetInt64 & LabSetInt64::INV() {

    unsigned int i;

    if (CNT_OPT) cardinality_ = 0;
    for( i = 0; i < data_len_; i++ ) {
        data_[i] = ~data_[i];
        if (CNT_OPT) cardinality_ += popcount(data_[i]);
    }

    // Clear the bits beyond the maximum size
    unsigned int begin = size_in_bits_ - 1;
    unsigned int end = data_len_ * 64;
    for ( i = begin; i<end; ++i ) UnSet(i);

    return *this;
}

// *************************************************


int LabSetInt64::CU ( const LabSetInt64 & rhs ) {
    int sum = 0;
    for( unsigned int i=0; i < data_len_; i++ )
        sum += popcount(data_[i] | rhs.data_[i]);
    return sum;
}

int LabSetInt64::CI  ( const LabSetInt64 & rhs ) {
    int sum = 0;
    for( unsigned int i=0; i < data_len_; i++ )
        sum += popcount(data_[i] & rhs.data_[i]);
    return sum;
}

int LabSetInt64::CD  ( const LabSetInt64 & rhs ) {
    int sum = 0;
    for( unsigned int i=0; i < data_len_; i++ )
        sum += popcount(data_[i] & (~rhs.data_[i]));
    return sum;
}

int LabSetInt64::CSD ( const LabSetInt64 & rhs ) {
    int sum = 0;
    for( unsigned int i=0; i < data_len_; i++ )
        sum += popcount(data_[i] ^ rhs.data_[i]);
    return sum;
}

移植性に関する注意:

一部のアーキテクチャ ( Windows 7+などMinGW) の1 つ (longは 32 ビットのみ) は、上記のコードで次のすべての出現箇所を置き換えます。

  • 1l1ll
  • __builtin_popcountl__builtin_popcountll
  • __builtin_ctzl__builtin_ctzll

これにより、常に 64 ビットの数値を使用することが保証されます ( long long)。

さて、これが有益であり、他の人にとって良い出発点になることを願っています.

于 2013-02-23T16:51:53.657 に答える