打印序列中的1的个数,直到一个数字,而不实际计算1
Print number of 1s in a sequence up to a number, without actually counting 1s
面试问题:
编写一个程序,输入'N'(unsigned long)并打印两列,第一列打印从1到N的数字(十六进制格式),第二列打印左列中数字的二进制表示中的1的个数。条件是该程序不应该计算1(因此不需要"每个数字"计算1/不需要除法运算符)。
我试图通过利用0x0到0xF中没有1的事实来实现这一点,可以重用为任何数字生成1。我正在粘贴代码(基本一个没有错误检查。)它给出了正确的结果,但我对空间使用不满意。我该如何改进呢?(我也不确定这是否是面试官想要的)。
void printRangeFasterWay(){
uint64_t num = ~0x0 ;
cout << " Enter upper number " ;
cin >> num ;
uint8_t arrayCount[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4} ;
// This array will store information needed to print
uint8_t * newCount = new uint8_t[num] ;
uint64_t mask = 0x0 ;
memcpy(newCount, &arrayCount[0], 0x10) ;
uint64_t lower = 0;
uint64_t upper = 0xF;
uint64_t count = 0 ;
uint32_t zcount= 0 ;
do{
upper = std::min(upper, num) ;
for(count = lower ; count <= upper ; count++){
newCount[count] = (uint32_t)( newCount[count & mask] + newCount[(count & ~mask)>>(4*zcount)]) ;
}
lower += count ;
upper |= (upper<<4) ;
mask = ((mask<<4) | 0xF ) ;
zcount++ ;
}while(count<=num) ;
for(uint64_t xcount=0 ; xcount <= num ; xcount++){
cout << std::hex << " num = " << xcount << std::dec << " number of 1s = " << (uint32_t)newCount[xcount] << endl;
}
}
编辑添加示例运行
Enter upper number 18
num = 0 number of 1s = 0
num = 1 number of 1s = 1
num = 2 number of 1s = 1
num = 3 number of 1s = 2
num = 4 number of 1s = 1
num = 5 number of 1s = 2
num = 6 number of 1s = 2
num = 7 number of 1s = 3
num = 8 number of 1s = 1
num = 9 number of 1s = 2
num = a number of 1s = 2
num = b number of 1s = 3
num = c number of 1s = 2
num = d number of 1s = 3
num = e number of 1s = 3
num = f number of 1s = 4
num = 10 number of 1s = 1
num = 11 number of 1s = 2
num = 12 number of 1s = 2
我有一个稍微不同的方法可以解决你的内存问题。它基于这样一个事实,即按位运算i & -i
给出的是数字i
中2的最小幂。例如:For i = 5
, i & -i = 1
, For i = 6
, i & -i = 2
。现在,对于代码:
void countBits(unsigned N) {
for (int i = 0;i < N; i ++)
{
int bits = 0;
for (int j = i; j > 0; j= j - (j&-j))
bits++;
cout <<"Num: "<<i <<" Bits:"<<bits<<endl;
}
}
我希望我正确理解了你的问题。希望能有所帮助
编辑:好的,试试这个——这是不使用每个数字中的每个位的动态规划:
void countBits(unsigned N) {
unsigned *arr = new unsigned[N + 1];
arr[0]=0;
for (int i = 1;i <=N; i ++)
{
arr[i] = arr[i - (i&-i)] + 1;
}
for(int i = 0; i <=N; i++)
cout<<"Num: "<<i<<" Bits:"<<arr[i]<<endl;
}
希望这个效果更好
到目前为止发布的几个答案使用了位移位(只是除以2的另一种说法)或掩蔽。这让我觉得有点欺骗。在4位模式中使用'1'位计数也是如此按4位块匹配。
一个简单的递归解决方案如何使用虚二叉树的位。每个左分支都包含一个"0"右分支包含一个'1'。然后进行深度优先遍历,在向下的过程中计算1位的数量。一次到达树的底部,在计数器上加1,打印出到目前为止找到的1位的数量,返回一级再递归。
当计数器达到所需数时停止递归。
我不是一个C/c++程序员,但这里有一个REXX解决方案,应该没有太多的想象力翻译。请注意神奇的数字32就是Unsigned long类型的位数。设置为任意值
/* REXX */
SAY 'Stopping number:'
pull StopNum
Counter = 0
CALL CountOneBits 0, 0
return
CountOneBits: PROCEDURE EXPOSE Counter StopNum
ARG Depth, OneBits
If Depth = 32 then Return /* Number of bits in ULong */
if Counter = StopNum then return /* Counted as high as requested */
call BitCounter Depth + 1, OneBits /* Left branch is a 0 bit */
call BitCounter Depth + 1, OneBits + 1 /* Right branch is a 1 bit */
Return
BitCounter: PROCEDURE EXPOSE Counter StopNum
ARG Depth, OneBits
if Depth = 32 then do /* Bottom of binary bit tree */
say D2X(Counter) 'contains' OneBits 'one bits'
Counter = Counter + 1
end
call CountOneBits Depth, OneBits
return
结果:
Stopping number:
18
0 contains 0 one bits
1 contains 1 one bits
2 contains 1 one bits
3 contains 2 one bits
4 contains 1 one bits
5 contains 2 one bits
6 contains 2 one bits
7 contains 3 one bits
8 contains 1 one bits
9 contains 2 one bits
A contains 2 one bits
B contains 3 one bits
C contains 2 one bits
D contains 3 one bits
E contains 3 one bits
F contains 4 one bits
10 contains 1 one bits
11 contains 2 one bits
可以通过适当的位交换在恒定时间内相对简单地完成。不能数1,也不能除法。我认为您保持已知位值数组的方法是正确的:
int bits(int x)
{
// known bit values for 0-15
static int bc[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
// bit "counter"
int b = 0;
// loop iterator
int c = 0;
do
{
// get the last 4 bits in the number
char lowc = static_cast<char>(x & 0x0000000f);
// find the count
b += bc[lowc];
// lose the last four bits
x >>= 4;
++c;
// loop for each possible 4 bit combination,
// or until x is 0 (all significant bits lost)
}
while(c < 8 && x > 0);
return b;
}
说明
下面的算法与你的算法类似,但扩展了这个想法(如果我理解正确的话)。它不会按照问题的指示对"每个数字"进行任何计算,而是使用存在于长度为2的幂的序列之间的递归。基本上,观察到的是,对于序列0, 1,..,2^n-1
,我们可以以以下方式使用序列0, 1, ...,2^(n-1)-1
。
设f(i)
为所有0<=i<2^(n-1)
的i
和f(2^(n-1)+i)=f(i)+1
中的1个数。(自己验证)
c++中的算法
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{
const int N = 32;
int* arr = new int[N];
arr[0]=0;
arr[1]=1;
for ( int i = 1; i < 15; i++ )
{
int pow2 = 1 << i;
int offset = pow2;
for ( int k = 0; k < pow2; k++ )
{
if ( offset+k >= N )
goto leave;
arr[offset+k]=arr[k]+1;
}
}
leave:
for ( int i = 0; i < N; i++ )
{
printf( "0x%8x %16d", i, arr[i] );
}
delete[] arr;
return EXIT_SUCCESS;
}
注意在for循环中
for ( int i = 0; i < 15; i++ )
如果大于15,可能会溢出为负数,否则如果想大于15,请使用unsigned int
该算法在O(N)
中运行,使用O(N)
空间
这是一个具有0 (nlogn)时间复杂度和0(1)内存使用的方法。其思想是获取该数字的十六进制等效物,并对其进行迭代以获得每个十六进制数字的1个数。
int oneCount[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
int getOneCount(int n)
{
char inStr[70];
sprintf(inStr,"%X",n);
int i;
int sum=0;
for(i=0; inStr[i];i++)
{
if ( inStr[i] > '9' )
sum += oneCount[inStr[i]-'A' + 10];
else
sum+= oneCount[inStr[i] -'0'];
}
return sum;
}
int i,upperLimit;
cin>>upperLimit;
for(i=0;i<=upperLimit;i++)
{
cout << std::hex << " num = " << i << std::dec << " number of 1s = " << getOneCount(i) << endl;
}
enum bit_count_masks32
{
one_bits= 0x55555555, // 01...
two_bits= 0x33333333, // 0011...
four_bits= 0x0f0f0f0f, // 00001111....
eight_bits= 0x00ff00ff, // 0000000011111111...
sixteen_bits= 0x0000ffff, // 00000000000000001111111111111111
};
unsigned int popcount32(unsigned int x)
{
unsigned int result= x;
result= (result & one_bits) + (result & (one_bits << 1)) >> 1;
result= (result & two_bits) + (result & (two_bits << 2)) >> 2;
result= (result & four_bits) + (result & (four_bits << 4)) >> 4;
result= (result & eight_bits) + (result & (eight_bits << 8)) >> 8;
result= (result & sixteen_bits) + (result & (sixteen_bits << 16)) >> 16;
return result;
}
void print_range(unsigned int low, unsigned int high)
{
for (unsigned int n= low; unsigned int n<=high; ++n)
{
cout << std::hex << " num = " << xcount << std::dec << " number of 1s = " << popcount32(n) << endl;
}
}
- 如何创建一个函数来计算并返回平均值、最大值和最小值
- 不计算一个范围内的完美数
- 3-3. 编写一个程序来计算每个不同单词在其输入中出现的次数
- 我的目标是编写一个程序来计算和存储字符串在字符数组中出现的位置
- 从另一个静态常量数组初始化静态常量数组(只需少量计算)
- 为什么我必须添加一个小数才能在C++中正确计算此数学
- 视觉我希望一个函数在另一个函数C++中进行计算
- 计算单词中的音节数,但考虑到单词中彼此相邻的任何元音都算作一个音节
- 如何使用条件计算 3D 网格中从一个点到另一个点的所有路径
- 计算在同一位置至少包含一个常用字符的不同字符串对
- 返回一个计算了参数的函数
- 我在c++中遇到了一个奇怪的错误,其中一个计算2个小整数加法的语句溢出到一个长值中
- 在C++中,是否可以编写一个条件的lambda,即只是一个计算结果为真或假的条件
- 你能在C++中制作一个计算的goto吗?
- 为什么需要 FPU 重置以防止 NaN 结果传播到下一个计算结果?
- 创建一个C++计算奇数、偶数和零数的程序
- 在C++中是否已经为文件系统迷你过滤器驱动程序编写了一个计算MD5哈希的函数
- 编写一个计算n的子程序.使用此子程序时,制作一个计算(a+b)的程序
- 将数组从C/C++传递给Fortran,并返回一个计算的数组
- 扩展一个计算线路频率的程序