Reducing time complexity of contiguous subarray - python

I was wondering how I could reduce the time complexity of this algorithm.
It calculates the length of the max subarray having elements that sum to the k integer.
a = an array of integers
k = max integer
ex: a = [1,2,3], k= 3
possible subarrays = [1],[1,2]
length of the max subarray = 2
sys.setrecursionlimit(20000)
def maxLength(a, k):
#a = [1,2,3]
#k = 4
current_highest = 0
no_bigger = len(a)-1
for i in xrange(len(a)): #0 in [0,1,2]
current_sum = a[i]
sub_total = 1
for j in xrange(len(a)):
if current_sum <= k and ((i+sub_total)<=no_bigger) and (k>=(current_sum + a[i+sub_total])):
current_sum += a[i+sub_total]
sub_total += 1
else:
break
if sub_total > current_highest:
current_highest = sub_total
return current_highest

You can use sliding window algorithm for this.
Start at index 0, and calculate sum of subarray as you move forward. When sum exceeds k, start decrementing the initial elements till sum is again less than k and start summing up again.
Find below the python code:
def max_length(a,k):
s = 0
m_len = 0
i,j=0,0
l = len(a)
while i<l:
if s<=k and m_len<(j-i):
m_len = j-i
print i,j,s
if s<=k and j<l:
s+=a[j]
j+=1
else:
s-=a[i]
i+=1
return m_len
a = [1,2,3]
k = 3
print max_length(a,k)
OUTPUT:
2

Related

Given an array of N integers, and an integer K, find the number of pairs of elements in the array whose sum is equal to K

Problem Statement:- Given an array of N integers, and an integer K, find the number of pairs of elements in the array whose sum is equal to K.
**def countpairs(x,length,sum):
count = 0
for i in range(0,length):
for j in range(i+1,length):
print(x[i],x[j])
if(x[i]+x[j]==sum):
count+=1
print(count)
x = [1, 1, 1, 1]
sum = 2
length=len(x)
countpairs(x,length,sum)
Output:= 6**
This is My solution used in VS code.
My Question:- whenever I am running the same code in gfg it is not accepting the code giving me this error. I even have tried the same code in the online compiler there also it is running correctly.
This Is the gfg code which i have written
class Solution:
def getPairsCount(self, arr, K, N):
count = 0
for i in range(0,N):
for j in range(i+1,N):
if(arr[i]+arr[j]==K):
count+=1
return count
#Initial Template for Python 3
if __name__ == '__main__':
tc = int(input())
while tc > 0:
n, k = list(map(int, input().strip().split()))
arr = list(map(int, input().strip().split()))
ob = Solution()
ans = ob.getPairsCount(arr, n, k)
print(ans)
tc -= 1
Error
if(arr[i]+arr[j]==K):
IndexError: list index out of range
There's no added value in using a class for this. You just need:-
def getPairsCount(arr, K):
count = 0
for i in range(len(arr)-1):
if arr[i] + arr[i+1] == K:
count += 1
return count
EDIT:
Previous answer assumed that only adjacent elements were to be considered. If that's not the case then try this:-
import itertools
def getPairsCount(arr, K):
count = 0
for c in itertools.combinations(sorted(arr), 2):
if c[0] + c[1] == K:
count += 1
return count
data = [1, 2, 1, 4, -1]
print(getPairsCount(data, 3))
We do not need two loops for this question. Here is something that runs in O(n):
def countpairs(list_,K):
count = 0
set_ = set(list_)
pairs_ = []
for val in list_:
if K - val in set_:
# we ensure that pairs are unordered by using min and max
pairs_.append ( (min(val, K-val), max(val, K-val)) )
count+=1
set_pairs = set(pairs_)
print ("Pairs which sum up to ",K," are: ", set_pairs)
return len(set_pairs)
x = [1,4,5,8,2,0,24,7,6]
sum_ = 13
print ("Total count of pairs summming up to ", sum_, " = ", countpairs(x, sum_))
Output:
Pairs which sum up to 13 are: {(6, 7), (5, 8)}
Total count of pairs summming up to 13 = 2
The idea is that if two values should sum to a value K, we can iterate through the array and check if there is another element in the array which when paired with the current element, sums up to K. The inner loop in your solution can be replaced with a search using the in. Now, we need this search to be fast (O(1) per element), so we create a set out of our input array (set_ in my example).
def solve(a,K):
freq = {}
for v in a:
if v in freq:
freq[v] += 1
else:
freq[v] = 1
for i in range(len(set(a))):
res += freq[a[i]] * freq[K - a[i]]
return res
a = [int(v) for v in input().split()]
K = int(input())
print(solve(a,K))
# Time Complexity : O(N)
# Space Complexity : O(1)
def solve(a,K):
freq = {}
for v in a:
if v in freq:
freq[v] += 1
else:
freq[v] = 1
for i in range(len(set(a))):
res += freq[a[i]] * freq[K - a[i]]
return res
a = [int(v) for v in input().split()]
K = int(input())
print(solve(a,K))

return index of max ascending array

Here, I currently return the length of the max ascending array. However, I want to modify it such that I can return the index of the max ascending array. I tried setting idx = I inside the if (A[i] > A[i-1]) :but that does not seem to work. What else could I try?
def func(A):
n = len(A)
m = 1
l = 1
idx = -1
# traverse the array from the 2nd element
for i in range(1, n) :
# if current element if greater than previous
# element, then this element helps in building
# up the previous increasing subarray encountered
# so far
if (A[i] > A[i-1]) :
l =l + 1
idx = i-1
else :
# check if 'max' length is less than the length
# of the current increasing subarray. If true,
# then update 'max'
if (m < l) :
m = l
# reset 'len' to 1 as from this element
# again the length of the new increasing
# subarray is being calculated
l = 1
# comparing the length of the last
# increasing subarray with 'max'
if (m < l) :
m = l
# required maximum length
return m
I hope this is what you are looking for.
def func(A):
n = len(A)
m = 1
l = 1
index = [0,0]
flag=0
for i in range(1, n):
if (A[i] > A[i-1]) :
l =l + 1
if flag==0:
flag=1
lindex = i-1
else:
if (m < l) :
m = l
index = [lindex,i-1]
flag=0
l = 1
if (m < l) :
m = l
index = [lindex,i]
return index

Optimizing the algorithm for brute force numbers in the problem

How many pairs (i, j) exist such that 1 <= i <= j <= n, j - i <= a?
'n' and 'a' input numbers.
The problem is my algorithm is too slow when increasing 'n' or 'a'.
I cannot think of a correct algorithm.
Execution time must be less than 10 seconds.
Tests:
n = 3
a = 1
Number of pairs = 5
n = 5
a = 4
Number of pairs = 15
n = 10
a = 5
Number of pairs = 45
n = 100
a = 0
Number of pairs = 100
n = 1000000
a = 333333
Number of pairs = 277778388889
n = 100000
a = 555555
Number of pairs = 5000050000
n = 1000000
a = 1000000
Number of pairs = 500000500000
n = 998999
a = 400000
Number of pairs = 319600398999
n = 898982
a = 40000
Number of pairs = 35160158982
n, a = input().split()
i = 1
j = 1
answer = 0
while True:
if n >= j:
if a >= (j-i):
answer += 1
j += 1
else:
i += 1
j = i
if j > n:
break
else:
i += 1
j = i
if j > n:
break
print(answer)
One can derive a direct formula to solve this problem.
ans = ((a+1)*a)/2 + (a+1) + (a+1)*(n-a-1)
Thus the time complexity is O(1). This is the fastest way to solve this problem.
Derivation:
The first a number can have pairs of (a+1)C2 + (a+1).
Every additional number has 'a+1' options to pair with. So, therefore, there are n-a-1 number remaining and have (a+1) options, (a+1)*(n-a-1)
Therefore the final answer is (a+1)C2 + (a+1) + (a+1)*(n-a-1) implies ((a+1)*a)/2 + (a+1) + (a+1)*(n-a-1).
You are using a quadratic algorithm but you should be able to get it to linear (or perhaps even better).
The main problem is to determine how many pairs, given i and j. It is natural to split that off into a function.
A key point is that, for i fixed, the j which go with that i are in the range i to min(n,i+a). This is since j-i <= a is equivalent to j <= i+a.
There are min(n,i+a) - i + 1 valid j for a given i. This leads to:
def count_pairs(n,a):
return sum(min(n,i+a) - i + 1 for i in range(1,n+1))
count_pairs(898982,40000) evaluates to 35160158982 in about a second. If that is still to slow, do some more mathematical analysis.
Here is an improvement:
n, a = map(int, input().split())
i = 1
j = 1
answer = 0
while True:
if n >= j <= a + i:
answer += 1
j += 1
continue
i = j = i + 1
if j > n:
break
print(answer)

Maximum Sum Increasing Subsequence

So I made a simple python code using Dynamic programming to solve the problem of Maximum Increasing Subsequence. The Question is as follows:
Given an array arr of N positive integers. Find the sum of maximum sum increasing subsequence of the given array.
Input:
The first line of input contains an integer T denoting the number of test cases. The first line of each test case is N(the size of array). The second line of each test case contains array elements.
Output:
For each test case print the required answer in new line.
In my solution I am calculating the maximum sum in a list called 'sums'
#code
T = int(input())
for _ in range(T):
N = int(input())
arr = list(map(int, input().split()))
sums = list(arr)
max_sum = arr[0]
for j in range(1,N):
for i in range(0,j):
if arr[i]<arr[j] and sums[j]<sums[i]+arr[j]:
sums[j] = (sums[i]+arr[j])
if sums[j]>max_sum:
max_sum = sums[j]
print(max_sum)
My Output:
Your program took more time than expected.Time Limit Exceeded.
Expected Time Limit < 2.32sec
Hint : Please optimize your code and submit again.
How do I optimise this code any further?
I think this will work
def max_inc(a):
max = a[0]
prev = a[0]
current = a[0]
for i in range(1,len(a)):
if a[i]>prev:
current+=a[i]
if current>max:
max = current
else:
current = 0
prev = a[i]
return max
in O(n)
More Readability:
def maxSumIncreasingSubsequence(array):
sums = [num for num in array]
maxSumIdx = 0
for i in range(len(array)):
currVal = array[i]
for j in range(i):
prevVal = array[j]
if currVal > prevVal and sums[j] + currVal >= sums[i]:
sums[i] = sums[j] + currVal
if sums[i] >= sums[maxSumIdx]:
maxSumIdx = i
return sums[maxSumIdx]
T = int(input())
for _ in range(T):
N = int(input())
arr = list(map(int, input().split()))
maxSumIncreasingSubsequence([10, 70, 20, 30, 50, 11, 30])

How do you sort a list with a while loop in Python?

How do you sort a list with a while loop? Having a bit of a problem, thanks very much in advance.
a = [12,0,39,50,1]
first = a[0]
i = 0
j = 1
while i < len(a):
if a[i] < first:
tmp = a[i]
a[i] = a[j]
a[j] = tmp
i += 1
print(a)
You can create an empty list that would store your sorted numbers
a = [12,0,39,50,1]
kk = len(a)
new_a = []
i = 0
while i < kk:
xx = min(a) ## This would retreive the minimum value from the list (a)
new_a.append(xx) ## You store this minimum number in your new list (new_a)
a.remove(xx) ## Now you have to delete that minimum number from the list a
i += 1 ## This starts the whole process again.
print(new_a)
Please, note that I used the original length of the list a (kk) for the while statement so as not to stop the iteration because the length of the list a decreases as we delete the minimum numbers.
Following is the implementation of basic sorting using two while loops.
In every iteration, the minimum element (considering ascending order) from the unsorted subarray is picked and moved to the sorted subarray.
:
a=[12,0,39,50,1]
i=0
while i<len(a):
key=i
j=i+1
while j<len(a):
if a[key]>a[j]:
key=j
j+=1
a[i],a[key]=a[key],a[i]
i+=1
print(a)
# By using For loop
def ascending_array(arr):
print(f"Original array is {arr}")
arr_length = len(arr)
if arr_length <= 1:
return arr
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] >= arr[j]:
arr[i], arr[j] = arr[j], arr[i]
print(f"The result array is {arr}") # [0,0,0,1,10,20,59,63,88]
arr = [1,10,20,0,59,63,0,88,0]
ascending_array(arr)
# By using While loop
def ascending_array(arr):
print(f"Original array is {arr}")
arr_length = len(arr)
if arr_length <= 1:
return arr
i = 0
length_arr = len(arr)
while (i<length_arr):
j = i+1
while (j<length_arr):
if arr[i] > arr[j]:
arr[i], arr[j] = arr[j], arr[i]
j+=1
i+=1
print(f"The result array is {arr}") # [0,0,0,1,10,20,59,63,88]
arr = [1,10,20,0,59,63,0,88,0]
ascending_array(arr)
For-loop is best in terms of performance. while-loop is checking condition every iteration.
You can also concatenate two lists and sort them in decreasing/increasing order using this example:
x = [2,9,4,6]
y = [7,8,3,5]
z = []
maxi = x[0]
pos = 0
print('x: '+str(x))
print('y: '+str(y))
for i in range(len(y)):
x.append(y[i])
for j in range(len(x)-1):
maxi = x[0]
for i in range(len(x)):
if maxi < x[i]:
maxi = x[i]
pos = i
z.append(maxi)
del x[pos]
z.append(x[0])
print('z: '+str(z))

Categories

Resources