1

Trying to find maximum length of ones in a binary representation including negative numbers. In the following code input_file is a text file where:

  • first line is a number of lines with sample integers
  • every line staring from the second line has just one sample integer

An example file:

4 - number of samples

3 - sample

0 - ...

1 - ...

2 - ...

Result: 2

Task: print the maximum number of ones found among all sample integers in input file. Find solution that takes O(n) time and makes just one pass through all samples.

How to modify solution to work with negative integers of arbitrary (or at least for n ≤ 10000) size?

Update:

As I understand binary representation of negative numbers is based on Two's complement (https://en.wikipedia.org/wiki/Two's_complement). So, for example:

+3 -> 011

-3 -> 101

How to convert integer to binary string representation taking its sign into account in general case?

def maxConsecutive(input): 
    return max(map(len,input.split('0'))) 

def max_len(input_file):
    max_len = 0
    with open(input_file) as file:
        first_line = file.readline()
        if not first_line:
            return 0
        k = int(first_line.strip()) # number of tests
        for i in range(k):
            line = file.readline().strip()
            n = int(line)
            xs = "{0:b}".format(n)
            n = maxConsecutive(xs)
            if n > max_len:
                max_len = n
    return max_len

print(max_len('input.txt'))

Update 2: This is a second task B from Yandex contest training page: https://contest.yandex.ru/contest/8458/enter/?lang=en

You need to register there to test your solution.

So far All solutions given here fail at test 9.

Update 3: Solution in Haskell that pass all Yandex tests

import Control.Monad (replicateM)

onesCount :: [Char] -> Int
onesCount xs = onesCount' xs 0 0
    where
        onesCount' "" max curr 
            | max > curr = max 
            | otherwise  = curr
        onesCount' (x:xs) max curr
            | x == '1' = onesCount' xs max $ curr + 1 
            | curr > max = onesCount' xs curr 0 
            | otherwise = onesCount' xs max 0

getUserInputs :: IO [Char]
getUserInputs = do
    n <- read <$> getLine :: IO Int
    replicateM n $ head <$> getLine

main :: IO ()
main = do
    xs <- getUserInputs 
    print $ onesCount xs
dokondr
  • 3,389
  • 12
  • 38
  • 62
  • 1
    I'm not too familiar with Haskell but it seems that the function expects each line to already be expressed as a string of 1s and 0s. The signature of onesCount takes in an array of characters (i.e. a string) and returns a number which is the maximum consecutive "1" characters in the string. I don't see where an integer value would be converted to its bit representation in that code. It doesn't seem to do more than `max(map(len,bits.split("0")))` – Alain T. Apr 09 '20 at 13:16
  • Thanks, will check! – dokondr Apr 09 '20 at 17:55

2 Answers2

1

Assumption

OP wants two's complement binary.

Python's integers already use two's complement, but since they have arbitrary precision, the binary representation of negative numbers would have an infinite string of 1s at the start, much like positive numbers have an infinite string of 0s. Since this obviously can't be shown, it is represented with a minus sign instead. reference

This results in:

>>> bin(-5)
'-0b101'

So to remove the effect of the infinite precision we can show 2's complement to a fixed number of bits. Use 16 here since OP mentions numbers are < 10, 000.

>>> bin(-5 % (1<<16))            # Modulo 2^16
>> bin(-5 & 0b1111111111111111)  # 16-bit mask
'0b1111111111111011'

Example Using 2's Complement

Test Code

result = []
for line in ['+3', '-3', '-25', '+35', '+1000', '-20000', '+10000']:
  n = int(line)
  xs = bin(n & 0b1111111111111011) # number in 16-bit 2's complement
  runs = maxConsecutive(xs)
  print(f"line: {line}, n: {n}, 2's complement: {xs}, max ones run: {runs}")
  result.append(runs)

print(f'Max run is {max(result)}')

Test Output

line: +3, n: 3, 2's complement binary: 0b11, max ones run: 2
line: -3, n: -3, 2's complement binary: 0b1111111111111101, max ones run: 14
line: -25, n: -25, 2's complement binary: 0b1111111111100111, max ones run: 11
line: +35, n: 35, 2's complement binary: 0b100011, max ones run: 2
line: +1000, n: 1000, 2's complement binary: 0b1111101000, max ones run: 5
line: -20000, n: -20000, 2's complement binary: 0b1011000111100000, max ones run: 4
line: +10000, n: 10000, 2's complement binary: 0b10011100010000, max ones run: 3
Max run is 14

Code

def maxConsecutive(input):
    return max(map(len,input[2:].split('0')))  # Skip 0b at beginning of each

def max_len(input_file):
    max_len_ = 0
    with open(input_file) as file:
        first_line = file.readline()
        if not first_line:
            return 0
        k = int(first_line.strip()) # number of tests
        for i in range(k):
            line = file.readline().strip()
            n = int(line)
            xs = bin(n & '0b1111111111111011') # number in 16-bit 2's complement
            n = maxConsecutive(xs)
            if n > max_len_:
                max_len_ = n
    return max_len_

Code Simplification of max_len

max_len could be reduced to:

def max_len(input_file):
  with open(input_file) as file:
    return max(maxConsecutive(bin(int(next(file).strip()), 0b1111111111111011)) for _ in range(int(next(file))))
DarrylG
  • 16,732
  • 2
  • 17
  • 23
  • Thanks! Yet your solution does not take into account Two's complement for negative numbers. Please, see my question update. – dokondr Apr 07 '20 at 11:18
  • @dokondr-yes it does. See my updated answer with example. Works by checking if input[0] is a sign i.e. '-'. If so skips using input[1:]. – DarrylG Apr 07 '20 at 11:35
  • maxConsecutive(+3) should return 2, and maxConsecutive(-3) should return 1 – dokondr Apr 07 '20 at 13:21
  • @dokondr--For line = `'+3'`, n = 3. Then we have xs = "{0:b}".format(n) converting n to a base 2 string. This will result in `11`. So we call `maxConsecutive('11')` to get 2. For `line = `'-3'``, n = -3, xs = -11`. We call `maxConsecutive('-11')` to get 2 also. – DarrylG Apr 07 '20 at 13:39
  • Two's complement is a mathematical operation on binary numbers ...It is used in computing as a method of signed number representation. The two's complement of an N-bit number is defined as its complement with respect to 2^N. For instance, for the three-bit number 010, the two's complement is 110, because 010 + 110 = 1000. The two's complement is calculated by inverting the digits and adding one. _Two's complement is the most common method of representing signed integers on computers. For -3 compliments is 101. So maxConsecutive must return 101 for -3 and compliments for other negative nums_ – dokondr Apr 07 '20 at 14:20
  • Thanks, there is a typo: `xs = bin(n & '0b1111111111111011') # number in 16-bit 2's complement` should be `xs = bin(n & 0b1111111111111011)` Testing it know ... – dokondr Apr 07 '20 at 18:13
  • @dokondr--Thanks, fixed. – DarrylG Apr 07 '20 at 18:49
  • Please see Update 2. – dokondr Apr 08 '20 at 19:49
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/211247/discussion-between-darrylg-and-dokondr). – DarrylG Apr 08 '20 at 19:58
1

For negative numbers, you will either have to decide on a word length (32 bits, 64 bits, ...) or process them as absolute values (i.e. ignoring the sign) or use the minimum number of bits for each value.

An easy way to control the word length is to use format strings. you can obtain the negative bits by adding the value to the power 2 corresponding to the selected word size. This will give you the appropriate bits for positive and for negative numbers.

For example:

n = 123
f"{(1<<32)+n:032b}"[-32:]  --> '00000000000000000000000001111011'

n = -123
f"{(1<<32)+n:032b}"[-32:]  --> '11111111111111111111111110000101'

Processing that to count the longest series of consecutive 1s is just a matter of string manipulation:

If you choose to represent negative numbers using a varying word size you can use one bit more than the minimal representation of the positive number. For example -3 is represented as two bits ('11') when positive so it will need a minimum of 3 bits to be represented as a negative number: '101'

n        = -123
wordSize = len(f"{abs(n):b}")+1
bits     = f"{(1<<wordSize)+n:0{wordSize}b}"[-wordSize:]
maxOnes  = max(map(len,bits.split("0")))

print(maxOnes) # 1   ('10000101')
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • Thanks, it is difficult for me to understand `"{(1< – dokondr Apr 08 '20 at 08:30
  • 1
    `"{0:0{1}b}".format((1< – Alain T. Apr 08 '20 at 12:08
  • Thanks! Unfortunately this solution as well as the other one does not pass all tests. Please see Update 2. – dokondr Apr 08 '20 at 19:49
  • Can you include the test data and expected result in your post, I can't login to the linked site. – Alain T. Apr 09 '20 at 01:34
  • On this site they run your code across a series of tests. All you can see is a list of test numbers passed and the number of test that failed. They don't show the test data, so you can only guess what is wrong and try again. You have 100 attempts to try for each problem. At login link I gave you there is an option to register with Yandex contest. Unfortunately there is no other way to do it. Please also see my Update 3 with solution in Haskell that pass all Yandex tests. – dokondr Apr 09 '20 at 07:25