Gosper Hack算法

代码和内容参考至 Gosper’s Hack Explained

算法描述

问题:对于n位的数值,得到其k个比特位为1的所有排列组合 (n >= k):
比如 k = 3, n = 6, 我们可以得到:
在这里插入图片描述

朴素实现:

  • 枚举所有二进制情况:
		 int mask = (1 << k) - 1;   //具有k位1 的最小值
         int limit = mask << (n - k); //具有k位1 的最大值
         
        while(mask !=  limit){
            if(Integer.bitCount(mask) != k){
                mask++;
                continue;
            }
            save(mask);
            mask++;
        }
  • 时间复杂度 O ( 2 n ) O(2 ^n ) O(2n)

GosperHack 算法

  • 可以在O(1) 的时间内就得到下一个有k个1-bit的 数值
  • 时间复杂度 O ( C n k ) O(C_n ^k ) O(Cnk)
void GospersHack(int k, int n)
{
    int set = (1 << k) - 1;
    int limit = (1 << n);
    while (set < limit)
    {
         save(mask);
        // Gosper's hack:
        int c = set & - set;
        int r = set + c;
        set = (((r ^ set) >> 2) / c) | r; 
        //java 也可以写为 set = ((r ^ set) >> ( Integer.numberOfTrailingZeros(c) + 2) )| r; 
    }
}

原理

  1. 首先手工计算,我们要遵循的规则有两条:
    • rule1 : 找到可以左移到0-bit 的最右侧 1-bit, 并把1-bit左移一位
    • rule2:讲右侧所有1-bits 一直移动到最右侧(没有尾部0-bit)
  • 举例
    比如开始时二进制为 000111
    rule1 : 000111 -> 001011
    rule2 : 001011 -> 001011
    结果 : 001011

    rule1 : 001011 -> 001101
    rule2 : 001101 -> 001101
    结果 : 001101

  1. 那么如何利用程序计算呢?
    解析程序:

    int c = set & - set;

    set & -set 的作用是获取set 最右侧的1, 比如

     			        set =   001110
     			       -set =   110010
     			   set&-set =   000010
    

    int r = set + c;

    set 和c 相加,其实就是实现了规则1,把set可以移动的1-bit左移
    在这里插入图片描述

    set = (((r ^ set) >> 2) / c) | r;

    r ^ set 的结果包括原来set右侧连续的1-bits 和刚刚左移的1-bit
    在这里插入图片描述

    现在开始进行规则2, 需要将右侧连续1-bits 右移。那么移动几位呢?可以通过c, 也就是set最右侧的1-bit判断位数,表现在运算上就是:(r ^ set) / c

    在这里插入图片描述
    但现在(r^set)/ c 是包含原本需要左移的1-bit 和已经左移的1-bit, 所以我们需要右移两位把它们去掉: (r ^ set)/ c)>> 2
    在这里插入图片描述
    最后在与之前计算的r相与,得到最终结果:((r ^ set)/ c)>> 2)| r
    在这里插入图片描述

  • 为什么用 ((r ^ set)>> 2) / c)而不是((r ^ set)/ c)>> 2)?

作者可能是考虑到cpu 除法的一些特性:

Gosper chose to use ((r ^ set) >> 2) / c) instead of ((r ^ set) / c) >> 2. Why? I believe he did so because some CPUs use the shift-and-subtract method to perform division and stop as soon as the remainder is 0, so dividing by a small number is faster. Thus, in some CPUs, we can expect ((r ^ set) >> 2) / c) to be marginally faster than ((r ^ set) / c) >> 2.

相关题目

力扣2397. Maximum Rows Covered by Columns

核心思想是二进制枚举,可以试一试

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值