有一个问题,我现在正在努力,它是如下:
有两个数x1和x2,且x2〉x1。
例如x1= 5;且x2 = 10;
我必须求出x1和x2的二进制数之和。
5 = 101 => 2 ones
6 = 110 => 2 ones
7 = 111 => 3 ones
8 = 1000 => 1 one
9 = 1001 => 2 ones
10= 1010 => 2 ones
so the sum will be
sum = 2 + 2 + 3 + 1 + 2 + 2 = 12 ones;
因此,我已经设法使一个代码,甚至没有转移的数字到二进制和浪费执行时间。
我注意到每个2^n
中1的个数为n >= 1
,例如:x一个三个一个x一个四个一个x一个五个一个
如果需要,您可以在此处进行测试:https://www.rapidtables.com/convert/number/decimal-to-binary.html?x=191
并且在每个2^n and 2^(n+1)
之间有连续的数字,正如您将在此示例中看到的:
num number of ones
2^4 = 16 1
17 2
18 2
19 3
20 2
21 3
22 3
23 4
24 2
25 3
26 3
27 4
28 3
29 4
30 4
31 5
2^5 = 32 1
所以我写了一个代码可以找出2^n and 2^(n+1)
之间有多少个1
int t; ////turns
int bin = 1; //// numbers of ones in the binary format ,,, and 1 for 2^5
int n1 = 32; //// 2^5 this is just for clarification
int n2 = 64; //// 2^6
int *keep = malloc(sizeof(int) * (n2 - n1); ///this is to keep numbers because
/// i'll need it later in my consecutive numbers
int i = 0;
int a = 0;
n1 = 33 //// I'll start from 33 cause "bin" of 32 is "1";
while (n1 < n2) /// try to understand it now by yourself
{
t = 0;
while (t <= 3)
{
if (t == 0 || t == 2)
bin = bin + 1;
else if (t == 1)
bin = bin;
else if (t == 3)
{
bin = keep[i];
i++;
}
keep[a] = bin;
a++;
t++;
}
n1++;
}
无论如何,正如你所看到的,我接近解决问题,但他们给予了我巨大的数字,我必须找到他们之间的,不幸的是,我已经尝试了很多方法来计算“总和”使用上述代码,我结束了时间执行问题。
例如:1, 1000000000 the numbers of ones is >>> 14846928141
所以你能不能给予我一点提示下一步该怎么做,先谢了。
我这么做是为了代码大战挑战赛https://www.codewars.com/kata/596d34df24a04ee1e3000a25/train/c
4条答案
按热度按时间ffx8fchx1#
可以通过计算
1
到n
范围内的位数,并对任何子范围使用简单的减法来解决这个问题:efzxgjgh2#
下面是一个加速建议:
1.找出最小的y1,使y1〉= x1且y1是2的幂
1.找出最大的y2,使y2〈= x2且y2为2的幂
1.求p1和p2,使2^p1 = y1,2^p2 = y2
1.计算y1和y2之间1:s的数量
1.分别处理x1到y1和y2到x2
1.将4和5的结果相加
让我们关注第4步,假设f(n)是1到(2^n)-1的和,我们可以很快地实现f(n)= 2 * f(n-1)+2 ^(n-1)和f(1)= 1,这可以进一步细化,这样你就不必处理递归调用,但我高度怀疑它是否重要,不管怎样,f(n)= n * 2 ^(n-1)
要得到y1和y2之间的结果,只需使用f(p2)-f(p1)
对于步骤5,您可以使用步骤4的修改版本。
编辑:
也许我说"快速实现"太快了。这里有一种理解它的方法。1到2 ¹ -1的数量很容易看到。2 ¹以下的两个二进制数只有0和1。为了得到1到2 ¹的数量,我们取2 ¹以下的数字,并做一列:
克隆:
并将0:s放在前半部分之前,将1:s放在后半部分之前:
为了得到2 3,我们做同样的事情。克隆它:
再加上0和1:
现在应该很容易理解为什么f(n)= 2 * f(n-1)+2 ^(n-1)了,克隆得到2f(n-1),将0:s和1:s相加得到2 ^(n-1),如果2 ^(n-1)很难理解,请记住2 ^(n-1)=(2^n)/2,在每一步中,我们有2^n行,其中一半得到额外的1。
编辑2:
当我查看这些列时,我想到了如何执行步骤5。假设您要查找从10到15的1:s的数量。用于此操作的二进制表为:
看看12 - 15的区间。二进制的最后两位是0 - 3对应表的副本。这可以利用,但我把它留给你。
编辑3:
这是一个有趣的问题,我写了一些python代码来做这个,我遇到了一些递归调用太多的问题,但这可以很容易地解决,而且把它转换成C应该不会太复杂:
我做了一个图像,这样你就可以更容易地理解,如果我们用
numberOfOnes(12)
调用函数numberOfOnes
,a
,b
和c
在函数numberOfOnes
中做了什么:我终于把它转换成了C语言。当然,我使用了一些我在这里找到的关于堆栈溢出的代码。我借用了log2和pow的整数版本的代码,并做了一些小的修改。
这段代码也许可以进一步优化,但这不是必须的。它运行得很快,我无法衡量它的性能。
ou6hu8tu3#
输出:一个计数为12
这里有一个方法来计算两个值之间的每个值的每一位。移位/掩码很可能比算术运算符更快,但仍然可能超时。你需要一个聪明的算法,就像另一个答案建议的那样,我认为,但这里是愚蠢的蛮力方法:)
4urapxun4#
这就是我解决问题的方法:
考虑从1到16的数字:
如果你注意每一列,你会发现一个模式,列索引
i (0,1,2 ...)
处从右数的位运行一个长度为2**(i+1)
的循环,也就是每2**(i+1)
行,列i
中的模式重复其自身。还要注意,第一个循环从给定列中第一次出现1开始。模式中1的数量是模式长度的一半。所以,如果要把所有的1加起来,直到n,我们必须记录下每个模式重复了多少次,以及一个模式是否没有完成。
设
x
为二进制数n
的最大指数,s
为n
之前所有1的和。然后,对于i = (0, 1, 2, ... , x)
,将(n / 2**(i+1)*(2**i)
与s
相加。如果余数大于2**i
,则将2**i
与s
相加。否则将余数相加,然后从n
中减去2**i
并重复该过程。n = 7 -> x = 2
也许不是最好的解释或最漂亮的解决方案,但它工作得很好。
在python中:
然后,对于范围
[a, b]
,只需计算cnt_bin(b) - cnt_bin(a-1)