19

An interview question:

Make a program which takes input 'N'(unsigned long) and prints two columns, 1st column prints numbers from 1 to N (in hexadecimal format) and second column prints the number of 1s in the binary representation of the number in the left column. Condition is that this program should not count 1s (so no computations 'per number' to get 1s/ no division operators).

I tried to implement this by leveraging fact that No of 1s in 0x0 to 0xF can be re-used to generate 1s for any number. I am pasting code ( basic one without error checking.) Its giving correct results but I am not happy with space usage. How can I improve on this? ( Also I am not sure if its what interviewer was looking for).

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

}

Edited to add sample run

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
Cœur
  • 37,241
  • 25
  • 195
  • 267
Manish Baphna
  • 422
  • 2
  • 13
  • 8
    There shouldn't be any reason to close this, especially since the OP showed an effort to solve the problem at hand and posted what he could best come up with. – Alok Save Aug 16 '11 at 11:01
  • 4
    This probably belongs to codereview.stackexchange.com. – RedX Aug 16 '11 at 11:01
  • 1
    Perhaps you could paste in what the output looks like? – Kev Aug 16 '11 at 11:33
  • @redx `@als I will consult with the code review folks and see what they think. – Kev Aug 16 '11 at 11:34
  • I don't fully understand what you mean by this: "Condition is that this program should not count 1s ( so no computations 'per number' to get 1s/ no division operators)". Also take a look at `std::bitset<>::count` (http://www.cplusplus.com/reference/stl/bitset/count/) which returns the number of bits that are set in the bitset. But `std::bitset<>::count` internally does the counting to compute the number of bits that are set. – yasouser Aug 16 '11 at 12:51
  • Can you also put a sample input and output for ease ? – iammilind Aug 16 '11 at 14:43
  • @yasouser : What that meant is that if we are giving input as 'N' then within 'N' iteration of loop we should get results. If we try to computer '1s' by counting, then total iterations would be Sum(Mx) where x is from 1 to N , and m is the 'iteration' done to count '1's in a number. – Manish Baphna Aug 16 '11 at 15:34
  • I edited the task in your question to fit your output. Please check if this is what you actually want. – Paŭlo Ebermann Aug 16 '11 at 16:18
  • @Manu, your solution is O(N*bits of integer/4). You can get an equivalent solution simply by using a bit set count function which is O(log log (bits of integer)) as well. – MSN Aug 16 '11 at 20:33
  • ! I dont think the answer any of the answer is satisfactory enough. The real question, IMHO is "Can you generate the series that will tell for nth term how many 1's will be contained in the term, assuming we start from n=0." I already see the pattern> 0 Next p – Ajeet Ganga Aug 17 '11 at 03:02
  • Why close this? I have an answer to propose. Anyone that wants it open should flag it for moderator attention. I think the question is specifically thinking outside the box. The problem isn't to find the bits in any given number, but specifically to count. Instead of passing the number to a function to count the bits, look at the pattern of building a binary number. Here's my method signature: "void processLower(int currentBit, int higherCount, int *number, int goal)". Each step call itself with current bit 0, then 1 (incrementing number), print when current bit is < 0. – Jason Goemaat Aug 22 '11 at 21:51

6 Answers6

5

I have a slightly different approach which should solve your memory problem. Its based on the fact that the bitwise operation i & -i gives you the smallest power of two in the number i. For example, for i = 5, i & -i = 1, for i = 6, i & -i = 2. Now, for code:

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

I hope I understood your question correctly. Hope that helps

Edit: Ok, try this - this is dynamic programming without using every bit in every number:

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

Hopefully, this works better

kyun
  • 640
  • 4
  • 10
  • Note my numbers are printed in decimal - these could be easily switched to your preferred hex format – kyun Aug 16 '11 at 15:17
  • I am getting '1' for all number. Is there some typo in the code ? – Manish Baphna Aug 16 '11 at 15:26
  • I edited the code, makes sure you have brackets around the j & -j. and also make sure you set j = i, I had set it to N initially (accidentally) – kyun Aug 16 '11 at 15:30
  • 1
    Termination condition should be j= j - (j&-j). Well, it's a good method ,but doesn't match the condition of 'not counting 1s in each number'. – Manish Baphna Aug 16 '11 at 15:36
  • I think I see what you mean. Ok, editing post for a dynamic programming approach. Hang on now. – kyun Aug 16 '11 at 15:40
  • Actually, no I don't have anything else right now - sorry! I'll post something if I can think of something better – kyun Aug 16 '11 at 15:41
  • Added a better approach - does that help matters? – kyun Aug 16 '11 at 15:49
  • @kyun: The dynamic solution approach is notorious for its voracity on memory. Perhaps an approach using constant memory may be convenient. In that case, the OP has a better approach, though still stores all the previous values in array. However, for interviews sake, I think the interviewer will be quite pleased with the dynamic programming approach provided you also mention the extravagant usage of memory. – Shamim Hafiz - MSFT Aug 16 '11 at 19:04
  • The OP seems to use the same amount of memory, if not a little bit more. I don't see how it is 'extravagant' in memory usage - its O(N). Thats what all of the stored up solutions use. Also, if we were to use constant memory, I can't think of solving the problem in O(N) time – kyun Aug 16 '11 at 19:54
  • The memory requirement for DP is unreasonable given that N could be a number as large as 2^32 (ULong is at least this size). Same goes for any of the other array based solutions. See my solution using recursion which does a depth first search of a binary bit tree where the bit pattern from the root to a leaf corresponds to a 32 bit integer. Depth first search yields integers in order from 0..2^32. Just count the '1' bits on the way to the leaf and you get the answer. – NealB Aug 17 '11 at 15:03
  • The question specifies no counting ones or computations per number, I think it is asking for a recursive approach where the number is secondary to the algorithm and not the primary focus. For instance "void processLower(int currentBit, int higherCount, int *number, int goal)" where you print the number when currentBit is < 0, recursive call twice with currentBit - 1, once as current bit 0 and original higherCount, then increment number and call again as current bit 1 and higherCount + 1. – Jason Goemaat Aug 22 '11 at 21:59
2

Several of the answers posted so far make use of bit shifting (just another word for division by 2) or bit masking. This stikes me as a bit of a cheat. Same goes for using the '1' bit count in a 4 bit pattern then matching by chunks of 4 bits.

How about a simple recursive solution using an imaginary binary tree of bits. each left branch contains a '0', each right branch contains a '1'. Then do a depth first traversal counting the number of 1 bits on the way down. Once the bottom of the tree is reached add one to the counter, print out the number of 1 bits found so far, back out one level and recurse again.

Stop the recursion when the counter reaches the desired number.

I am not a C/C++ programmer, but here is a REXX solution that should translate without much imagination. Note the magic number 32 is just the number of bits in an Unsigned long. Set it to anything

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

Results:

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

This answer is resonably efficient in time and space.

NealB
  • 16,670
  • 2
  • 39
  • 60
  • I love this solution too. I initially thought you'd have a stack overflow but with a max depth of 32, it should be fine! Nice backtrack +1 – kyun Aug 17 '11 at 15:47
1

Can be done relatively trivially in constant time with the appropriate bit switching. No counting of 1s and no divisions. I think you were on the right track with keeping the array of known bit values:

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;
}
Chad
  • 18,706
  • 4
  • 46
  • 63
  • I like this solution. Memory wise, I think its the best you'll get. However, I understood not counting bits as not doing one number at a time - its the only potential flaw I see – kyun Aug 16 '11 at 20:03
0

Explanation

The following algorithm is like yours, but expands on the idea (if I understood your approach correctly.) It does not do any computation 'per number' as directed by the question, but instead uses a recursion that exists between sequences of lengths that are powers of 2. Basically, the observation is that for the sequence 0, 1,..,2^n-1 , we can use the sequence 0, 1, ...,2^(n-1)-1 in the following way.

Let f(i) be the number of ones in number i then f(2^(n-1)+i)=f(i)+1 for all 0<=i<2^(n-1). (Verify this for yourself)

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

Note that in the for loop

   for ( int i = 0; i < 15; i++ )

there may be overflow into negative numbers if you go higher than 15, otherwise use unsigned int's if you want to go higher than that.

Efficiency

This algorithm runs in O(N) and uses O(N) space.

ldog
  • 11,707
  • 10
  • 54
  • 70
0

Here is an approach that has O(nlogn) time complexity and O(1) memory usage. The idea is to get the Hex equivalent of the number and iterate over it to get number of ones per Hex digit.

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;

}
Shamim Hafiz - MSFT
  • 21,454
  • 43
  • 116
  • 176
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;
    }
}
MSN
  • 53,214
  • 7
  • 75
  • 105