Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 生成具有给定数量的位集和位索引和的整数列表_Recursion_Bit Manipulation - Fatal编程技术网

Recursion 生成具有给定数量的位集和位索引和的整数列表

Recursion 生成具有给定数量的位集和位索引和的整数列表,recursion,bit-manipulation,Recursion,Bit Manipulation,我想以一种有效的方式生成一个整数列表(最好是有序的) 具有以下定义属性: 所有整数都具有相同的位数集N 所有整数具有相同的位索引之和K 确切地说,对于整数I 其二进制表示形式为: $I=\sum_{j=0}^M c_j 2^j$ where $c_j=0$ or $1$ 位集的数量为: $N(I)=\sum_{j=0}^M c_j$ 位索引之和为: $K(I)=\sum_{j=0}^M j c_j$ 我有一种低效的方法生成列表,如下所示: 在使用递增的整数上进行do/for循环 “sn

我想以一种有效的方式生成一个整数列表(最好是有序的) 具有以下定义属性:

  • 所有整数都具有相同的位数集
    N

  • 所有整数具有相同的位索引之和
    K

  • 确切地说,对于整数
    I
    其二进制表示形式为:

    $I=\sum_{j=0}^M c_j 2^j$ where $c_j=0$ or $1$
    
    位集的数量为:

    $N(I)=\sum_{j=0}^M c_j$
    
    位索引之和为:

    $K(I)=\sum_{j=0}^M j c_j$
    
    我有一种低效的方法生成列表,如下所示: 在使用递增的整数上进行do/for循环 “snoob”函数的最小值-具有相同位数集的下一个整数 并在每次增量时检查其是否具有正确的K值

    这是非常低效的,因为通常从整数开始 使用正确的
    N
    K
    值,来自
    I
    的snoob整数没有正确的
    K
    ,必须进行多次snoob计算才能得到下一个整数
    N
    K
    均等于所选值。 使用snoob可以得到一个有序列表,这对于二分法搜索很方便,但是 不是绝对强制的

    通过递归可以轻松计算此列表中的元素数 当被视为分区numner计数时。下面是fortran 90中的一个递归函数:

    =======================================================================
    recursive function BoundedPartitionNumberQ(N, M, D)  result (res)
    implicit none
    
      ! number of partitions of N into M distinct integers, bounded by D
      ! appropriate for Fermi counting rules
    
       integer(8) :: N, M, D, Nmin
       integer(8) :: res
        
        Nmin = M*(M+1)/2       ! the Fermi sea
        
        if(N < Nmin) then
            res = 0
    
        else if((N == Nmin) .and. (D >= M)) then
            res = 1
    
        else if(D < M) then
           res = 0
    
        else if(D == M)  then
           if(N == Nmin) then
                  res = 1
           else 
                  res = 0  
           endif
    
        else if(M == 0) then
           res = 0
    
         else
    
         res = BoundedPartitionNumberQ(N-M,M-1,D-1)+BoundedPartitionNumberQ(N-M,M,D-1)
    
         endif
    
        end function BoundedPartitionNumberQ
    ========================================================================================
    

    自从您提到C/C++/Fortran以来,我一直试图相对地保留这一点,但在适用的情况下也包括了这一点

    所有整数都具有相同的位数集
    N

    然后我们也可以说,所有有效整数都是N个集合位的置换。

    首先,我们必须生成初始/min排列:

    uint32_t firstPermutation(uint32_t n){
        // Fill the first n bits (on the right)
        return (1 << n) -1;
    }
    
    最后,我们需要一种方法来获得下一个排列

    uint32_t nextPermutation(uint32_t n){
        uint32_t t = (n | (n - 1)) + 1;
        return t | ((((t & -t) / (n & -n)) >> 1) - 1);
    }
    
    // or with builtins:
    uint32_t nextPermutation(uint32_t &p){
        uint32_t t = (p | (p - 1));
        return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(p) + 1));
    }
    

    所有整数具有相同的位索引之和
    K

    假设这些是整数(32位),您可以使用此Debrijn序列来快速识别第一组位的索引-fsb。 对于其他类型/比特数,存在类似的序列,例如,一个序列可以调整以供使用

    通过剥离当前fsb,我们可以应用上述技术来识别下一个fsb的索引,等等

    int sumIndices(uint32_t n){
        const int MultiplyDeBruijnBitPosition[32] = {
          0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
          31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
        };
    
        int sum = 0;
        // Get fsb idx
        do sum += MultiplyDeBruijnBitPosition[((uint32_t)((n & -n) * 0x077CB531U)) >> 27];        
        // strip fsb
        while (n &= n-1);   
    
        return sum;
    }
    
    // or with builtin
    int sumIndices(uint32_t n){
        int sum = 0;
        do sum += __builtin_ctz(n);
        while (n &= n-1);
        return sum;
    }
    

    最后,我们可以迭代每个置换,检查所有索引的总和是否与指定的K值匹配

    p = firstPermutation(n);
    lp = lastPermutation(n);
    
    do {
        p = nextPermutation(p);
        if (sumIndices(p) == k){
            std::cout << "p:" << p << std::endl;
        }
    } while(p != lp);
    
    p=firstPermutation(n);
    lp=最后置换(n);
    做{
    p=下一个术语(p);
    if(sum指数(p)=k){
    
    std::cout基本递归实现可以是:

    void listIntegersWithWeight(int currentBitCount, int currentWeight, uint32_t pattern, int index, int n, int k, std::vector<uint32_t> &res)
    {
        if (currentBitCount > n ||
            currentWeight > k)
            return;
    
        if (index < 0)
        {
            if (currentBitCount == n && currentWeight == k)
                res.push_back(pattern);
        }
        else
        {
            listIntegersWithWeight(currentBitCount, currentWeight, pattern, index - 1, n, k, res);
            listIntegersWithWeight(currentBitCount + 1, currentWeight + index, pattern | (1u << index), index - 1, n, k, res);
        }
    }
    

    在我的电脑上,使用相同的参数,这只需要半秒钟。可能还可以进一步改进。

    感谢您非常出色的实现。但是,它只比我在原始帖子中添加的解决方案快一点点。如果我看一看固定位数的最大列表,32位字、16位和s在8位31位的表中,该表的维数为8908546,由C++代码生成,其中G++-0和12+G++-O3,而我的F90实现用GoFrTRA-O0和15S用GFRTRAN -O3生成了70年代的同一个列表。我不清楚如何使用64位整数的DeBuijn技术。澄清了关于debrujin序列的部分-它们也适用于其他位计数。我已经链接了一个您可能会适应的序列。@harold同意,我已经更新了,在适用的情况下包含了内置替代项。您如何从main调用这样一个函数?抱歉,初始值对我来说不太清楚。而且我猜它在ui中也可以正常工作nt64或甚至uint128(编译器允许)?@Blaise例如
    listIntegersWithWeight(0,0,0,31,n,k,res)
    对于32位整数,31是最高位的索引。只要稍作修改,它就可以用于较大的类型,
    1u递归解决方案非常有效。在最近的PC上,在n=15最高索引=42和k=15*21(最坏情况)的情况下,生成长度为15亿的列表只需32秒,使用g++-O3。解决了我遇到的瓶颈(当然还有其他的)@Blaise btw作为一个有用的变量,如果您切换两个递归调用的顺序,那么列表将以相反的顺序生成。
    
    p = firstPermutation(n);
    lp = lastPermutation(n);
    
    do {
        p = nextPermutation(p);
        if (sumIndices(p) == k){
            std::cout << "p:" << p << std::endl;
        }
    } while(p != lp);
    
    void listIntegersWithWeight(int currentBitCount, int currentWeight, uint32_t pattern, int index, int n, int k, std::vector<uint32_t> &res)
    {
        if (currentBitCount > n ||
            currentWeight > k)
            return;
    
        if (index < 0)
        {
            if (currentBitCount == n && currentWeight == k)
                res.push_back(pattern);
        }
        else
        {
            listIntegersWithWeight(currentBitCount, currentWeight, pattern, index - 1, n, k, res);
            listIntegersWithWeight(currentBitCount + 1, currentWeight + index, pattern | (1u << index), index - 1, n, k, res);
        }
    }
    
    void listIntegersWithWeight(int currentBitCount, int currentWeight, uint32_t pattern, int index, int n, int k, std::vector<uint32_t> &res)
    {
        if (currentBitCount > n || 
            currentWeight > k ||
            currentBitCount + index + 1 < n ||
            currentWeight + (index * (index + 1) / 2) < k)
            return;
    
        if (index < 0)
        {
            if (currentBitCount == n && currentWeight == k)
                res.push_back(pattern);
        }
        else
        {
            listIntegersWithWeight(currentBitCount, currentWeight, pattern, index - 1, n, k, res);
            listIntegersWithWeight(currentBitCount + 1, currentWeight + index, pattern | (1u << index), index - 1, n, k, res);
        }
    }