0

I am working on Fibonacci series but in bit string which can be represented as:

f(0)=0;
f(1)=1;
f(2)=10;
f(3)=101;
f(4)=10110;
f(5)=10110101;

Secondly, I have a pattern for example '10' and want to count how many times this occurs in particular series, for example, the Fibonacci series for 5 is '101101101' so '10' occur 3 times.

my code is running correctly without error but the problem is that it cannot run for more than the value of n=45 I want to run n=100 can anyone help? I only want to calculate the count of occurrence

n=5
fibonacci_numbers = ['0', '1']
for i in range(1,n):
    fibonacci_numbers.append(fibonacci_numbers[i]+fibonacci_numbers[i-1])
#print(fibonacci_numbers[-1])
print(fibonacci_numbers[-1])
nStr = str (fibonacci_numbers[-1])
pattern = '10'
count = 0
flag = True
start = 0
while flag:
    a = nStr.find(pattern, start)

    if a == -1:
        flag = False
    else:
        count += 1
        start = a + 1
print(count)
Ahsan Ali
  • 1
  • 2
  • 1
    What happens when you try to run it for a higher value? – Samwise Nov 15 '19 at 05:58
  • fibonacci_numbers.append(fibonacci_numbers[i]+fibonacci_numbers[i-1]) MemoryError – Ahsan Ali Nov 15 '19 at 06:02
  • Basically Memory error because I think this program is not memory efficient – Ahsan Ali Nov 15 '19 at 06:03
  • It's hard for me to reverse engineer what it is you're trying to compute because I'm not familiar with this concept of a Fibonacci bit string. Could you explain it in terms of a mathematical formula? There's probably a way to calculate it that doesn't require using a terabyte of memory. – Samwise Nov 15 '19 at 06:05
  • It have same formula f(n)=f(n-1)+f(n-2) but the + sign do not sum 2 number but it concatenate them for example f(2)=1+0 so answer is 10 similarly f(3)=10+1 so answer is 101 – Ahsan Ali Nov 15 '19 at 06:14
  • f(4)=101 + 10 so 10110 if you see characters are increasing like Fibonacci numbers 2,3,5 and so on, So when you reach to 100 there are 218922995834555169026 characters – Ahsan Ali Nov 15 '19 at 06:21
  • Now problem is to store these high number of characters to find the count of some pattern – Ahsan Ali Nov 15 '19 at 06:23
  • Do you want to count the occurrences of *any* given pattern (**hard**) or just `10` (far easier)? – meowgoesthedog Nov 15 '19 at 10:04
  • no any pattern i know how to calculate pattern of 10 it can be done by Fibonacci sequence – Ahsan Ali Nov 15 '19 at 10:24

3 Answers3

1

This is a fun one! The trick is that you don't actually need that giant bit string, just the number of 10s it contains and the edges. This solution runs in O(n) time and O(1) space.

from typing import NamedTuple

class FibString(NamedTuple):
    """First digit, last digit, and the number of 10s in between."""
    first: int
    tens: int
    last: int

def count_fib_string_tens(n: int) -> int:
    """Count the number of 10s in a n-'Fibonacci bitstring'."""

    def combine(b: FibString, a: FibString) -> FibString:
        """Combine two FibStrings."""
        tens = b.tens + a.tens
        # mind the edges!
        if b.last == 1 and a.first == 0:
            tens += 1
        return FibString(b.first, tens, a.last)

    # First two values are 0 and 1 (tens=0 for both)
    a, b = FibString(0, 0, 0), FibString(1, 0, 1)

    for _ in range(1, n):
        a, b = b, combine(b, a)

    return b.tens  # tada!

I tested this against your original implementation and sure enough it produces the same answers for all values that the original function is able to calculate (but it's about eight orders of magnitude faster by the time you get up to n=40). The answer for n=100 is 218922995834555169026 and it took 0.1ms to calculate using this method.

Samwise
  • 68,105
  • 3
  • 30
  • 44
  • what should i give input in main – Ahsan Ali Nov 15 '19 at 10:19
  • I think this is over-engineered for the sole purpose of counting `10`s. Note that since strings for `n > 0` *always* start with 1, concatenating two strings will *never* create additional occurrences of `10`. So the number of `10`'s at `n` is just `Fib(n-1)`. This agrees with the answer for `n = 100`, as `218922995834555169026 = Fib(99)`. – meowgoesthedog Nov 15 '19 at 10:23
  • true! although if you wanted to count any other arbitrary pattern it'd be pretty easy with this approach as a starting point (you might have to vary the amount of "edge" you track, as a function of the pattern length you're matching). – Samwise Nov 15 '19 at 14:59
  • can anyone tell how to get output of this program as it is printing nothing by just copy pasting the code – Ahsan Ali Nov 16 '19 at 06:43
  • `print(count_fib_string_tens(100))` – Samwise Nov 16 '19 at 07:06
  • so i cannot count any other pattern such as 101 by this program as it takes only one input – Ahsan Ali Nov 16 '19 at 09:08
  • That wasn't part of your original question, but I explained the approach I used and it would be *very* easy to adapt this approach to counting other patterns. :) Just make `first` and `last` strings of length 2 if you want to be able to count a pattern of length 3, and adapt the `combine` function to be able to correctly count new instances of the pattern that happen between combined strings. I actually thought about writing this as a function that would accept an arbitrary pattern, but like @meowgoesthedog suggested, going to that extent felt like over-engineering. – Samwise Nov 16 '19 at 16:02
0

The nice thing about the Fibonacci sequence that will solve your issue is that you only need the last two values of the sequence. 10110 is made by combining 101 and 10. After that 10 is no longer needed. So instead of appending, you can just keep the two values. Here is what I've done:

n=45
fibonacci_numbers = ['0', '1']
for i in range(1,n):
    temp = fibonacci_numbers[1]
    fibonacci_numbers[1] = fibonacci_numbers[1] + fibonacci_numbers[0]
    fibonacci_numbers[0] = temp

Note that it still uses a decent amount of memory, but it didn't give me a memory error (it does take a bit of time to run though).

I also wasn't able to print the full string as I got an OSError [Errno 5] Input/Output error but it can still count and print that output.


For larger numbers, storing as a string is going to quickly cause a memory issue. In that case, I'd suggest doing the fibonacci sequence with plain integers and then converting to bits. See here for tips on binary conversion.

While the regular fibonacci sequence doesn't work in a direct sense, consider that 10 is 2 and 101 is 5. 5+2 doesn't work - you want 10110 or an or operation 10100 | 10 yielding 22; so if you shift one by the length of the other, you can get the result. See for example

x = 5
y = 2
(x << 2) | y
>> 22

Shifting x by the number of bits representing y and then doing a bitwise or with | solves the issue. Python summarizes these bitwise operations well here. All that's left for you to do is determine how many bits to shift and implement this into your for loop!


For really large n you will still have a memory issue shown in the plot: 'memory usage up to n=48

ufoxDan
  • 609
  • 4
  • 13
  • it fails at n=100 i want to run till n=100 – Ahsan Ali Nov 15 '19 at 06:29
  • it ran till n=48 – Ahsan Ali Nov 15 '19 at 06:32
  • I see you edited your question with that now. See my edited answer, only thing I can think of in that case is to use ints and then convert to a binary string after the fact. – ufoxDan Nov 15 '19 at 06:33
  • You cannot go with simple Fibonacci sequence because 10 =2 ,101=5, 10110=22 , 10110101 =181 – Ahsan Ali Nov 15 '19 at 06:38
  • Fair enough. Since strings take a byte per character (I think) there is no way you will be able to do this with an array of strings like that. See my new edit which uses ints with bitwise operators. – ufoxDan Nov 15 '19 at 06:51
  • I'm reluctant to just give you full code because this seems like a homework/learning exercise. Consider what we did to combine `101` and `10`. Now we need to combine `10110` and `101`. How many bits do you need to shift `10110` by? How can you determine this for any int? Hint: logarithm base with base 2 – ufoxDan Nov 15 '19 at 08:04
  • I was not asking for whole code actually i mentioned was unable to understand where to put print statement, Secondly i got your explanation how to get 181 x = 22 y = 5 print ((x << 3) | y) – Ahsan Ali Nov 15 '19 at 09:40
  • i will make whole program and let you know ,Thanks ! – Ahsan Ali Nov 15 '19 at 09:41
  • i have made program according to your logic posted below it return 10110101011110101 on 7 but it should return 101101011011010110101 – Ahsan Ali Nov 15 '19 at 10:17
  • so, there is no need of logarithm shifting is according to Fibonacci series but still a memory error see program below – Ahsan Ali Nov 16 '19 at 08:57
0

Finally i got the answer but can someone explain it briefly why it is working

def count(p, n):
    count = 0
    i = n.find(p)
    while i != -1:
        n = n[i + 1:]
        i = n.find(p)
        count += 1
    return count


def occurence(p, n):
    a1 = "1"
    a0 = "0"
    lp = len(p)
    i = 1
    if n <= 5:
        return count(p, atring(n))
    while lp > len(a1):
        temp = a1
        a1 += a0
        a0 = temp
        i += 1
        if i >= n:
            return count(p, a1)
    fn = a1[:lp - 1]
    if -lp + 1 < 0:
        ln = a1[-lp + 1:]
    else:
        ln = ""
    countn = count(p, a1)
    a1 = a1 + a0
    i += 1
    if -lp + 1 < 0:
        lnp1 = a1[-lp + 1:]
    else:
        lnp1 = ""
    k = 0
    countn1 = count(p, a1)
    for j in range(i + 1, n + 1):
        temp = countn1
        countn1 += countn
        countn = temp
        if k % 2 == 0:
            string = lnp1 + fn
        else:
            string = ln + fn
        k += 1
        countn1 += count(p, string)
    return countn1


def atring(n):
    a0 = "0"
    a1 = "1"
    if n == 0 or n == 1:
        return str(n)
    for i in range(2, n + 1):
        temp = a1
        a1 += a0
        a0 = temp
    return a1


def fn():


    a = 100
    p = '10'
    print( occurence(p, a))




if __name__ == "__main__":
    fn()
Ahsan Ali
  • 1
  • 2