0

this is my code so the logic basically is supposed to be that the user inputs an integer we check with the first bit of integer whether it is 1 or not if 1 the var count goes up if not we skip it we do this till the longest set of 1's is reached which is set in var temp when I run it with any number it outputs 1

n = int(input('enter your no'))
count = 0
temp = 0
while n>0:
    if n&1 == 0:
        count+=1
    if count>=temp:
        temp = count
        count = 0
    else:
        count = 0
    n = n>>1
print(count, temp)
Shlok
  • 9
  • The task isn't clear, should include an example. – Kelly Bundy Mar 02 '23 at 13:13
  • There are ways to efficiently get runs of a bit, you don't have to test each bit individually. – Mark Ransom Mar 02 '23 at 13:21
  • @MarkRansom I can imagine that. Would like to see what you have in mind. – Kelly Bundy Mar 02 '23 at 13:23
  • @KellyBundy it's an old hack, if you subtract 1 from a string of 0's they all flip to 1. I don't have time to flesh it out now, but maybe later. – Mark Ransom Mar 02 '23 at 14:19
  • @MarkRansom Ok. I did it similarly, *adding* 1 to turn a streak of 1s into 0s. Plus some other things. It worked well, but then I realized that for large numbers, turning it into a binary *string* is more efficient anyway... – Kelly Bundy Mar 02 '23 at 14:24
  • @MarkRansom Would yours be similar to my [g function here](https://ato.pxeger.com/run?1=XZFNbsQgDIXVLafwagJqEjG7aqSZq0RJBwhSYhBx1D_1JN3Mpj1UT1N-UqnpDuP3PvDzx5d_odHh7fa5km4evu_Izt4FgtDj1c2MXZUGzVGcGEBQtAaEuX_mc-_5pLAGXb3haXiv2sVPlnglKyFEsZnNFvWdQ7XAGWQsn0Y7KcDUAaDQ28mi6V5VcEnBEQ7QoGgHS118wdDIBTRwzHKEy-X8z5Qbid8NSrugIgSz-9GtSFxsxkO8hvuN8-dLZZpS1jtOs-eIXQJZz1gUQgcWU15G8aPME2PklgBboyidImeJXSkTxQcbgSnVOockSvjbDn538QM)? Or somehow better? – Kelly Bundy Mar 02 '23 at 14:38
  • @MarkRansom [Another version](https://ato.pxeger.com/run?1=bVFLTsMwEBXbnGJWxBZJ5OxQpfYoWKlqJ5aSseVMVD7iJGy6gQNwHE6DPwER1J3Hb968mffePtwTDRYvl_eFdH3_dfNpJmc9ge_wZKeiOCkNmiHfFQBe0eIRpu6RTZ1jo8IKdPmCu-Nr2cxuNMRKUXLOM61faaFfWlQz7EGE8jyYUQFGBIB8Z0aDvXxW3sYOhnALNfLmaEgGhZ4GxqGGNrUjHA77f6QEnE04YyH5C62CcRrDu5Zv1VY0iD1cp271E_nPGdmBXFbbqfzanhHY-Jd_Cm09SDAY3e4Va0XyC4NCtr_pFcVX2GUOqBBxuvMGicVMqmQxz9GtCf4k-Q0). – Kelly Bundy Mar 02 '23 at 14:51
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Blue Robin Mar 02 '23 at 15:39
  • 1
    @KellyBundy I forgot that the binary carry trick can be used for both zeros and ones. I've posted my answer. Didn't look at your answers yet. – Mark Ransom Mar 03 '23 at 13:44

3 Answers3

2

You can take advantage of binary carry to get more than one bit at a time. This function breaks the number apart into alternating runs of 0s and 1s, and keeps track of the longest run of 1s.

def longest_one_run(n):
    if n < 0:
        # Negative numbers in Python have an infinite number
        # of ones on the left side.  Fix it by capping the
        # number of bits to a reasonable number, say 32.
        n &= (1 << 32) - 1
    longest = 0
    while n > 0:
        if n&1:
            mask = n + 1
        else:
            mask = n - 1
        run = (n ^ mask) >> 1
        run_count = run.bit_length()
        if n&1 and run_count > longest:
            longest = run_count
        n >>= run_count
    return longest

I used an answer from Fast way of counting non-zero bits in positive integer for CountBits to count the number of bits in a run, any of the answers there should suffice. If I had Python 3.10 I would have used .bit_count(). And based on a comment I was able to replace both with .bit_length() which works even better.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    `run` is all 1s, so you can just use `run_count = run.bit_length()`. – Kelly Bundy Mar 03 '23 at 13:52
  • @KellyBundy great idea, I forgot all about that function! And it avoids a problem that occurs if you use a bit count function with a maximum size like I did. – Mark Ransom Mar 03 '23 at 14:37
  • Hmm, what problem? – Kelly Bundy Mar 03 '23 at 15:05
  • @KellyBundy the function I used wouldn't give a correct result for anything over 64 bits, so the maximum length it could return was 64. Thanks again for your suggestion. – Mark Ransom Mar 03 '23 at 15:21
  • Ah, you meant that particular `CountBits` function in one of the answers. I posted my string solution now, along with benchmarks. I used random numbers, to reflect the average case. Which means runs are usually short, and the simple solution seems faster then. – Kelly Bundy Mar 03 '23 at 15:41
  • @KellyBundy yes I wanted to run benchmarks too. You have to be careful, because first you want to use the same numbers for each test and second generating random numbers takes a significant amount of time and could affect your results. – Mark Ransom Mar 03 '23 at 15:57
  • Well, of course I use the same inputs for all solutions, and don't include the input generation in the time. That's standard, don't need to be careful for that :-) – Kelly Bundy Mar 03 '23 at 16:05
  • Wonder what's the time will be if take this ```max(len(list(g)) for k, g in itertools.groupby(s) if k == '1')``` – Daniel Hao Mar 03 '23 at 20:47
2

More solutions and benchmarks:

random 20-bit numbers:
 0.57 ± 0.25 μs  Kelly3
 1.51 ± 0.48 μs  Kelly
 1.80 ± 0.61 μs  Kelly2
 3.99 ± 0.96 μs  Mark
 4.75 ± 0.54 μs  Yash

random 1000-bit numbers:
 0.00 ± 0.00 ms  Kelly3
 0.03 ± 0.00 ms  Kelly
 0.03 ± 0.00 ms  Kelly2
 0.22 ± 0.01 ms  Mark
 0.27 ± 0.01 ms  Yash

random 10000-bit numbers:
 0.01 ± 0.00 ms  Kelly3
 0.34 ± 0.00 ms  Kelly2
 0.38 ± 0.00 ms  Kelly
 5.29 ± 0.06 ms  Mark
 5.42 ± 0.09 ms  Yash

random 30000-bit numbers:
 0.03 ± 0.00 ms  Kelly3
 1.08 ± 0.01 ms  Kelly2
 1.30 ± 0.11 ms  Kelly
34.77 ± 1.00 ms  Yash
36.38 ± 1.05 ms  Mark

Code (Attempt This Online!):

def Kelly(n):
    return max(map(len, f'{n:b}'.split('0')))

def Kelly2(n):
    return max(map(len, filter(None, f'{n:b}'.split('0'))), default=0)

def Kelly3(n):
    ctr = 0
    while n:
        n &= n >> 1
        ctr += 1
    return ctr

def Mark(n):
    longest = 0
    while n > 0:
        if n&1:
            mask = n + 1
        else:
            mask = n - 1
        run = (n ^ mask) >> 1
        run_count = run.bit_length()
        if n&1 and run_count > longest:
            longest = run_count
        n >>= run_count
    return longest

def Yash(n):
    count = 0
    temp = 0
    while n>0:
        if n&1 == 1:
            count+=1
        else:
            temp = max(temp, count)
            count = 0
        n = n>>1
    temp = max(temp, count)
    return temp

funcs = Kelly, Kelly2, Kelly3, Mark, Yash

import random
from timeit import repeat
from statistics import mean, stdev

# Correctness
for n in [*range(10000), *(random.getrandbits(10) for _ in range(1000))]:
  expect = funcs[0](n)
  for f in funcs[1:]:
    if f(n) != expect:
      print('fail:', f.__name__)
    
def test(bits, number, unit, scale):
  print(f'random {bits}-bit numbers:')

  times = {f: [] for f in funcs}
  def stats(f):
    ts = [t * scale for t in times[f][5:]]
    return f'{mean(ts):5.2f} ± {stdev(ts):4.2f} {unit} '

  for _ in range(10):
    n = random.getrandbits(bits)
    for f in funcs:
      t = min(repeat(lambda: f(n), number=number)) / number
      times[f].append(t)

  for f in sorted(funcs, key=stats):
    print(stats(f), f.__name__)
  print()

test(20, 1000, 'μs', 1e6)
test(1000, 10, 'ms', 1e3)
test(10000, 1, 'ms', 1e3)
test(30000, 1, 'ms', 1e3)
Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
  • I knew the string solution was going to be a lot simpler, didn't expect it to be so much faster though. Well done! – Mark Ransom Mar 03 '23 at 16:05
  • @MarkRansom Ha, I was actually disappointed that it wasn't *more* faster for large numbers. I mean, it's linear time vs your quadratic time... – Kelly Bundy Mar 03 '23 at 17:10
  • @MarkRansom Check out `Kelly3` :-). It still has quadratic worst case, though. Will now try to make it linearithmic... – Kelly Bundy Mar 03 '23 at 17:51
  • I didn't think conversion to string was linear, unless binary is a special case. – Mark Ransom Mar 03 '23 at 17:53
  • @MarkRansom Bases that are powers of 2 all take linear time. I also tried to think of something nice using `hex(n)` or even `n.to_bytes(...)`, but it's tricky. Then I thought of doing bit fiddling tricks like `Kelly3` instead. – Kelly Bundy Mar 03 '23 at 17:58
  • Kelly3 is brilliant outside-the-box thinking, took me a minute to understand why it works. Congratulations. – Mark Ransom Mar 03 '23 at 18:01
  • @MarkRansom I decided to share the linearithmic one as a separate [Q&A](https://stackoverflow.com/q/75631407/12671057) focused on speed for very large numbers. – Kelly Bundy Mar 03 '23 at 20:01
0

Some bugs in your code.

(1) You wanted to find longest consecutive 1's this condition if n&1 == 0 should be if n&1 == 1.

(2) Your second if always satisfied every time when count>=temp (i.e when count>=0) and it is always true and you are setting to count=0everytime.

Code:

n = int(input('enter your no: ')) #7
count = 0
temp = 0
while n>0:
    if n&1 == 1:
        count+=1
    else:
        temp = max(temp, count)
        count = 0
    n = n>>1
temp = max(temp, count)
print(temp) #3
Yash Mehta
  • 2,025
  • 3
  • 9
  • 20