打印序列中的1的个数,直到一个数字,而不实际计算1

Print number of 1s in a sequence up to a number, without actually counting 1s

本文关键字:一个 计算 数字 不实际 打印      更新时间:2023-10-16

面试问题:

编写一个程序,输入'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)if(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;
    }
}
相关文章: