整数的词典比较

时间:2015-11-09 17:53:45

标签: c++ bit-manipulation

我想按字典顺序比较两个小的(< = 20)整数组(1..20)。

集合由单个整数表示,例如
1,2,4,6 将表示为

 ... 0 1 0 1 0 1 1
(... 7 6 5 4 3 2 1)

因此,如果该数字存在1,那么该数字就会出现。

有人可以验证此代码是否正确吗?

bool less_than(unsigned a, unsigned b) {
    unsigned tmp = a ^ b;
    tmp = tmp & (~tmp + 1); //first difference isolated
    return (tmp & a) && (__builtin_clz(b) < __builtin_clz(tmp));
}

__builtin_clz部分适用于ba前缀的情况。
空集的情况在别处处理(__builtin_clz未定义为0)。

编辑:

bool less_than(unsigned a, unsigned b) {
    unsigned tmp = a ^ b;
    tmp &= -tmp; //first difference isolated
    return ((tmp & a) && (__builtin_clz(b) < __builtin_clz(tmp)))
            || (__builtin_clz(a) > __builtin_clz(tmp));
}

bool less_than_better(unsigned a, unsigned b) {
    unsigned tmp = a ^ b;
    tmp &= -tmp; //first difference isolated
    return ((tmp & a) && tmp < b) || tmp > a;
}

似乎都是正确的。 (在数以千万计的随机测试中使用std::lexicographical_compare测试了一个天真的实现)

第二个更方便,因为它不使用__builtin_clz 我的机器上的速度差异可以忽略不计(第二个速度快〜2%),但是在没有__builtin_clz作为一个处理器指令的机器上(例如x86上的BSR),差异可能会很大。

2 个答案:

答案 0 :(得分:0)

这是列出计算2位输入的所有组合的列表:

#include <stdio.h>

bool less_than(unsigned a, unsigned b) {
    unsigned tmp = a ^ b;
    tmp = tmp & (~tmp + 1); //first difference isolated
    return (tmp & a) && (__builtin_clz(b) < __builtin_clz(tmp));
}

#define BITPATTERN "%d%d%d"
#define BYTETOBITS(byte)  \
  (byte & 0x04 ? 1 : 0), \
  (byte & 0x02 ? 1 : 0), \
  (byte & 0x01 ? 1 : 0) 

int main(int argc, char** argv) {
  for ( int a = 0; a < 4; a ++ )
    for ( int b = 0; b < 4; b ++)
      printf("a: "BITPATTERN" b: "BITPATTERN": %d\n",
        BYTETOBITS(a), BYTETOBITS(b), less_than(a,b)
      );
}

这是输出:

a: 000 b: 000: 0
a: 000 b: 001: 0
a: 000 b: 010: 0
a: 000 b: 011: 0
a: 001 b: 000: 0
a: 001 b: 001: 0
a: 001 b: 010: 1
a: 001 b: 011: 0
a: 010 b: 000: 0
a: 010 b: 001: 0
a: 010 b: 010: 0
a: 010 b: 011: 0
a: 011 b: 000: 0
a: 011 b: 001: 0
a: 011 b: 010: 1
a: 011 b: 011: 0

它似乎看起来不正确..

答案 1 :(得分:0)

a == 0的情况不正确。这应该返回true,除非b == 0,但是因为tmp & a将是假的,无论tmp的值(这将是b中的最低位1位),该函数将返回假的。

如果符合以下情况,

a应该“小于”b

1. `a` is a proper prefix of `b`, or
2. The lowest-order bit of `a^b` is in `a`.

第一个条件还处理a为空集且b不是空集的情况。 (这与您的公式略有不同,后者是“a^ba的最低位”而不是ba的正确前缀)。)

对案例“a的简单测试是b”的正确前缀,因为我们在a^b中具有tmp的最低位},是tmp > a。这避免了使用__builtin_clz [注1]。

另外,你可以写

tmp = tmp & (~tmp + 1);

作为

tmp &= -tmp;

但我认为大多数C编译器会自行发现优化。 [注2]。

应用这些优化,结果将是(未经测试):

bool less_than(unsigned a, unsigned b) {
    unsigned tmp = a ^ b;
    tmp &= -tmp; //first difference isolated
    return tmp > a || tmp & a;
}

注释

  1. 这是值得做的,因为(1)即使内置__builtin_clz,它也不一定超快; (2)如果您使用gcc或clang以外的编译器进行编译,则可能不存在。

  2. 如果-tmp是无符号类型,则
  3. tmp保证为tmp的2s补码否定,即使底层实现不是2s补码。参见§6.2.6.2/ 1(对于某些整数N,无符号类型的范围是0..2 N -1)和&amp; 6.3.1.3/2(负值转换为无符号整数类型,重复添加2 N 直到值在范围内。