可以通过组合和排列技术来解决。
F(N) = 排名
首先找到表示N所需的位数。在二进制表示中,数字可以构造如下:
N = cn 2^n + .... + c3 2^2 + c2 2^1 + c1 2^0
现在,为了在上述等式中找到n(或二进制表示的位数),我们可以假设它将是floor(log2(N)) + 1。例如,考虑任何数字,假设118 那么它可以表示为 floor(log2(118)) + 1 = 7 位。
n = floor(log2(118)) + 1;
以上公式仅为O(1)。现在,我们需要找出一个数字的二进制表示中有多少个 1。考虑使用伪代码来完成这项工作:
function count1(int N) {
int c = 0;
int N' = N;
while(N' > 0) {
N' -= 2^(floor(log2(N')));
c++;
}
}
上面的伪代码是O(logN)。我在 MATLAB 中编写了一个小脚本来测试我上面的伪代码,结果如下。
count1(6) = 2
count1(3) = 2
count1(118) = 5
完美,现在我们有了这些位的位数和 1 的数量。现在,可以应用简单的组合和排列来找到数字的秩。首先让我们假设,n 是表示数字所需的位数,c 是数字的位表示中的 1 的数量。因此,排名将由以下方式给出:
r = n ! / c ! * (n - c) !
编辑:根据 DSM 的建议,我已更正逻辑以找到正确的 RANK。想法是从排列中删除所有不需要的数字。所以添加了这段代码:
for i = N + 1 : 2^n - 1
if count(i) == c
r = r - 1;
end
end
我已经编写了一个 MATLAb 脚本来使用上述方法查找数字的排名:
function r = rankN(N)
n = floor(log2(N)) + 1;
c = count(N);
r = factorial(n) / (factorial(c) * factorial(n - c));
% rejecting all the numbers may have included in the permutations
% but are unnecessary.
for i = N + 1 : 2^n - 1
if count(i) == c
r = r - 1;
end
end
function c = count(n)
c = 0;
N = n;
while N > 0
N = N - 2^(floor(log2(N)));
c = c + 1;
end
上面的算法是O(1) + O(logN) + O(1) = O(logN)。输出是:
>> rankN(3)
ans =
1
>> rankN(4)
ans =
3
>> rankN(7)
ans =
1
>> rankN(118)
ans =
18
>> rankN(6)
ans =
3
注意:0 的等级始终为1,因为0 的上述方法将失败,因为log2(0) 未定义。