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

时间:2011-08-16 10:55:48

标签: c++ algorithm puzzle

面试问题:

创建一个输入'N'(无符号长)并打印两列的程序,第一列打印从1到N的数字(十六进制格式),第二列打印二进制表示中的1的数量左栏中的数字。条件是这个程序不应该计算1s(所以没有计算'每个数''得到1s /没有除法运算符。)

我试图通过利用这样的事实来实现这一点: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

6 个答案:

答案 0 :(得分:5)

我有一个稍微不同的方法,可以解决你的记忆问题。它基于以下事实:按位运算i & -i为您提供数字i中最小的2的幂。例如,i = 5i & -i = 1i = 6i & -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;
}

希望这更好用

答案 1 :(得分:2)

到目前为止发布的几个答案都使用了位移(只有另一个词除以2)或 比特掩蔽。这让我觉得有点作弊。同样,以4位模式使用“1”位计数 通过4比特的块进行匹配。

使用虚构二进制树的简单递归解决方案怎么样?每个左分支包含一个'0',每个 右分支包含'1'。然后进行深度优先遍历,计算向下的1位数。一旦 到达树的底部,向计数器添加一个,打印出目前为止找到的1位数,然后退出 一级并再次递归。

当计数器达到所需的数字时停止递归。

我不是C / C ++程序员,但这里是一个REXX解决方案,应该没有太多想象力。注意 幻数32只是无符号长的位数。将其设置为任何内容

/* 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

这个答案在时间和空间上都是合理有效的。

答案 2 :(得分:1)

通过适当的位切换,可以在恒定时间内相对简单地完成。不计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;
}

答案 3 :(得分:0)

说明

以下算法与您的算法相似,但扩展了这个想法(如果我理解您的方法正确的话。)它不按照问题的指示进行任何“每个数字”的计算,而是使用在序列之间存在的递归长度为2的幂。基本上,观察结果是对于序列0, 1,..,2^n-1,我们可以按以下方式使用序列0, 1, ...,2^(n-1)-1

对于所有f(i)if(2^(n-1)+i)=f(i)+1的数量,然后是0<=i<2^(n-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)空格。

答案 4 :(得分:0)

这是一种具有O(nlogn)时间复杂度和O(1)内存使用的方法。我们的想法是获得Hex的等价数字并迭代它以获得每个Hex数字的数量。

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;

}

答案 5 :(得分:0)

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;
    }
}