求一个数L,在二进制下末尾0的个数k,与fft过程中的码位倒序

博客探讨了如何利用数学原理和树状数组解决求二进制数末尾0的个数问题,并将其应用到快速傅里叶变换(FFT)中的码位倒序过程,提供了一段相关的代码实现,指出这种方法可以优化计算效率。

今天看树状数组,突然发现了这个东西,想了一想,然后用文字简要的证明了一下:

2^k = L and (L xor (L - 1) )


设i在二进制下从末尾数的第 k 位第一次不为0.
先用 c = i^(i-1)则将第 k 位以左的数变为0,从k位往右都为1
再用 i & c 因为c的末尾 k位为1,k位以左为0,i的末尾 k-1位为0,第k位为1,k位以左不知道。
按位与后则剩余的数即为 2^(k - 1)

下面是程序:

#include <stdio.h>
#include <math.h>
#define NUM 16
                        
//lowbit:计算当 i 在二进制下整数 末尾0的个数为k时求2^k 即 2^k = lowBit(i)
                        
//设i在二进制下从末尾数的第 k 位第一次不为0.
//先用 c = i^(i-1)则将第 k 位以左的数变为0,从k位往右都为1
//再用 i & c 因为c的末尾 k位为1,k位以左为0,i的末尾 k-1位为0,第k位为1,k位以左不知道。
//按位与后则剩余的数即为 2^(k - 1),
                        
//从而能计算出当 i 在二进制下整数 末尾0的个数为k时求2^k 即 2^k = lowBit(i)
//要算k为多少只需再以2为底进行取余即可。 
int lowBit(int i){
    return i&(i^(i-1));
}
                        
int main(int argc, char *argv[])
{
                            
    for(int i = 1;i < NUM + 1;++i){
        printf("%d:\t%d\n",i,(int)log2(lowBit(i)));
    }
    return 0;
}

在解决完这个问题后,我突然想到了,一个问题,按照这个方法,改改程序,我就可以求出fft里面的那个码位倒序这个过程了。


代码如下:fft中的码位倒序代码, 当然也可以一位一位的去求但是当i为2的k次幂时就会比较费时,至少能起到一个常数系数的优化:


#include <stdio.h>
#include <math.h>
#define NUM 16
#define NUM_WEI 4
           
int lowBit(int i){
    return i&(i^(i-1));
}
          
int ma_wei_dao_xu(int k){
    int l;
    int sum = 0;
    while(k){
                  
        l = lowBit(k);
                  
        sum += (int)pow(2,NUM_WEI - log2(l) - 1);
                  
        k &= (NUM  - l * 2);
    }
    return sum;
} 
          
          
int main(int argc, char *argv[])
{
              
    for(int i = 0;i < NUM;++i){
        printf("%d:\t%d\n",i,ma_wei_dao_xu(i));
    }
    return 0;
}


后续:

刚才吃饭的时候想了一下以4位数来说节省的时间为C(1,4) + 2*C(2,4) +3* C(3,4) + 4*C(4,4);

对于为N位的二进制数来说总的时间为:  C(1,N) + 2*C(2,N) +.....+ (N-1)*C(N-1,N) + N*C(N,N);

因为节剩的总 时间就为0的总个数。  C(m,N)其实就暗含了,N位二进制中包含m个零的数的个数,包含的零的个数为:m*C(m,N); 













# P8482 「HGOI-1」Number ## 题目背景 $\text{bh1234666}$ 正在学习乘法! ## 题目描述 $\text{bh1234666}$ 有一定量的字 $0 \sim 9$,现在他想让你寻找一种分配方案,将它们分成两个整,使得他们的乘积 $p$ 最大。 由于 $\text{bh1234666}$ 不喜欢太大的,所以你只需要输出**两个非负整**,使它们的乘积**等于**最大乘积 $p$,但是这两个整 $0 \sim 9$ 的量不能等于给定的量(任意一个量不相等即可,**不考虑前导零**)。 $\text{bh1234666}$ 是很善良的,如果 $0 \sim 9$ 的量等于给定的量了,你依旧可以得到的一半的分。 ## 输入格式 第一行十个整 $c_0,c_1,\cdots c_9$,分别表示 $0 \sim 9$ 的个数。 ## 输出格式 共两行每行一个非负整,分别表示你给出的两个非负整。 ## 输入输出样例 #1 ### 输入 #1 ``` 1 2 3 2 1 1 2 1 2 1 ``` ### 输出 #1 ``` 13949030 620572547 ``` ## 说明/提示 #### 样例解释 最大可能乘积为 $97643210 \times 88653221=13949030 \times 620572547=8656385075279410$。 若输出 $97643210 \times 88653221$ 则只能得到一半的分,因为 $0\sim 9$ 出现的次给定的相同。 #### 据范围及约定 本题采用**捆绑测试**,共有 $5$ 个 $\text{subtask}$,最终分为所有 $\text{subtask}$ 分之和。 $$ \def\arraystretch{1.5} \begin{array}{|c|c|c|}\hline \textbf{Task} & \textbf{Score} & \sum c_i\le \cr\hline 1 & 10 & 20 \cr\hline 2 & 20 & 100 \cr\hline 3 & 20 & 5000 \cr\hline 4 & 20 & 10^6 \cr\hline 5 & 30 & 10^7 \cr\hline \end{array} $$ 对于 $100\%$ 的据,保证 $1 \le c_i$,$\sum c_i \le 10^7$。 #### 说明 本题有 $\text{spj}$,两乘积正确得一半的分,给出的不同且乘积正确得全部分。故每一 $\text{subtask}$ 的得分为其中所有据点得分的**最小值**。
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值