채팅에서 발표했듯이 세련된 답변을 추가합니다. 여기에는 세 부분으로 나뉘며 각 부분에는 그 부분에 대한 설명이 이어집니다.
첫 번째 부분 인 get.h는 내 해결책이지만 일반화되고 하나의 수정으로 해결됩니다.
두 번째 부분 인 got.h는 질문에 게시 된 원래 알고리즘으로 서명되지 않은 유형의 STL 컨테이너에서 작동하도록 일반화되었습니다.
세 번째 부분 인 main.cpp에는 정확성과 성능을 검증하는 단위 테스트가 포함되어 있습니다.
#include <cstddef>
using std::size_t;
template < typename C >
typename C::value_type get (C const &container, size_t lo, size_t hi)
{
typedef typename C::value_type item; // a container entry
static unsigned const bits = (unsigned)sizeof(item)*8u; // bits in an item
static size_t const mask = ~(size_t)0u/bits*bits; // huge multiple of bits
// everthing above has been computed at compile time. Now do some work:
size_t lo_adr = (lo )/bits; // the index in the container of ...
size_t hi_adr = (hi-(hi>0))/bits; // ... the lower or higher item needed
// we read container[hi_adr] first and possibly delete the highest bits:
unsigned hi_shift = (unsigned)(mask-hi)%bits;
item hi_val = container[hi_adr] <<hi_shift>> hi_shift;
// if all bits are in the same item, we delete the lower bits and are done:
unsigned lo_shift = (unsigned)lo%bits;
if (hi_adr <= lo_adr) return (hi_val>>lo_shift) * (lo<hi);
// else we have to read the lower item as well, and combine both
return (hi_val<<bits-lo_shift | container[lo_adr]>>lo_shift);
}
위의 첫 번째 부분은 내 원래 솔루션이지만 부호없는 정수 유형의 모든 STL 컨테이너에서 작동하도록 일반화되었습니다. 따라서 32 비트 정수 또는 128 비트 정수를 사용하고 테스트 할 수 있습니다. 난 여전히 아주 작은 숫자에 대해서는 부호없는을 사용하지만 size_t로 대체 할 수도 있습니다. 알고리즘은 거의 수정되지 않고 작은 수정으로 - 컨테이너의 총 비트 수를 lo라고하면 이전의 get()은 컨테이너 크기 바로 위에있는 항목에 액세스합니다. 이것은 지금 고쳐졌습니다.
#include <cstddef>
using std::size_t;
// Shift right, but without being undefined behaviour if n >= 64
template < typename val_type >
val_type shiftr(val_type val, size_t n)
{
val_type good = n < sizeof(val_type)*8;
return good * (val >> (n * good));
}
// Shift left, but without being undefined behaviour if n >= 64
template < typename val_type >
val_type shiftl(val_type val, size_t n)
{
val_type good = n < sizeof(val_type)*8;
return good * (val << (n * good));
}
// Mask the word preserving only the lower n bits.
template < typename val_type >
val_type lowbits(val_type val, size_t n)
{
val_type mask = shiftr<val_type>((val_type)(-1), sizeof(val_type)*8 - n);
return val & mask;
}
// Struct for return values of locate()
struct range_location_t {
size_t lindex; // The word where is located the 'begin' position
size_t hindex; // The word where is located the 'end' position
size_t lbegin; // The position of 'begin' into its word
size_t llen; // The length of the lower part of the word
size_t hlen; // The length of the higher part of the word
};
// Locate the one or two words that will make up the result
template < typename val_type >
range_location_t locate(size_t begin, size_t end)
{
size_t lindex = begin/(sizeof(val_type)*8);
size_t hindex = end/(sizeof(val_type)*8);
size_t lbegin = begin % (sizeof(val_type)*8);
size_t hend = end % (sizeof(val_type)*8);
size_t len = (end - begin) * size_t(begin <= end);
size_t hlen = hend * (hindex > lindex);
size_t llen = len - hlen;
range_location_t l = { lindex, hindex, lbegin, llen, hlen };
return l;
}
// Main function.
template < typename C >
typename C::value_type got (C const&container, size_t begin, size_t end)
{
typedef typename C::value_type val_type;
range_location_t loc = locate<val_type>(begin, end);
val_type low = lowbits<val_type>(container[loc.lindex] >> loc.lbegin, loc.llen);
val_type high = shiftl<val_type>(lowbits<val_type>(container[loc.hindex], loc.hlen), loc.llen);
return high | low;
}
이 두번째 부분은, 위의 got.h, 어떤 부호없는 정수 유형의 STL 컨테이너를 받아 들일뿐만 아니라 일반화 질문에 원래의 알고리즘이다. get.h와 마찬가지로이 버전은 컨테이너 유형을 정의하는 단일 템플릿 매개 변수를 제외한 다른 정의를 사용하지 않으므로 다른 항목 크기 나 컨테이너 유형을 쉽게 테스트 할 수 있습니다.
#include <vector>
#include <cstddef>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "get.h"
#include "got.h"
template < typename Container > class Test {
typedef typename Container::value_type val_type;
typedef val_type (*fun_type) (Container const &, size_t, size_t);
typedef void (Test::*fun_test) (unsigned, unsigned);
static unsigned const total_bits = 256; // number of bits in the container
static unsigned const entry_bits = (unsigned)sizeof(val_type)*8u;
Container _container;
fun_type _function;
bool _failed;
void get_value (unsigned lo, unsigned hi) {
_function(_container,lo,hi); // we call this several times ...
_function(_container,lo,hi); // ... because we measure ...
_function(_container,lo,hi); // ... the performance ...
_function(_container,lo,hi); // ... of _function, ....
_function(_container,lo,hi); // ... not the performance ...
_function(_container,lo,hi); // ... of get_value and ...
_function(_container,lo,hi); // ... of the loop that ...
_function(_container,lo,hi); // ... calls get_value.
}
void verify (unsigned lo, unsigned hi) {
val_type value = _function(_container,lo,hi);
if (lo < hi) {
for (unsigned i=lo; i<hi; i++) {
val_type val = _container[i/entry_bits] >> i%entry_bits & 1u;
if (val != (value&1u)) {
printf("lo=%d hi=%d [%d] is'nt %d\n",lo,hi,i,(unsigned)val);
_failed = true;
}
value >>= 1u;
}
}
if (value) {
printf("lo=%d hi=%d value contains high bits set to 1\n",lo,hi);
_failed = true;
}
}
void run (fun_test fun) {
for (unsigned lo=0; lo<total_bits; lo++) {
unsigned h0 = 0;
if (lo > entry_bits) h0 = lo - (entry_bits+1);
unsigned h1 = lo+64;
if (h1 > total_bits) h1 = total_bits;
for (unsigned hi=h0; hi<=h1; hi++) {
(this->*fun)(lo,hi);
}
}
}
static uint64_t time_used () {
struct rusage ru;
getrusage(RUSAGE_THREAD,&ru);
struct timeval t = ru.ru_utime;
return (uint64_t) t.tv_sec*1000 + t.tv_usec/1000;
}
public:
Test (fun_type function): _function(function), _failed() {
val_type entry;
unsigned index = 0; // position in the whole bit array
unsigned value = 0; // last value assigned to a bit
static char const entropy[] = "The quick brown Fox jumps over the lazy Dog";
do {
if (! (index%entry_bits)) entry = 0;
entry <<= 1;
entry |= value ^= 1u & entropy[index/7%sizeof(entropy)] >> index%7;
++index;
if (! (index%entry_bits)) _container.push_back(entry);
} while (index < total_bits);
}
bool correctness() {
_failed = false;
run(&Test::verify);
return !_failed;
}
void performance() {
uint64_t t1 = time_used();
for (unsigned i=0; i<999; i++) run(&Test::get_value);
uint64_t t2 = time_used();
printf("used %d ms\n",(unsigned)(t2-t1));
}
void operator() (char const * name) {
printf("testing %s\n",name);
correctness();
performance();
}
};
int main()
{
typedef typename std::vector<uint64_t> Container;
Test<Container> test(get<Container>); test("get");
Test<Container> tost(got<Container>); tost("got");
}
위의 3 부분, MAIN.CPP, 단위 테스트 클래스를 포함하고 get.h 그들에게 적용 got.h, 그 약간, 내 솔루션과 질문의 원본 코드입니다 수정 됨. 단위 테스트는 정확성을 확인하고 속도를 측정합니다. 그들은 256 비트의 컨테이너를 생성하고, 일부 데이터로 채우고, 컨테이너 항목에 더해지는 많은 비트의 모든 가능한 비트 섹션을 읽고, 많은 병리학 적 케이스를 읽고, 각 결과의 정확성을 검증함으로써 정확성을 검증합니다. 동일한 섹션을 다시 읽고 사용자 공간에서 사용 된 스레드의 시간을보고함으로써 속도를 측정합니다.
['std :: bitset'] (http://en.cppreference.com/w/cpp/utility/bitset)를 중개자로 사용하는 것은 어떻습니까? –
'good * ...'을 반환 할 때 'good'은 0 또는 1이라고 가정 할 수 없습니다. 이식성이 없습니다. 대신 삼항 연산자를 사용하십시오 : 'return good? ... : 0;' – Christophe
@Christophe C++에서 완벽하게 이식 가능합니다. '<'는'bool'을 만들어서 다른 정수로 변환하면 0 또는 1이됩니다. C++ 11, 4.7/4를보십시오. –