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);
}
}