Decrease computation time for finding maximal subarray (Python) - python

I need to compute an algorithm that finds the maximal subarray of an array of integers (both positive and negative), and then computes the sum of this subarray.
So far I've the following solution, but I reach the code evaluator's time limit when the input becomes large (the input array A can be of up to length 200k).
n = int(input())
A = list(map(int, input().split()))
max_sum = A[0]
current_max = 0
x = 1
for i in range(n):
for j in range(x,n+1):
current_max = sum(A[i:j])
if current_max > max_sum:
max_sum = current_max
x += 1
print(max_sum)
Any help is greatly appreciated.

Related

loop through the list to get Maximum sum for a given range in python

I am a novice in python. I have a code where I loop through a list to capture maximum sum of numbers for given range k. It is working fine but I want it to make it shorter/optimal. 'k' may vary
numb = [100,33,22,200,333,1000,22]
m=0
k=2
sum1=0
temp=[]
for j in range(len(numb)-(k-1)):
for i in range(m,k):
temp.append(numb[i])
if sum1 < sum(temp):
sum1 = sum(temp)
temp=[]
m+=1
k+=1
print(sum1)
Ans: 1533 when k = 3
Ans: 1333 when k = 2
You can start by adding up the first k numbers. That is your starting sum and your current max. Then run a sliding window along the list, adding the next number and removing the one that goes out of the window.
def sum_k(x, k):
m = s = sum(x[:k])
for i, a in enumerate(x[k:]):
b = x[i] # number to remove
s += a - b
m = max(m, s)
return m
numb = [100, 33, 22, 200, 333, 1000, 22]
print(sum_k(numb, 2), sum_k(numb, 3))
This runs in linear time, which is optimal since you need to at least look at every element in your input.
The index, i, in the loop runs from zero to n-k-1, so although we enumerate over x[k:] the indices we pick are from x[0:], so when we pick b we are picking the number that goes out of the window. Meanwhile, a is the new number that comes in.
This is the simplified code you want which takes O(n) of time complexity. This approach is based on Sliding Window Algorithm.
maxSum is the function which takes 2 arguments (array of numbers and k) and returns maximum for any value of k.
def maxSum(arr, k):
# Edge case
if len(arr) <= k:
return sum(arr)
sums = sum(arr[:k]) # sum the first 3 val in arr.
start = 0 # tell us the first element index whose value is in sums variable
maximum = sums
for val in arr[k:]:
sums = (sums - arr[start]) + val
# here we first subtracted the start value and then added current value.
# Eg. From [1,2,3,4] sums have 1+2+3, but now sums have ( 1+2+3(previous) - 1(start) ) + 4(current)
# Now check for maximum.
if sums > maximum:
maximum = sums
# now increase start by 1 to make pointer to value '2' and so on.
start += 1
# return maximum
return maximum
arr = [100,33,22,200,333,1000,22]
k = 2
print("For k=2: ", maxSum(arr, k))
k = 3
print("For k=3: ", maxSum(arr, k))
Output:
For k=2: 1333
For k=3: 1533

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])

Trying to optimize intersection of n ranges but haven't came up with a solution yet?

here is my version of code i need to check every range and the ranges are in 10^9 limit
p = []
count = 0
n = int(input())
for i in range(n):
a = list(map(int,input().split()))
p.append(a)
for i in range(n):
for j in range(n):
if range(max(p[i][0], p[j][0]), min(p[i][-1], p[j][-1])+1):
count+=1
print(count-n)
I guess this answer is slow because of O(n^2).
This code takes n (start and end) of ranges and returns the total number of intersection possible

counting sort - negative integers

I'm trying to do a counting_sort on negative numbers, the following code only seems to work on positive integers. Any pointers on how I can fix it to work on negative numbers?
def count_sort(l):
output = [0] * len(l)
low = min(l)
if low >= 0:
low = 0
high = max(l)
working = [0 for i in range(low, high+1)]
for i in l:
workng[i] += 1
for j in range(1, len(working)):
working[j] += working[j-1]
for k in reversed(ul):
output[working[k] - 1] = k
working[k] -= 1
return output
Counting sort can work for any bounded range of integers, they don't all need to be positive. To make your code work for negative values (and work without wasting lots of memory for positive values that are all far from zero, e.g. [1e10, 1e10+1, 1e10+2]), just offset your indexes into count_array by the minimum value in the input:
def counting_sort(unsorted):
result = [0] * len(unsorted)
low = min(unsorted) # we don't care if this is positive or negative any more!
high = max(unsorted)
count_array = [0 for i in range(low, high+1)]
for i in unsorted:
count_array[i-low] += 1 # use an offset index
for j in range(1, len(count_array)):
count_array[j] += count_array[j-1]
for k in reversed(unsorted):
result[count_array[k-low] - 1] = k # here too
count_array[k-low] -= 1 # and here
return result
When you index count_array with negative elements, your algorithm doesn't work. Python will interpret count_array[-1] as the last element of count_array
So you can either change the code to take that into account or shift all elements before sorting
unsorted = [e - low for e in unsorted]
and at the end change it back
result = [e + low for e in result]

Finding the "centered average" of a list

"Return the "centered" average of a list of integers, which we'll say is the mean average of the values, except ignoring the largest and smallest values in the list. If there are multiple copies of the smallest value, ignore just one copy, and likewise for the largest value. Use integer division to produce the final average. You may assume that the list is length 3 or more."
This is a problem I have from my homework assignment and I am stumped at how to find the the largest/smallest numbers and cut them out of the list. Here is what I have so far. and It works for 10/14 the scenarios that I have to pass.. I think it is just because it grabs the median
def centered_average(nums):
x = 0
for i in range(len(nums)):
x = i + 0
y = x + 1
if y%2 == 0:
return (nums[y/2] + nums[(y/2)+1]) / 2
else:
return nums[y/2]
Sorting the array is certainly terser code, here's an alternative with a manual loop
max_value = nums[0]
min_value = nums[0]
sum = 0
for x in nums:
max_value = max(max_value, x)
min_value = min(min_value, x)
sum += x
return (sum - max_value - min_value) / (len(nums) - 2)
This just adds everything in and removes the max and min at the end.
If the list isn't too long, it shouldn't be too computationally expensive to sort the list:
sorted(nums)
Then you can create a new list without the first and last entries, which will be the smallest and largest values:
new_nums = sorted(nums)[1:-1] # from index 1 to the next-to-last entry
Before i start i know there are easier ways mentioned in the other answers using the function sort, yes that is true but i believe your teacher must have iven you this to able to master loops and use them logically.
First pick your first number and assign it to high and low, don't worry it will make sense afterwards.
def centered average(nums):
high = nums[0]
small = nums[0]
Here is were the magic happens, you loop through your list and if the number your on in the loop is larger then the previous ones then you can replace the variable high with it, let me demonstrate.
for count in nums:
if count > high:
high = count
if count < low:
low = count
Now you have the low and the high all you do is add the values of the loop together minus the high and the low (as you said you do not need them).Then divide that answer by len of nums.
for count in nums:
sum = count + sum
sum = sum - (high + low)
return sum
New here. I like to check my solutions with solutions found on the internet and did not see my code here yet (hence the post). I found this challenge on https://codingbat.com/prob/p126968. And here is my solution:
** This is done in Python 3.9.1.
First the min and max are popped from the list with the index method. After it's just a simple avg calculation.
def centered_average(nums):
nums.pop(nums.index(max(nums)))
nums.pop(nums.index(min(nums)))
return sum(nums)/len(nums)
If I understand the question, this should work:
def centered_average(nums):
trim = sorted(nums)[1:-1]
return sum(trim) / len(trim)
def centered_average(nums):
nums = sorted(nums)
for i in range(len(nums)):
if len(nums)%2 != 0:
return nums[len(nums)/2]
else:
return ((nums[len(nums)/2] + nums[len(nums)/2 - 1]) / 2)
This is a very sub standard solution to the problem. This code is a bad code that does not take into account any consideration for complexity and space. But I think the thought process to be followed is similar to the steps in the code. This then can be refined.
def centered_average(nums):
#Find max and min value from the original list
max_value = max(nums)
min_value = min(nums)
#counters for counting the number of duplicates of max and min values.
mx = 0
mn = 0
sum = 0
#New list to hold items on which we can calculate the avg
new_nums = []
#Find duplicates of max and min values
for num in nums:
if num == max_value:
mx += 1
if num == min_value:
mn += 1
#Append max and min values only once in the new list
if mx > 1:
new_nums.append(max_value)
if mn > 1:
new_nums.append(min_value)
#Append all other numbers in the original to new list
for num in nums:
if num != max_value and num != min_value:
new_nums.append(num)
#Calculate the sum of all items in the list
for new in new_nums:
sum += new
#Calculate the average value.
avg = sum/len(new_nums)
return avg
def centered_average(nums):
min1=nums[0]
max1=nums[0]
for item in nums:
if item > max1:
max1 = item
if item < min1:
min1 = item
sum1=(sum(nums)-(min1+max1))/(len(nums)-2)
return sum1
simple solution
def centered_average(nums):
b=nums
ma=max(b)
mi=min(b)
l=(len(b)-2)
s=sum(b)-(ma+mi)
av=int(s/l)
return av
use sum function to sum the array
max and min functions to get the biggest and smallest number
def centered_average(nums):
return (sum(nums) - max(nums) - min(nums)) / (len(nums) - 2)
def centered_average(nums):
sorted_list = sorted(nums)
return sum(sorted_list[1:-1])/(len(nums)-2)
This will get the job done.
Python 3 Solution using list.index, list.pop, min and max functions.
def solution(input):
average = 0
minimum = min(input)
maximum = max(input)
input.pop(input.index(minimum))
input.pop(input.index(maximum))
average = round(sum(input) / len(input))
return average
def centered_average(nums):
nums.remove((min(nums)))
nums.remove((max(nums)))
new_nums=nums
count = 0
for i in range(len(new_nums)):
count+=1
ans=sum(new_nums)//count
return ans
def centered_average(nums):
maximums = []
minimums = []
sum_of_numbers = 0
length =len(nums) + (len(minimums)-1) + (len(maximums)-1)
for i in nums:
if i == max(nums):
maximums.append(i)
elif i == min(nums):
minimums.append(i)
else:
sum_of_numbers += i
if len(maximums)>=2 or len(minimums)>=2:
sum_of_numbers = sum_of_numbers + (max(nums)*(len(maximums)-1))(min(nums)*(len(minimums)-1))
return sum_of_numbers / length

Categories

Resources