快速 log2 取整算法 (O(1) 时间与空间复杂度)

先上核心代码(文末附针对多种整数类型的代码):

inline int log_2(int x) {
    int rst = 0;
    if (x & 0xffff'0000U) rst += 16, x >>= 16;
    if (x & 0x0000'ff00U) rst +=  8, x >>=  8;
    if (x & 0x0000'00f0U) rst +=  4, x >>=  4;
    if (x & 0x0000'000cU) rst +=  2, x >>=  2;
    if (x & 0x0000'0002U) rst +=  1          ;
    return rst;
}

原理很简单:

首先,任意正整数都可以拆分为数个互不相等的 2 的幂的和,且这些 2 的幂都小于等于该数;

其次,int 占用 32 位,故对于 int 范围内的数,其对数都不超过 31

最后,容易注意到二进制与对数间的天然联系,介于这种联系是显然且不易描述的,在此不予点明[doge];

综上,对于 int 类型的数据,其对数必然可以通过 {16, 8, 4, 2, 1} 组合出来,通过其二进制位即可判断其对数是由那几个数组合的。


附录(以下代码效率差别可以忽略不计,但代码量有显著差距):

inline auto log_2(uint64_t x) { // also unsigned long long
    uint64_t rst = 0;
    if (x & 0xffff'ffff'0000'0000ULL) rst += 32, x >>= 32;
    if (x & 0x0000'0000'ffff'0000ULL) rst += 16, x >>= 16;
    if (x & 0x0000'0000'0000'ff00ULL) rst +=  8, x >>=  8;
    if (x & 0x0000'0000'0000'00f0ULL) rst +=  4, x >>=  4;
    if (x & 0x0000'0000'0000'000cULL) rst +=  2, x >>=  2;
    if (x & 0x0000'0000'0000'0002ULL) rst +=  1          ;
    return rst;
}
inline auto log_2(uint32_t x) { // also unsigned int
    uint32_t rst = 0;
    if (x & 0xffff'0000U) rst += 16, x >>= 16;
    if (x & 0x0000'ff00U) rst +=  8, x >>=  8;
    if (x & 0x0000'00f0U) rst +=  4, x >>=  4;
    if (x & 0x0000'000cU) rst +=  2, x >>=  2;
    if (x & 0x0000'0002U) rst +=  1          ;
    return rst;
}
inline auto log_2(uint16_t x) { // also unsigned short
    uint16_t rst = 0;
    if (x & 0x0000'ff00U) rst += 8, x >>= 8;
    if (x & 0x0000'00f0U) rst += 4, x >>= 4;
    if (x & 0x0000'000cU) rst += 2, x >>= 2;
    if (x & 0x0000'0002U) rst += 1         ;
    return rst;
}
inline auto log_2(uint8_t x) { // also unsigned char
    uint8_t rst = 0;
    if (x & 0x00f0U) rst += 4, x >>= 4;
    if (x & 0x000cU) rst += 2, x >>= 2;
    if (x & 0x0002U) rst += 1         ;
    return rst;
}
posted @ 2023-11-30 22:35  Expector  阅读(728)  评论(0编辑  收藏  举报