How to calculate the time complexity for nested for loops in the following example? - python-3.x

So in the following code, I am trying I am passing a (huge)number-string to the function where I have to find the maximum product of consecutive m digits
So, first, I am looping through let's say n-string and then the inner loop looping through m numbers.
So the inner loop is affected by the if-statement which makes a jump of m indexes if the next number is 0.
EDIT : 1
Actual Problem Question:
The four adjacent digits in the 1000-digit number that have the greatest product are 9 × 9 × 8 × 9 = 5832.
731671765313306249192251....(1000digits)
Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product?
Example:
m = 12 number = "1234567891120123456704832...(1000 digits)"
So in 1st iteration function will calculate the product of 1st 12 digits(i.e. from index-11 to index-0 - "1234567891120123456704832..."
Now, in 2nd iteration when it checks the value at index-12 which is 0 then index will jump to index-13. This way the loop will skip 11 iterations.
For the 3rd Iteration, the inner loop will execute for 4 iterations until it finds 0 ("0123456704832...".
def LargestProductInSeries_1(number,m):
max = -1
product = 1
index = 0
x = 0
while index < len(number)-(m-1):
for j in range(index+(m-1), index-1, -1):
num = int(number[j])
if(not num):
index = j
break
product = product * int(number[j])
max = product if max < product else max
product = 1
index += 1
return max
So according to me, the Worst Case Time Complexity would be O(n*m)
I think the Best Time would be O(n/m) if only once the inner loop is completely iterated or every mth digit is 0 which will make the outer loop execute but the index will jump to every mth digit.
Is my analysis correct?
What will be the Average Time for this case?
Will it be O(n*(log m)). Can anyone explain how? Or how to find Complexity in such cases?

Related

Given a binary string "10110". Find the count of all the substring with number of set bit count >= n

We could solve this question in brute force, taking all possible substrings and checking if the set bit count is greater n.
I was asked to solve this in o(n). I could not find any answer which could achieve this in o(n).
Is it possible to get all possible substrings of binary string in 0(n)?
Answer changed (noticed >= in problem statement).
Make two indices - left and right.
We want to account substrings starting from position left containing at least k ones.
At first move right until bit count reaches k.
Now we have some "good" substrings starting at left and ending in any position after right, so we can add len(s) - right + 1 to result.
Increment left by 1 until the next one.
Repeat moving right and so on. Algorithm is linear.
Python example:
s = '0010110010'
#s = '110010110010'
k = 2
left = 0
right = 0
res = 0
cnt = 0
while right < len(s):
while (right < len(s)) and (cnt < k):
if s[right] == "1":
cnt += 1
right +=1
while (left <= right) and (cnt >= k):
addend = len(s) + 1 - right
res += addend
print(left, right, addend, res) #intermediate debug output
if s[left] == "1":
cnt -= 1
left +=1
print(res)
0 5 6 6
1 5 6 12
2 5 6 18
3 6 5 23
4 6 5 28
5 9 2 30
30
A useful approach is to ask yourself how many substrings have less than n bits set.
If you can answer this question, then the answer to the original question is right around the corner.
Why is the modified question easier to grasp? Because when you have a substring, say S, with exactly n bits set, then any substring that contains S will have at least n bits set, so you don't need to examine any of those.
So let's say you have a substring. If it has less than n bits set, you can grow it to accommodate more bits. If it has n or more bits set, it cannot grow, you must shrink it.
Suppose you start from the leftmost empty substring, start index 0, end index 0, length 0. (Of course it's a half-open interval). It has no bits set, so you can grow it. The only direction it can grow is to the right, by increasing its end index. It grows and grows and grows until it eats n 1-bits; now it must shrink. How should it shrink? Obviously shrinking it in the opposite direction (decreasing its end index) would accomplish nothing. You would arrive at a substring you have just examined! So you should shrink it from the left, by increasing its start index. So it shrinks and shrinks and shrinks until it excretes a 1-bit from its rear end. Now it has n-1 1-bits, and it can grow again.
It is not difficult to show that you would enumerate all strings with less than n bits set this way.
let N = count of '1'
And let M = count of '0'
int sum = 0 ;
for( int i = n ; i <= N; i++ ) sum += C(N,i) ;
sum *= 1<<M ;
sum is your answer.

I want to improve speed of my algorithm with multiple rows input. Python. Find average of consequitive elements in list

I need to find average of consecutive elements from list.
At first I am given lenght of list,
then list with numbers,
then am given how many test i need to perform(several rows with inputs),
then I am given several inputs to perform tests(and need to print as many rows with results)
every row for test consist of start and end element in list.
My algorithm:
nu = int(input()) # At first I am given lenght of list
numbers = input().split() # then list with numbers
num = input() # number of rows with inputs
k =[float(i) for i in numbers] # given that numbers in list are of float type
i= 0
while i < int(num):
a,b = input().split() # start and end element in list
i += 1
print(round(sum(k[int(a):(int(b)+1)])/(-int(a)+int(b)+1),6)) # round up to 6 decimals
But it's not fast enough.I was told it;s better to get rid of "while" but I don't know how. Appreciate any help.
Example:
Input:
8 - len(list)
79.02 36.68 79.83 76.00 95.48 48.84 49.95 91.91 - list
10 - number of test
0 0 - a1,b1
0 1
0 2
0 3
0 4
0 5
0 6
0 7
1 7
2 7
Output:
79.020000
57.850000
65.176667
67.882500
73.402000
69.308333
66.542857
69.713750
68.384286
73.668333
i= 0
while i < int(num):
a,b = input().split() # start and end element in list
i += 1
Replace your while-loop with a for loop. Also you could get rid of multiple int calls in the print statement:
for _ in range(int(num)):
a, b = [int(j) for j in input().split()]
You didn't spell out the constraints, but I am guessing that the ranges to be averaged could be quite large. Computing sum(k[int(a):(int(b)+1)]) may take a while.
However, if you precompute partial sums of the input list, each query can be answered in a constant time (sum of numbers in the range is a difference of corresponding partial sums).

I want to remove rows where a specific value doesn't increase. Is there a faster/more elegant way?

I have a dataframe with 30 columns, 1.000.000 rows and about 150 MB size. One column is categorical with 7 different elements and another column (Depth) contains mostly increasing numbers. The graph for each of the elements looks more or less like this.
I tried to save the column Depth as series and iterate through it while dropping rows that won't match the criteria. This was reeeeeaaaally slow.
Afterwards I added a boolean column to the dataframe which indicates if it will be dropped or not, so I could drop the rows in the end in a single step. Still slow. My last try (the code to it is in this post) was to create a boolean list to save the fact if it passes the criteria there. Still really slow (about 5 hours).
dropList = [True]*len(df.index)
for element in elements:
currentMax = 0
minIdx = df.loc[df['Element']==element]['Depth'].index.min()
maxIdx = df.loc[df['Element']==element]['Depth'].index.max()
for x in range(minIdx,maxIdx):
if df.loc[df['Element']==element]['Depth'][x] < currentMax:
dropList[x]=False
else:
currentMax = df.loc[df['Element']==element]['Depth'][x]
df: The main dataframe
elements: a list with the 7 different elements (same as in the categorical column in df)
All rows in an element, where the value Depth isn't bigger than all previous ones should be dropped. With the next element it should start with 0 again.
Example:
Input: 'Depth' = [0 1 2 3 4 2 3 5 6]
'AnyOtherColumn' = [a b c d e f g h i]
Output: 'Depth' [0 1 2 3 4 5 6]
'AnyOtherColumn' = [a b c d e h i]
This should apply to whole rows in the dataframe of course.
Is there a way to get this faster?
EDIT:
The whole rows of the input dataframe should stay as they are. Just the ones where the 'Depth' does not increase should be dropped.
EDIT2:
The remaining rows should stay in their initial order.
How about you take a 2-step approach. First you use a fast sorting algorithm (for example Quicksort) and next you get rid of all the duplicates?
Okay, I found a way thats faster. Here is the code:
dropList = [True]*len(df.index)
for element in elements:
currentMax = 0
minIdx = df.loc[df['Element']==element]['Tiefe'].index.min()
# maxIdx = df.loc[df['Element']==element]['Tiefe'].index.max()
elementList = df.loc[df['Element']==element]['Tiefe'].to_list()
for x in tqdm(range(len(elementList))):
if elementList[x] < currentMax:
dropList[x+minIdx]=False
else:
currentMax = elementList[x]
I took the column and saved it as a list. To preserve, the index of the dataframe I saved the lowest one and within the loop it gets added again.
Overall it seems the problem was the loc function. From initially 5 hours runtime, its now about 10 seconds.

Finding the middle index value of an array in a binary search algorithm in Python

I am new to Python and implementing a binary search algorithm. Here is the algorithm:
def binary_search(list, item):
low = 0
high = len(list)-1
while low <= high:
mid = (low + high)
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return None
My question is in regard to the line mid = (low + high). The algorithm returns the correct index location for any item in the array whether I use mid = (low + high) or mid = (low + high)/2. Why is that? I have searched everywhere for an explanation for this specific question and cannot find one. All I've found is that Python 3 automatically rounds down numbers that are not evenly divisible, so for an array with an odd number of elements, like 13, the index of the middle element will be 6. But how does the algorithm above get to the middle index element without dividing by 2 every time?
It's because your algorithm isn't a binary search.
mid = (low + high)
Since high starts at len(arr) - 1 and low always starts at 0 mid always starts at len(arr) - 1.
if guess > item:
high = mid - 1
In this case in the next recalculation of mid the value of mid decreases by one. So if the guess is too high it goes down one element. The thing is since mid always starts at len(arr) - 1, this will always be True. guess will always start out as the largest element and then go down one by one. Until you hit:
if guess == item:
return mid
In which case you just return the item. Your algorithm searches for the item linearly from the last element to the first in a one by one manner.
If you actually add a print(low, high, mid) you'll get an output like this:
0 6 6
0 5 5
0 4 4
0 3 3
0 2 2
0 1 1
0 0 0

Python - Negative numbers not adding, but positive numbers do

I am supposed to add up the rows and the grand total of all the numbers. I can add the grand total well, but I am unable to add the row that has negative numbers only. The following code adds up the positive numbers but do not add up the negative numbers correctly.
grandTotal = 0
sumRow = 0
for x in range(len(numbers)):
sumRow = (sumRow + x)
print(sumRow)
for x in range(len(numbers)):
for y in range(len(numbers[x])):
grandTotal = grandTotal + int(numbers[x][y])
print(grandTotal)
When the user input is:
1,1,-2 -1,-2,-3 1,1,1
My output is: 0
1
3
-3
instead of: 0
-6
3
-3
I know it has something to do with the first for loop, but I can't figure it out. When I try this:
grandTotal = 0
sumRow = 0
for x in range(len(numbers)):
sumRow = (sumRow + (numbers[x]))
print(sumRow)
for x in range(len(numbers)):
for y in range(len(numbers[x])):
grandTotal = grandTotal + int(numbers[x][y])
print(grandTotal)
I get the error message:
File "list.py", line 14, in
sumRow = (sumRow + (numbers[x]))
TypeError: unsupported operand type(s) for +: 'int' and 'list'
Why doesn't my code add up the negative numbers? Any help is greatly appreciated!
Where you say
sumRow = (sumRow + (numbers[x]))
To add integers you say 1+1, not (1+(1)) this would be adding to lists so you could change that.
From my understanding numbers is an array as well, so saying
numbers[x]
Will give you many numbers. What you want is the total for every row, and the total of all rows. Heres a program that does this. I am assuming that your program automatically gets numbers from the user input.
grandTotal = 0
for row in numbers:
#for each row we find total amount
rowTotl = 0
for value in row:
#for each column/ value we add tot the row total
rowTotl += value
print(rowTotl)
#add this row's value to the grandTotal before we move on
grandTotal += rowTotl
#After all adding we print grand total
print(grandTotal)
The reason your program doesn't add negative numbers, is really because the row totals are not adding numbers at all. They are just adding the indexes, rather than the value, so they don't work for positive number either. The grand total works because you are adding all the values properly, rather than adding the indexes. FYI,
for index in range(len(numbers)) :
does not give you the values, but rather : 0,1,2,3,4,5,6...(indexes) till the end of the range, to get the value numbers you would do
for value in numbers:

Resources