我正在尝试解决此问题:
给出一个整数 k ,找到最小整数 n ,使得在{1,2,...的二进制表示形式中总数为1。 。 。, n }至少为 k 。
例如,给定 k = 4,我们希望 n = 3,因为1、2和3的二进制表示形式包含1、1和2 (分别)和1 +1 + 2≥4。
我尝试使用Log(n)中从(1到n)的设置位进行计数,无法有效地找到最小n。
编辑:
代码:计算编号。设置的位从1到n(Reference)的个数,但是找到最小n是一个问题。是否有任何方法可以解决这个问题?
int getSetBitsFromOneToN(int N){
int two = 2,ans = 0;
int n = N;
while(n){
ans += (N/two)*(two>>1);
if((N&(two-1)) > (two>>1)-1) ans += (N&(two-1)) - (two>>1)+1;
two <<= 1;
n >>= 1;
}
return ans;
}
答案 0 :(得分:1)
该算法相对简单。我们将使用一系列函数{a(m),b(m),c(m)而不是一次累积一个二进制表示的1的数目,而不是一次累积一个数字。 。}靠近目标,最后最多留下几个数字以手动添加。每个函数的格式为,其中x是函数的编号(对于a(m)x = 0,对于b(m)x = 1 ...)。
这些函数基于二进制数的特征:在从1到1的所有数字中 以其{1,2 ... n}的二进制表示形式可以知道1的累加数。
让我们看一下数字,它位于二进制文件1111中。您可以知道从1(0001)到15(1111)的所有数字中1的计数-它计算的是您可以放置多少种方法四分之一的分数(4)乘以1,再加上四分之六的分数可以乘以2(6)乘2,四分之三的分数可以四分之三(4)乘以3数在4个地方中,有4个(1)乘以4。所以总数是32,也就是。您会注意到,对于任何这样的数字n = ,累积的1的数量为。让我们根据上面的决定将此函数命名为a(m)(此处x = 0,因此无需在该函数中添加元素)。例如:
,依此类推。因此对于数字15 ,我们计算a(4)并得到32个累加的1。我们还将注意到,数字中正好有m 1个数字(所有数字均设置为1)。
知道这一点后,您将数字k找出小于k的最接近的a(m),而a(m + 1)将大于k。如果a(m + 1)仅比k多m + 1,则以a(m + 1)作为答案并完成算法。由于a(m + 1)至少包含m + 2,这意味着如果没有它,您将无法累积所需的所有k 1。
如果k大于大于a(m + 1)的m + 1但大于a(m),则需要通过定义第二个函数来进行第二步近似处理-我们将其称为b(m)。我们将定义b(m)= 。该数字将完全等于(而不是a函数的)例如:
我们定义b的原因是为了描述第一批的数字与第二批的第二批数字之间1的累加的唯一差异-另加另一个最高有效的1给他们第二批中的每个数字。这就是为什么我们现在只看而不看的原因。
通过添加两个函数,我们可以得到数字n。如果我们在经过两次近似运算后仍未达到最后一个k,则可以逐个累加数字,直到达到k。
让我们假设k = 50。我们知道a(4)是我们可以获得的最接近的数字,它是仍然低于50的最大数字。如上所述,a(5)将使我们达到80。因此,a(4)是解的前半部分,即15。
其余1为50-32 = 18。我们需要查看需要处理多少个数字才能累积超过15个1。通过计算b函数,我们看到b(2)使我们更近,而b(3)等于2。因为我们知道b(3)表示的数字中至少有4 1个,所以我们知道这是我们需要的数字-低于它的任何数字都只会累加16 1个,而我们需要18个。所以我们选择b(3 ),即或8。结果为15 + 8 = 23,这是最低的数字,在所有{1,2..23}二进制表示形式中,至少有50个累加的1。 / p>
如果需要再次迭代,可以定义,它将使我们更加接近。例如,对于k = 120,我们得到a(5)+ b(3)= 100,加上c(2)将使我们再增加12个到112。我们可以手动添加缺失的8个数字或决定添加{{0 }}通过添加a(5)+ b(3)+ c(2)+ d(1)将我们带到119。这意味着下一个数字必须是累计k或大于1的最小n。 a(5)= 31,b(3)= 8,c(2)= 4和d(1)= 2,因此n = 46-从119 1的45中收集的下一个数字。
复杂度为O(log(k))-我们有log(k)个步骤来获得a(m),最多还有另一个log(k)累加来获得b(m)和我们最终的n。>
代码
//This represents the function a(m), b(m)... etc.
public int getFuncResult(int funcNum, int arg) {
Double result = Math.pow(2,arg-1)*arg+funcNum*Math.pow(2,arg);
return result.intValue();
}
//This is the iterative algorithm described: add a(m)+b(m)... until k
public int countOnesToKIter(int k) {
int funcNum = 0;
int counter = 0;
int retVal = 0;
int exponent = 0;
while (k > 0) {
//for the current function, find the appropriate m
while (k > counter) {
exponent++;
counter = getFuncResult(funcNum, exponent);
}
//if the last number contains more 1's than the difference, use it.
if (counter-k < exponent) {
counter=getFuncResult(funcNum, exponent-2);
retVal+=Math.pow(2,exponent-2);
} else {
counter = getFuncResult(funcNum, exponent-1);
retVal+=Math.pow(2,exponent-1);
}
funcNum ++;
exponent=0;
k = k-counter;
counter = 0;
}
return retVal;
}