13

I would like to know if my implementation is efficient. I have tried to find the simplest and low complex solution to that problem using python.

def count_gap(x):
    """
        Perform Find the longest sequence of zeros between ones "gap" in binary representation of an integer

        Parameters
        ----------
        x : int
            input integer value

        Returns
        ----------
        max_gap : int
            the maximum gap length

    """
    try:
        # Convert int to binary
        b = "{0:b}".format(x)
        # Iterate from right to lift 
        # Start detecting gaps after fist "one"
        for i,j in enumerate(b[::-1]):
            if int(j) == 1:
                max_gap = max([len(i) for i in b[::-1][i:].split('1') if i])
                break
    except ValueError:
        print("Oops! no gap found")
        max_gap = 0
    return max_gap

let me know your opinion.

agiledatabase
  • 29
  • 1
  • 4
  • The [codility BinaryGap test](https://app.codility.com/programmers/lessons/1-iterations/binary_gap/) allows for solutions written in 18 different languages: `C, C++, C#, Go, Java 8, Java 11, JavaScript, Kotlin, Lua, Objective-C, Pascal, PHP, Perl, Python, Ruby, Scala, Swift 4, Visual Basic`. So I don't see any reason to restrict this question to Python only. – Henke Jan 29 '22 at 13:41

27 Answers27

65

I do realize that brevity does not mean readability nor efficiency.

However, ability to spell out solution in spoken language and implement it in Python in no time constitutes efficient use of my time.

For binary gap: hey, lets convert int into binary, strip trailing zeros, split at '1' to list, then find longest element in list and get this element lenght.

def binary_gap(N):
    return len(max(format(N, 'b').strip('0').split('1')))  
Aivar Paalberg
  • 4,645
  • 4
  • 16
  • 17
  • 2
    Super nice :thumbsup: – Brian Wylie Jan 12 '19 at 21:04
  • This one liner really very efficient. But I got stuck in one point, what is the functionality of strip? I ran that code without using strip and it had same output. Thank you in advance. – Encipher Mar 22 '19 at 23:25
  • 5
    By definition binary gap is 'zeros between ones', therefore one should not consider trailing zeros as binary gap. Try to run both versions with int 32 (100000 in binary). With strip you get 0 (there is no binary gap) without strip you get 5 (which is incorrect as there is no 5 zeros between ones). – Aivar Paalberg Mar 25 '19 at 07:23
  • If you are looking for performance: bin(), which @Orenico uses in his answers, is quite a bit faster than format(N, 'b'). – quassy Jul 31 '20 at 15:43
  • @quassy - please define 'quite a bit faster'. Casual check with timeit & Python 3.8 shows that format() is little bit faster than bin()[2:]. Same casual check also indicates that one can claim `f"{N:b}"` to be "quite a bit faster". – Aivar Paalberg Aug 01 '20 at 13:57
  • Testing my implementation I see small but probably not significant differences for testing all numbers from 1 to 21474836 (.format() took 26.1s; format() took 25.6s; bin() took 24.2s; also for bigger ranges). This seems to confirm [other observations](https://stackoverflow.com/questions/699866/python-int-to-binary-string#comment29223504_699892). – quassy Aug 05 '20 at 13:25
  • If you want to run [the corresponding Codility test](https://app.codility.com/programmers/lessons/1-iterations/binary_gap/start/), then just rename `binary_gap` as `solution`. This solution scores as [100% correct](https://i.imgur.com/blMWx8I.png) and it passes [15 of 15 tests](https://i.imgur.com/4BPWiea.png). – Henke Jan 29 '22 at 15:41
14

Your implementation converts the integer to a base two string then visits each character in the string. Instead, you could just visit each bit in the integer using << and &. Doing so will avoid visiting each bit twice (first to convert it to a string, then to check if if it's a "1" or not in the resulting string). It will also avoid allocating memory for the string and then for each substring you inspect.

You can inspect each bit of the integer by visiting 1 << 0, 1 << 1, ..., 1 << (x.bit_length).

For example:

def max_gap(x):
    max_gap_length = 0
    current_gap_length = 0
    for i in range(x.bit_length()):
        if x & (1 << i):
            # Set, any gap is over.
            if current_gap_length > max_gap_length:
                max_gap_length = current_gap_length
            current_gap_length = 0
         else:
            # Not set, the gap widens.
            current_gap_length += 1
    # Gap might end at the end.
    if current_gap_length > max_gap_length:
        max_gap_length = current_gap_length
    return max_gap_length
Jean-Paul Calderone
  • 47,755
  • 6
  • 94
  • 122
  • 1
    I'd suggest using the `int.bit_length` method instead of the `ceil(log2(...))` computation, to avoid corner-case errors due to rounding. – Mark Dickinson Feb 23 '18 at 16:00
  • You have said Your implementation converts the integer to a base two string then visits each character in the string but that is not completely correct as I break after detecting one then I split. Could you plz highlight why your implementation should be better in terms of time and memory complexity, please. –  Feb 23 '18 at 16:16
  • You still have to visit each character. How else can `split` find the right place to split? It visits each character until it finds the value you supplied. – Jean-Paul Calderone Feb 23 '18 at 17:42
  • 1
    Hi Jean, Your code is much slower than the one I provided. I will add the time complexity in the answer (as running time test code). –  Feb 26 '18 at 08:45
  • Note that "complexity" is not the same as "wallclock runtime". – Jean-Paul Calderone Feb 26 '18 at 14:14
  • Complexity wise, I think I've convinced myself that your solution is O(N) (so is mine) and I doubt it's possible to do better than that. So all that's left is constant factors and in Python those are often minimized by making sure you use built-in functionality (since the C implementation is typically much faster than anything you can do with Python code). On PyPy the story may be different. – Jean-Paul Calderone Feb 26 '18 at 15:17
  • 1
    Thanks, Jean. In the beginning, i was afraid to publish my codes in StackOverflow (negative comments), but you really encouraged me to keep on going (publish and optimize). Good luck –  Feb 27 '18 at 10:11
  • @Jean-PaulCalderone: It looks to me like your algorithm is actually O((log x)^2) where the OP's is O(log x), which leads to dramatic differences in runtime as the OP's test case has log(x) around half a million. The problem appears to be the calculation of `1 << i`, which requires zeroing i/8 bytes of memory and consequently runs in O(i) time. The `.format` OP uses, on the other hand, presumably just loops through the bytes of x, converts each to an 8-byte string, and writes the result of the conversion into a buffer, which is an O(log x) operation that's done once. – Daniel McLaury Apr 03 '18 at 18:14
  • Nice catch. I thought about taking the `1 << i` out and replacing it with a loop variable and `loop_var << 1`. Even with this change, my version is still _slower_ than the OPs (except for very very small integers). Often, complexity doesn't matter in Python (even if you can figure out - what about the `range`, what about the `b[::-1]`, etc) because finding some way to push a "more" complex operation off into C (`format`) still beats a "less" complex operation in byte code. My version goes through a whole byte code interpretation loop for each bit! Seriously big `k` being applied there. – Jean-Paul Calderone Apr 04 '18 at 01:31
7
def max_gap(N):
    xs = bin(N)[2:].strip('0').split('1')
    return max([len(x) for x in xs])

Explanation:

  1. Both leading and trailing zeros are redundant with binary gap finding as they are not bounded by two 1's (left and right respectively)
  2. So step 1 striping zeros left and right
  3. Then splitting by 1's yields all sequences of 0'z
  4. Solution: The maximum length of 0's sub-strings
Orenico
  • 138
  • 1
  • 6
  • Welcome to Stack Overflow! Thank you for the code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its [long-term value](https://meta.stackexchange.com/q/114762/206345) by describing why this is a good solution to the problem, and would make it more useful to future readers with other similar questions. Please edit your answer to add some explanation, including the assumptions you've made. – sepehr Oct 24 '18 at 15:08
2

As suggested in the comments, itertools.groupby is efficient in grouping elements of an iterable like a string. You could approach it like this:

from itertools import groupby

def count_gap(x):
    b = "{0:b}".format(x)
    return max(len(list(v)) for k, v in groupby(b.strip("0")) if k == "0")

number = 123456
print(count_gap(number))

First we strip all zeroes from the ends, because a gap has to have on both ends a one. Then itertools.groupby groups ones and zeros and we extract the key (i.e. "0" or "1") together with a group (i.e. if we convert it into a list, it looks like "0000" or "11"). Next we collect the length for every group v, if k is zero. And from this we determine the largest number, i.e. the longest gap of zeroes amidst the ones.

Mr. T
  • 11,960
  • 10
  • 32
  • 54
2

I think the accepted answer dose not work when the input number is 32 (100000). Here is my solution:

def solution(N):
    res = 0
    st = -1
    for i in range(N.bit_length()):
        if N & (1 << i):
            if st != -1:
                res = max(res, i - st - 1)
            st = i

    return res
Jingda Mai
  • 51
  • 4
2
def solution(N):
    # write your code in Python 3.6
    count = 0
    gap_list=[]
    bin_var = format(N,"b")
    for bit in bin_var:
        if (bit =="1"):
            gap_list.append(count)
            count =0
        else:
            count +=1
    return max(gap_list)
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Vasanth
  • 31
  • 3
2

Here is my solution:

def solution(N):
    num = binary = format(N, "06b")
    char = str(num)
    find=False
    result, conteur=0, 0

    for c in char:
        if c=='1' and not find:
            find = True
            
        if find and c=='0':
            conteur+=1

        if c=='1':
            if result<conteur:
                result=conteur
            conteur=0

    return result
fcdt
  • 2,371
  • 5
  • 14
  • 26
1

this also works:

def binary_gap(n):
    max_gap = 0
    current_gap = 0

    # Skip the tailing zero(s)
    while n > 0 and n % 2 == 0:
        n //= 2

    while n > 0:
        remainder = n % 2
        if remainder == 0:
            # Inside a gap
            current_gap += 1
        else:
            # Gap ends
            if current_gap != 0:
                max_gap = max(current_gap, max_gap)
                current_gap = 0
        n //= 2

    return max_gap
1

Old question, but I would solve it using generators.

from itertools import dropwhile

# a generator that returns binary 
# representation of the input
def getBinary(N):
    while N:
        yield N%2
        N //= 2

def longestGap(N):
    longestGap = 0
    currentGap = 0

    # we want to discard the initial 0's in the binary
    # representation of the input
    for i in dropwhile(lambda x: not x, getBinary(N)):
        if i:
            # a new gap is found. Compare to the maximum
            longestGap = max(currentGap, longestGap)
            currentGap = 0
        else:
            # extend the previous gap or start a new one
            currentGap+=1

    return longestGap
SMir
  • 650
  • 1
  • 7
  • 19
1

Can be done using strip() and split() function : Steps:

  1. Convert to binary (Remove first two characters )
  2. Convert int to string
  3. Remove the trailing and starting 0 and 1 respectively
  4. Split with 1 from the string to find the subsequences of strings
  5. Find the length of the longest substring

Second strip('1') is not mandatory but it will decrease the cases to be checked and will improve the time complexity Worst case T

def solution(N):
    return len(max(bin(N)[2:].strip('0').strip('1').split('1')))

Harsh Shah
  • 147
  • 1
  • 3
1
def solution(N: int) -> int:
    binary = bin(N)[2:]
    longest_gap = 0
    gap = 0
    for char in binary:
        if char == '0':
            gap += 1
        else:
            if gap > longest_gap:
                longest_gap = gap
            gap = 0
    return longest_gap
m.joy
  • 11
  • 2
1
def solution(N):
# write your code in Python 3.6

    iterable_N = "{0:b}".format(N)
    max_gap = 0
    gap_positions = []
    for index, item in enumerate(iterable_N):
        if item == "1":
            if len(gap_positions) > 0:
               if (index - gap_positions[-1]) > max_gap:
                    max_gap = index - gap_positions[-1]
            gap_positions.append(index)
    max_gap -= 1 
    return max_gap if max_gap >= 0 else 0
Sven Eberth
  • 3,057
  • 12
  • 24
  • 29
Harrison
  • 188
  • 2
  • 9
1

Solution using bit shift operator (100%). Basically the complexity is O(N).

def solution(N):
    # write your code in Python 3.6
    meet_one = False
    count = 0
    keep = []
    while N:
        if meet_one and N & 1 == 0:
            count+=1
        
        if  N & 1:
            meet_one = True
            keep.append(count)
            count = 0
        N >>=1

    return max(keep)
hungneox
  • 9,333
  • 12
  • 49
  • 66
0

this also works:

def solution(N):
    bin_num = str(bin(N)[2:])
    list1 = bin_num.split('1')
    max_gap =0
    if bin_num.endswith('0'):
        len1 = len(list1) - 1
    else:
        len1 = len(list1)
    if len1 != 0:
        for i in range(len1):
            if max_gap < len(list1[i]):
                max_gap = len(list1[i])
    return max_gap
kritika
  • 21
  • 1
0
def solution(number):

    bits = [int(digit) for digit in bin(number)[2:]]
    occurences = [i for i, bit in enumerate(bits) if(bit==1)]
    res = [occurences[counter+1]-a-1 for counter, a in enumerate(occurences) if(counter+1 < len(occurences))]

    if(not res):
        print("Gap: 0")
    else:
        print("Gap: ", max(res))

number = 1042
solution(number)
PythonNoob
  • 914
  • 1
  • 7
  • 15
0

This works

def solution(number):
    # convert number to binary then strip trailing zeroes
    binary = ("{0:b}".format(number)).strip("0")
    longest_gap = 0
    current_gap = 0
    for c in binary:
        if c is "0":
           current_gap = current_gap + 1
        else:
           current_gap = 0

        if current_gap > longest_gap:
           longest_gap = current_gap 


    return longest_gap
0
def max_gap(N):
    bin = '{0:b}'.format(N)
    binary_gap = []
    bin_list = [bin[i:i+1] for i in range(0, len(bin), 1)] 

    for i in range(len(bin_list)):
        if (bin_list[i] == '1'):
            # print(i)
            # print(bin_list[i])
            # print(binary_gap)
            gap = []
            for j in range(len(bin_list[i+1:])):
                # print(j)
                # print(bin_list[j])
                if(bin_list[i+j+1]=='1'):
                    binary_gap.append(j)
                    # print(j)
                    # print(bin_list[j])
                    # print(binary_gap)
                    break
                elif(bin_list[i+j+1]=='0'):
                    # print(i+j+1)
                    # print(bin_list[j])
                    # print(binary_gap)
                    continue
                else:
                    # print(i+j+1)
                    # print(bin_list[i+j])
                    # print(binary_gap)
                    break
        else:
            # print(i)
            # print(bin_list[i])
            # print(binary_gap)
            binary_gap.append(0)


    return max(binary_gap)
    pass
  • You should [edit](https://stackoverflow.com/posts/57247918/edit) your answer to add a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) demonstrating the problem. – Mebin Joe Sep 13 '19 at 10:11
0
def find(s, ch):
    return [i for i, ltr in enumerate(s) if ltr == ch]

def solution(N):
    get_bin = lambda x: format(x, 'b')
    binary_num = get_bin(N)
    print(binary_num)
    binary_str = str(binary_num)
    list_1s = find(binary_str,'1')
    diffmax = 0
    for i in range(len(list_1s)-1):
        if len(list_1s)<1:
            diffmax = 0
            break
        else:
            diff = list_1s[i+1] - list_1s[i] - 1
            if diff > diffmax:
                diffmax = diff
    return diffmax
    pass
pzaenger
  • 11,381
  • 3
  • 45
  • 46
0

Here's a solution using iterators and generators that will handle edge cases such as the binary gap for the number 32 (100000) being 0 and the binary gap for 0 being 0. It doesn't create a list, instead relying on iterating and processing elements of the bit string one step at a time for a memory efficient solution.

def solution(N):
    def counter(n):
        count = 0
        preceeding_one = False
        for x in reversed(bin(n).lstrip('0b')):
            x = int(x)
            if x == 1:
                count = 0
                preceeding_one = True
                yield count
            if preceeding_one and x == 0:
                count += 1
                yield count
        yield count
    return(max(counter(N)))
SatoriChi
  • 31
  • 2
0

Here is one more that seems to be easy to understand.

def count_gap(x):
     binary_str = list(bin(x)[2:].strip('0'))
     max_gap = 0 
     n = len(binary_str)
     pivot_point = 0

     for i in range(pivot_point, n):
         zeros = 0
         for j in range(i + 1, n):
              if binary_str[j] == '0':
                   zeros += 1 
              else:
                   pivot_point = j
                   break

         max_gap = max(max_gap, zeros)

     return max_gap
govgo
  • 625
  • 6
  • 18
0

This is really old, I know. But here's mine:

def solution(N):
    gap_list = [len(gap) for gap in bin(N)[2:].strip("0").split("1") if gap != ""]
    
    return max(gap_list) if gap_list else 0
Caspian
  • 633
  • 1
  • 10
  • 29
0

Here is another efficient solution. Hope it may helps you. You just need to pass any number in function and it will return longest Binary gap.

def LongestBinaryGap(num):

n = int(num/2)
bin_arr = []

for i in range(0,n):
    if i == 0:
        n1 = int(num/2)
        bin_arr.append(num%2)
    else:
        bin_arr.append(n1%2)
        n1 = int(n1/2)

        if n1 == 0:
            break

print(bin_arr)
result = ""
count = 0
count_arr = []

for i in bin_arr:
    if result == "found":
        if i == 0:
            count += 1
        else:
            if count > 0:
                count_arr.append(count)
                count = 0
    if i == 1:
        result = 'found'
    else:
        pass

if len(count_arr) == 0:
    return 0
else:
    return max(count_arr)

print(LongestBinaryGap(1130))  # Here you can pass any number.
Raza ul Mustafa
  • 104
  • 3
  • 8
0

My code in python 3.6 scores 100 Get the binary Number .. Get the positions of 1 get the abs differennce between 1.. sort it

 S = bin(num).replace("0b", "")
          res = [int(x) for x in str(S)]
          print(res)
          if res.count(1) < 2 or res.count(0) < 1:
              print("its has no binary gap")
          else: 
            positionof1 = [i for i,x in enumerate(res) if x==1]
            print(positionof1)
            differnce = [abs(j-i) for i,j in zip(positionof1, positionof1[1:])]
            differnce[:] = [differnce - 1 for differnce in differnce]
            differnce.sort()
            print(differnce[-1])
  • Please provide additional details in your answer. As it's currently written, it's hard to understand your solution. – Community Sep 05 '21 at 17:13
0
def solution(N):
    return len(max(bin(N).strip('0').split('1')[1:]))
0
def solution(N):
    maksimum = 0 
    zeros_list = str(N).split('1')
    if zeros_list[-1] != "" :
        zeros_list.pop()
        
    for item in zeros_list :
        if  len(item) > maksimum :
            maksimum = len(item)
    return(maksimum)
seyyah
  • 1
  • 1
-1
def solution(N):
    bin_num = str(bin(N)[2:])
    bin_num = bin_num.rstrip('0')
    bin_num = bin_num.lstrip('0')
    list1 = bin_num.split('1')
    max_gap = 0

    for i in range(len(list1)):
        if len(list1[i]) > max_gap:
            max_gap = len(list1[i])

    return (max_gap)
Sven Eberth
  • 3,057
  • 12
  • 24
  • 29
  • 1
    This works fine with any "Binary Gap " Lesson 1 codility – Kushal Kadam Oct 09 '18 at 03:48
  • 2
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – Nic3500 Oct 09 '18 at 04:32
  • Your code does not even run, because of the missing indention. Please fix that! – hellow Oct 09 '18 at 06:22
  • @hellow, I had problems while posting . The code works absolutely fine . – Kushal Kadam Oct 10 '18 at 01:14
  • @Nic3500 I acknowledge, this is my first time :) – Kushal Kadam Oct 10 '18 at 01:15
  • 2
    Then please read https://stackoverflow.com/editing-help and https://stackoverflow.com/help/how-to-answer . There is a ton of help available how to format your answer properly, so please don't use that as an excuse ;) (I don't drive a car, before I took some driving lessons. That's (nearly) the same. Read the instructions first and post afterwards) – hellow Oct 10 '18 at 06:00
-1
def solution(N):
    # Convert the number to bin 
    br = bin(N).split('b')[1]
    sunya=[]
    groupvalues=[]
    for i in br:
        count = i
        if int(count) == 1:
            groupvalues.append(len(sunya))
            sunya=[]
        if int(count) == 0:
            sunya.append('count') 

    return max(groupvalues)
Asocia
  • 5,935
  • 2
  • 21
  • 46