Max Sub Array (non-adjacent) - python

I'm working on a HackerRank Max Array Sum problem. I see some simple and smart solutions but I wanna know why my code failing.
Here is my code. It takes next 4 elements from array and choose index0 or index1 and shift 2 or 3 elements.
if I can find max subset elements instead of sum of elements I can see my mistake.
HackerRank Problem Link
def Calc(arr):
result = 0
i = 0
while i < len(arr):
tempar = [i if i > 0 else 0 for i in arr[i:i+4]]
if len(tempar) == 4:
tmax = max(tempar[0] + tempar[2], tempar[0] + tempar[3], tempar[1] + tempar[3])
if tempar[0] + tempar[2] == tmax or tempar[0] + tempar[3] == tmax:
result += tempar[0]
i += 2
elif tempar[1] + tempar[3] == tmax:
result += tempar[1]
i+=3
if len(tempar) == 3:
if tempar[0] + tempar[2] > tempar[1]:
result += tempar[0] + tempar[2]
else:
result += tempar[1]
i+=3
if len(tempar) == 2:
result += max(tempar)
i+=2
if len(tempar) == 1:
result += tempar[0]
i+=1
return result
input()
ar = list(map(int, input().split()))
print(Calc(ar))

I didn't read your algorithm and the problem carefully, so I can't say whether your algorithm is right or not.
But it seems that your code allows choosing an empty set when all elements are negative. (tempar = [i if i > 0 else 0 for i in arr[i:i+4]])
For example, your program will output 0 for the input
5
-1 -1 -1 -1 -1

Your algorithm doesn't work.
for simple input
5
-1 -1 3 -2 5
I suggest you use the following approach:
def Calc(arr):
result = 0
prev_result=0
is_last_included=False
i = 0
while i < len(arr):
if arr[i] > 0:
if is_last_included==False:
prev_result=result
result=result+arr[i]
is_last_included=True
elif (prev_result+arr[i])>result:
temp=result
result=prev_result+arr[i]
prev_result=temp
is_last_included=True
else:
prev_result=result
is_last_included=False
i=i+1
return result

Related

Algorithm to return list of indices of a run length

I'm working on a function that will accept a list of integers and a "run length" as arguments. The goal is to return a list of the indices of the first element of each "run" that increments or decrements by 1 for the given run length
e.g.
values=[1,2,3,5,5,9,8,9,10,11], run_length=3 would return [0,6,7]
Where I've gotten so far, and can't quite get what I'm messing up:
def solution(values, run_length):
count = 0
previous_int = 99
marker = 0
accumulator = 0
locations = []
for i in values:
if i == (previous_int + 1):
accumulator += 1
if accumulator == (run_length - 1):
locations.append(marker)
accumulator -= 1
marker += 1
else:
marker = count
accumulator = 0
count += 1
previous_int = i
return(locations)
values=[1,2,3,5,10,9,8,9,10,11,7,8,7,6,5,4,3,2,1]
run_length=3
result = solution(values, run_length)
print(result)
Will work for some test cases but not all, like the above will only return [0,6,7] when there are clearly more.
You need to make a second pass for handling a decreasing sequence, then merge the two lists together. This code is somewhat long for my liking, but including all the code is necessary because some of the changes are subtle.
def solution(values, run_length):
increasing_locations = []
decreasing_locations = []
for differential, location_storage in [(1, increasing_locations), (-1, decreasing_locations)]:
count = 0
previous_int = 99
marker = 0
accumulator = 0
for i in values:
if i == (previous_int + differential):
accumulator += 1
if accumulator == (run_length - 1):
location_storage.append(marker)
accumulator -= 1
marker += 1
else:
marker = count
accumulator = 0
count += 1
previous_int = i
merged_result = []
increase_location_index = 0
decrease_location_index = 0
while len(merged_result) < len(increasing_locations) + len(decreasing_locations):
if decrease_location_index == len(decreasing_locations):
merged_result.append(increasing_locations[increase_location_index])
increase_location_index += 1
elif increase_location_index == len(increasing_locations):
merged_result.append(decreasing_locations[decrease_location_index])
decrease_location_index += 1
elif decreasing_locations[decrease_location_index] < increasing_locations[increase_location_index]:
merged_result.append(decreasing_locations[decrease_location_index])
decrease_location_index += 1
else:
merged_result.append(increasing_locations[increase_location_index])
increase_location_index += 1
return merged_result

Further Optimisation of Project Euler problem 14 (Collatz Sequence)

When I first starting trying the question, my code would take over a minute to even finish running and give me the answer. I have already tried dynamic programming and storing previous numbers so it doesn't have to run the same number multiple times. I have also tried compacting (n3)+1 and n / 2 into a single line with ((n3)+1) but both of these has only managed to cut my code to 10 seconds. Is there anything else I can try to speed up my code?
def Collatz(n):
dic = {a: 0 for a in range(1,1000000)}
dic[1] = 0
dic[2] = 1
number,length = 1,1
for i in range(3,n,1):
z = i
testlength = 0
loop = "T"
while loop == "T":
if z % 2 == 0:
z = z / 2
testlength += 1
else:
z = ((z*3)+1) / 2
testlength += 2
if z < i:
testlength += dic[z]
loop = "F"
dic[i] = testlength
if testlength > length:
print(i,testlength)
number,length = i,testlength
return number,length
print(Collatz(1000000))
When you calculate the sequence for one input, you find out the sequence length for all the intermediate values. It helps to remember all of these in the dictionary so you never have to calculate a sequence twice of any number < n.
I also started at (n-1)//2, since there's no point testing any number x if 2x is going to be tested later, because 2x will certainly have a longer sequence:
def Collatz(n):
dic = [-1]*n
dic[1] = 0
bestlen = 0
bestval = 1
q=[]
for i in range((n-1)//2,n,1):
q.clear()
z = i
while z >= n or dic[z] < 0:
q.append(z)
if z % 2 == 0:
z = z//2
else:
z = z*3+1
testlen = len(q)+dic[z]
if testlen > bestlen:
bestlen = testlen
bestval = i
print (bestval, bestlen)
for j in range(0,len(q)):
z = q[j]
if z < n:
dic[z] = testlen-j
return bestval, bestlen
print(Collatz(1000000))
Although the answer from Matt Timmermanns is fast, it is not quite as easy to understand as a recursive function. Here is my attempt that is actually faster for n = 10*million and perhaps easier to understand...
f = 10000000
def collatz(n):
if n>=collatz.bounds:
if (n % 4) == 0:
return collatz(n//4)+2
if (n % 2) == 0:
return collatz(n//2)+1
return collatz((3*n+1)//2)+2
if collatz.memory[n]>=0:
return collatz.memory[n]
if (n % 2) == 0:
count = collatz(n//2)+1
else:
count = collatz((3*n+1)//2)+2
collatz.memory[n] = count
return count
collatz.memory = [-1]*f
collatz.memory[1] = 0
collatz.bounds = f
highest = max(collatz(i) for i in range(f//2, f+1))
highest_n = collatz.memory.index(highest)
print(f"collatz({highest_n}) is {highest}")
My results:
$ time /usr/bin/python3 collatz.py
collatz(8400511) is 685
real 0m9.445s
user 0m9.375s
sys 0m0.060s
Compared to
$ time /usr/bin/python3 mattsCollatz.py
(8400511, 685)
real 0m10.672s
user 0m10.599s
sys 0m0.066s

Program about radical numbers: IndexError: list index out of range [python]

I am sure about that there aren't any mistakes in my class. I can't give all codes because the program is big. The program is about radical numbers. Such as converting √8 to 2√2. I am going to use this class in my main python file. Here are the codes:
def kokluyazdir(self,number):
i = 1
bolenler_listesi = []
for i in range(i,number):
if (number % i == 0):
bolenler_listesi.append(i)
i += 1
kontroldegiskeni = 0
yazdirilacak = "1√{}".format(number)
while True:
if (kontroldegiskeni + 1 > len(bolenler_listesi)):
break
kontroldegiskeni += 1
y = number // bolenler_listesi[kontroldegiskeni]
abc = self.tamkaremi(y)
if (abc.endswith("tam karedir.")):
yazdirilacak = "{}√{}".format(self.neyleneyincarpimi,bolenler_listesi[kontroldegiskeni])
return yazdirilacak
hesaplamalar = Hesaplamalar()
print(hesaplamalar.kokluyazdir(8))
Here is the error:
y = number // bolenler_listesi[kontroldegiskeni]
IndexError: list index out of range
for i in range(i,number):->number=8
if (number % i == 0):
bolenler_listesi.append(i)
i += 1
After completion of the loop bolenler_listesi will be[1,2,4], len=3
kontroldegiskeni = 0
while True:
if (kontroldegiskeni + 1 > len(bolenler_listesi)):
break
kontroldegiskeni += 1
y = number // bolenler_listesi[kontroldegiskeni]
abc = self.tamkaremi(y)
when the above loop runs iteration values will be as follows:
iteration 1: (0+1 >3)--->false -->kontrodegiskeni=0+1=1--> y=8 // bolenler_listesi[1]
iteration 2: (1+1 >3)--->false -->kontrodegiskeni=1+1=2--> y=8 // bolenler_listesi[2]
iteration 3: (2+1 >3)--->false -->kontrodegiskeni=2+1=3--> y=8 // bolenler_listesi[3]
// error will be thrown here as index out of range because bolenler_listesi=[1,2,4], len=3
but here , here it's trying to access element greater than len of list bolenler_listesi[3] (index for values in list starts from 0)
if (kontroldegiskeni + 1 > len(bolenler_listesi)):
break
since you increment kontroldegiskeni after the check it might equal the Len of the array when accessing which would cause the error. Instead the check should be done after the increment or make the condition
if (kontroldegiskeni + 1 >= len(bolenler_listesi)):
break
Add an if condition inside the while loop when the "kontroldegiskeni" variable becomes equal to the length of array "bolenler_listesi". I tested the code as a stand alone function and it works with no errors. I couldn't verify the correctness of the output as this function depends on other classes.
def kokluyazdir(self,number):
i = 1
bolenler_listesi = []
for i in range(i,number):
if (number % i == 0):
bolenler_listesi.append(i)
i += 1
kontroldegiskeni = 0
yazdirilacak = "1√{}".format(number)
while True:
if (kontroldegiskeni + 1 > len(bolenler_listesi)):
break
kontroldegiskeni += 1
if(kontroldegiskeni<len(bolenler_listesi)):
y = number // bolenler_listesi[kontroldegiskeni]
abc = self.tamkaremi(y)
if (abc.endswith("tam karedir.")):
yazdirilacak = "{}√{}".format(self.neyleneyincarpimi,bolenler_listesi[kontroldegiskeni])
return yazdirilacak
hesaplamalar = Hesaplamalar()
print(hesaplamalar.kokluyazdir(8))

Function to sum and compare two sides of an array

For example this is array = [1,2,3,4,3,2,1]
i want computer to choose i=4 and sum both left of the four and right of the four and then compare if they are equal to each other, program prints i value to the screen.
Now i write it in here and it works as long as the i value is 0 but if i want to make this search for i>0 then i gets complicated.
this is my main program:
# importing "array" for array creations
import array as arr
# Function to find sum
# of array exlcuding the
# range which has [a, b]
def sumexcludingrange(li, a, b): # I FOUND THIS FUNCTION ONLINE BUT ALSO DIDN`T WORK EITHER.
sum = 0
add = True
# loop in li
for no in li:
# if no != a then add
if no != a and add == True:
sum = sum + no
# mark when a and b are found
elif no == a:
add = False
elif no == b:
add = True
# print sum
return sum
#lis = [1, 2, 4, 5, 6]
#a = 2
#b = 5
#sumexcludingrange(arr, 0, i-1)
def my(arr):
for i in range(len(arr)):
if i == 0: #this works when array is like [1,0,0,1] and i equals zero
sum_left = arr[0]
sum_right = sum(arr) - arr[0]
while sum_left == sum_right:
print(i)
break
elif i > 0 : # i 1 2 3 4 5 6 7 8 9..
#sumleft and sumright are not right.. what is the issue here?
sum_left = sumexcludingrange(arr, 0, i-1) #result is 16, sumexcludingrange function didn`t
#work.
print (sum_left)
#print(i)
sum_right == sum(arr) - sum_left
#print(sum_right)
#break
while sum_left == sum_right:
print(sum_left)
print(sum_right)
print("they are equal now")
break
else:
print("index cannot be a negative number.")
something = arr.array('i', [1,2,3,4,3,2,1]) # len(arr) = 7
my(something)
After seeing that i need another function to compare two sides of the array, i created a new file and write it in here and somehow it outputs this:
1
13
13
2
10
10
#array[i] array[i+1] .. + array[length-1]
array = [1,2,3,4,5,6,7] # 7 element so length=7,
for i in range(len(array)):
if i > 0 :
ln = len(array) + 1
sum_right = sum(array[i+1:ln])
sum_left = sum(array) - sum_right
print(i)
print(sum_right)
print(sum_right)
the way i think is this:
# array[i] i>0 i={1,2,3,4..}
sum_total = sum(arr) = array[0] + ..+ array[i-1] + array[i] + array[i+1] + ... + array[length-1]
sum_right = array[i+1] + .. + array[length-1]
sum_left = sum_total - sum_right
Is there a better way to accomplish that?
If I understand your question correctly, the solution could be something like this:
array = [1,2,3,4,5,6,7]
a1 = array[:4] # first 4 numbers
a2 = array[-4:] # last 4 numbers
total = sum(array)
diff = total-sum(a2)

Python 3: Optimizing Project Euler Problem #14

I'm trying to solve the Hackerrank Project Euler Problem #14 (Longest Collatz sequence) using Python 3. Following is my implementation.
cache_limit = 5000001
lookup = [0] * cache_limit
lookup[1] = 1
def collatz(num):
if num == 1:
return 1
elif num % 2 == 0:
return num >> 1
else:
return (3 * num) + 1
def compute(start):
global cache_limit
global lookup
cur = start
count = 1
while cur > 1:
count += 1
if cur < cache_limit:
retrieved_count = lookup[cur]
if retrieved_count > 0:
count = count + retrieved_count - 2
break
else:
cur = collatz(cur)
else:
cur = collatz(cur)
if start < cache_limit:
lookup[start] = count
return count
def main(tc):
test_cases = [int(input()) for _ in range(tc)]
bound = max(test_cases)
results = [0] * (bound + 1)
start = 1
maxCount = 1
for i in range(1, bound + 1):
count = compute(i)
if count >= maxCount:
maxCount = count
start = i
results[i] = start
for tc in test_cases:
print(results[tc])
if __name__ == "__main__":
tc = int(input())
main(tc)
There are 12 test cases. The above implementation passes till test case #8 but fails for test cases #9 through #12 with the following reason.
Terminated due to timeout
I'm stuck with this for a while now. Not sure what else can be done here.
What else can be optimized here so that I stop getting timed out?
Any help will be appreciated :)
Note: Using the above implementation, I'm able to solve the actual Project Euler Problem #14. It is giving timeout only for those 4 test cases in hackerrank.
Yes, there are things you can do to your code to optimize it. But I think, more importantly, there is a mathematical observation you need to consider which is at the heart of the problem:
whenever n is odd, then 3 * n + 1 is always even.
Given this, one can always divide (3 * n + 1) by 2. And that saves one a fair bit of time...
Here is an improvement (it takes 1.6 seconds): there is no need to compute the sequence of every number. You can create a dictionary and store the number of the elements of a sequence. If a number that has appeared already comes up, the sequence is computed as dic[original_number] = dic[n] + count - 1. This saves a lot of time.
import time
start = time.time()
def main(n,dic):
'''Counts the elements of the sequence starting at n and finishing at 1'''
count = 1
original_number = n
while True:
if n < original_number:
dic[original_number] = dic[n] + count - 1 #-1 because when n < original_number, n is counted twice otherwise
break
if n == 1:
dic[original_number] = count
break
if (n % 2 == 0):
n = n/2
else:
n = 3*n + 1
count += 1
return dic
limit = 10**6
dic = {n:0 for n in range(1,limit+1)}
if __name__ == '__main__':
n = 1
while n < limit:
dic=main(n,dic)
n += 1
print('Longest chain: ', max(dic.values()))
print('Number that gives the longest chain: ', max(dic, key=dic.get))
end = time.time()
print('Time taken:', end-start)
The trick to solve this question is to compute the answers for only largest input and save the result as lookup for all smaller inputs rather than calculating for extreme upper bound.
Here is my implementation which passes all the Test Cases.(Python3)
MAX = int(5 * 1e6)
ans = [0]
steps = [0]*(MAX+1)
def solve(N):
if N < MAX+1:
if steps[N] != 0:
return steps[N]
if N == 1:
return 0
else:
if N % 2 != 0:
result = 1+ solve(3*N + 1) # This is recursion
else:
result = 1 + solve(N>>1) # This is recursion
if N < MAX+1:
steps[N]=result # This is memoization
return result
inputs = [int(input()) for _ in range(int(input()))]
largest = max(inputs)
mx = 0
collatz=1
for i in range(1,largest+1):
curr_count=solve(i)
if curr_count >= mx:
mx = curr_count
collatz = i
ans.append(collatz)
for _ in inputs:
print(ans[_])
this is my brute force take:
'
#counter
C = 0
N = 0
for i in range(1,1000001):
n = i
c = 0
while n != 1:
if n % 2 == 0:
_next = n/2
else:
_next= 3*n+1
c = c + 1
n = _next
if c > C:
C = c
N = i
print(N,C)
Here's my implementation(for the question specifically on Project Euler website):
num = 1
limit = int(input())
seq_list = []
while num < limit:
sequence_num = 0
n = num
if n == 1:
sequence_num = 1
else:
while n != 1:
if n % 2 == 0:
n = n / 2
sequence_num += 1
else:
n = 3 * n + 1
sequence_num += 1
sequence_num += 1
seq_list.append(sequence_num)
num += 1
k = seq_list.index(max(seq_list))
print(k + 1)

Categories