用10个设定位找到第n个int n是0 <= n <= 30 045 014的范围内的int 第0个int = 1023,第1个= 1535,依此类推
snob()相同的位数,
返回大于n的最小整数,其设置位数与n
int snob(int n) {
int a=n&-n, b=a+n;
return b|(n^b)/a>>2;
}
呼叫snob n次将起作用
int nth(int n){
int o =1023;
for(int i=0;i<n;i++)o=snob(o);
return o;
}
示例
有没有办法更快找到它?
我发现了一种模式,但不确定它是否有用。
使用factorial可以找到所有10个设置位都是连续的“索引”
1023&lt;&lt; x =(x + 10)! /(x!* 10!) - 第1个整数
1023<<1 is the 10th
1023<<2 is the 65th
1023<<3 the 285th
...
是的,我不是学生,这不是作业。
编辑:
找到snob()
的替代品https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
int lnbp(int v){
int t = (v | (v - 1)) + 1;
return t | ((((t & -t) / (v & -v)) >> 1) - 1);
}
答案 0 :(得分:1)
让我们考虑设置k = 10位的数字。
诀窍是确定给定n的最重要的等级。
有一个长度为k的数:C(k,k)= 1。有k + 1 = C(k + 1,k)个长度为k + 1的... ...有长度为m的C(m,k)个数。
对于k = 10,限制n为1 + 10 + 55 + 220 + 715 + 2002 + 5005 + 11440 + ...
对于给定的n,您可以轻松找到相应的m。然后问题被简化为找到设置了k-1位的n-C(m,k)数。等等递归。
使用预先计算的表格,这可以非常快。 30045015需要30次查找,所以我猜最坏的情况是29 x 30/2 = 435次查找。
(这是基于线性查找,有利于小值。通过二分法搜索,你可以将其减少到小于29 x lg(30)= 145更差的查找。)
<强>更新强>
我以前的估计是悲观的。实际上,当我们寻找k位时,m只有10次测定。在线性情况下,更糟糕的245次查找,在二分法情况下,小于50。
(我不会在估算中排除一个错误,但显然这种方法效率很高,不需要势利。)
答案 1 :(得分:1)
我已经构建了一个满足您需求的实现。
/** A lookup table to see how many combinations preceeded this one */
private static int[][] LOOKUP_TABLE_COMBINATION_POS;
/** The number of possible combinations with i bits */
private static int[] NBR_COMBINATIONS;
static {
LOOKUP_TABLE_COMBINATION_POS = new int[Integer.SIZE][Integer.SIZE];
for (int bit = 0; bit < Integer.SIZE; bit++) {
// Ignore less significant bits, compute how many combinations have to be
// visited to set this bit, i.e.
// (bit = 4, pos = 5), before came 0b1XXX and 0b1XXXX, that's C(3, 3) + C(4, 3)
int nbrBefore = 0;
// The nth-bit can be only encountered after pos n
for (int pos = bit; pos < Integer.SIZE; pos++) {
LOOKUP_TABLE_COMBINATION_POS[bit][pos] = nbrBefore;
nbrBefore += nChooseK(pos, bit);
}
}
NBR_COMBINATIONS = new int[Integer.SIZE + 1];
for (int bits = 0; bits < NBR_COMBINATIONS.length; bits++) {
NBR_COMBINATIONS[bits] = nChooseK(Integer.SIZE, bits);
assert NBR_COMBINATIONS[bits] > 0; // Important for modulo check. Otherwise we must use unsigned arithmetic
}
}
private static int nChooseK(int n, int k) {
assert k >= 0 && k <= n;
if (k > n / 2) {
k = n - k;
}
long nCk = 1; // (N choose 0)
for (int i = 0; i < k; i++) {
// (N choose K+1) = (N choose K) * (n-k) / (k+1);
nCk *= (n - i);
nCk /= (i + 1);
}
return (int) nCk;
}
public static int nextCombination(int w, int n) {
// TODO: maybe for small n just advance naively
// Get the position of the current pattern w
int nbrBits = 0;
int position = 0;
while (w != 0) {
final int currentBit = Integer.lowestOneBit(w); // w & -w;
final int bitPos = Integer.numberOfTrailingZeros(currentBit);
position += LOOKUP_TABLE_COMBINATION_POS[nbrBits][bitPos];
// toggle off bit
w ^= currentBit;
nbrBits++;
}
position += n;
// Wrapping, optional
position %= NBR_COMBINATIONS[nbrBits];
// And reverse lookup
int v = 0;
int m = Integer.SIZE - 1;
while (nbrBits-- > 0) {
final int[] bitPositions = LOOKUP_TABLE_COMBINATION_POS[nbrBits];
// Search for largest bitPos such that position >= bitPositions[bitPos]
while (Integer.compareUnsigned(position, bitPositions[m]) < 0)
m--;
position -= bitPositions[m];
v ^= (0b1 << m--);
}
return v;
}
现在进行一些解释。 LOOKUP_TABLE_COMBINATION_POS[bit][pos]
是算法的核心,使其尽可能快。该表的设计使得位置k
的{{1}}位的位模式的位置为“\ sum_ {i = 0} ^ {k - 1} {LOOKUP_TABLE_COMBINATION_POS [i] [p_i]}
直觉是我们尝试逐个移回位,直到我们到达所有位都处于最低位置的模式。将p_0 < p_1 < ... < p_{k - 1}
位从i
移至k + 1
会向后移动k
个位置,前提是所有较低位位于最右侧位置(无移动位)因为我们跳过了C(k-1, i-1)
个插槽中i-1
位的所有可能组合。
因此我们可以将位模式“解码”到一个位置,跟踪遇到的位。然后我们前进k-1
个位置(如果我们枚举n
位的所有可能位置,则翻转)并再次编码该位置。
要对模式进行编码,我们会反转该过程。为此,只要位置小于我们的目标,我们就会将位从其起始位置向前移动。我们可以代替通过k
进行线性搜索,对我们的目标索引LOOKUP_TABLE_COMBINATION_POS
使用二进制搜索,但它几乎不需要,int的大小并不大。然而,我们重复使用我们的变体,一个较小的位也必须位于不太重要的位置,以便我们的算法有效m
其中O(n)
。
我仍然使用以下断言来显示生成的算法:
n = Integer.SIZE