Why does my list of zeroes result in IndexError? - python

I get an error on occurence[j] = 0. I do not really understand the origins of this error in my code, as it is of length dna, because I append at the top of the code len(dna) zeroes and then I assign some value to the same list occurence in my nested loop, where j can only reach the value of len(dna).
for i in range(len(dna)):
occurence.append(0)
print(f"{len(dna)}")
print(f"{len(occurence)}")
#Calculating consecutive sequences and creating a 2D array occurence...
for i in types:
for j in range(len(dna)):
if (dna[j:j+len(i)] != i):
occurence[j] = 0
else:
space = len(i)
while(dna.find(i, space+len(i)) != -1):
index = dna.find(i, space+len(i))
space = space + len(i)
if (index == len(i)):
occurence[j] += 1
for k in range(len(occurence)):
maximum = 0
if(occurence[k] > maximum):
maximum = occurence[k]
counts.append(maximum)
maximum = 0
occurence.clear()

At the end of the first iteration over types, you call occurence.clear(), which will result in occurence being an empty list. Then, when you try to access occurence[j] on the second iteration, this throws an IndexError since the list is empty.
I think you instead want to initialize your list inside the for i in types loop, e.g.:
for i in types:
occurence = [0] * len(dna)
for j in range(len(dna)):
...
You would then not need to call the clear method on your list, since it would be redefined as a list of zeroes on each iteration.

Related

Remove Duplicates from Sorted Array in O(1) Space

Question: https://leetcode.com/problems/remove-duplicates-from-sorted-array/description/
Basically, duplicates in a sorted array needs to be removed without creating a new one. I have tested my solution, and it falters when there are 3, 5, 7 (and so on) number of duplicates of the same number. What techniques can I try?
Code:
def removeDuplicates(nums):
i = 0 # moving pointer
end = len(nums)-1 # points to the end of the array
# iterates over the array
while i != end:
# if current number is equal to the next
if (nums[i] == nums[i+1]):
# pop it off
nums.pop(i)
# decrement the length of array
end -= 1
i += 1 # increments the moving pointer
return i + 1
nums = [0,0,1,1,1,2,2,3,3,4]
print(removeDuplicates(nums))
The way to remove duplicates from a sorted list in O(1) space and O(n) time is to pack the unique elements at the beginning of the list, and then truncate the list:
def removeDuplicates(nums):
#new length
newlen = 0
for val in nums:
if newlen == 0 or nums[newlen-1] != val:
# no matter what, we've already retrieved nums[newlen],
# so this write doesn't interfere with the rest of the loop
nums[newlen] = val
newlen += 1
# delete the unused space at the end of the list, all at once
if newlen < len(nums):
del nums[newlen:]
return newlen
nums = [0,0,1,1,1,2,2,3,3,4]
print(removeDuplicates(nums))
print(nums)
Your original code fails, because you remove elements from the start of the array as you're iterating through the end. The iteration remembers its position in the array in i, and when you remove an element, the elements after it are shifted into lower positions.
You could fix that by putting the i += 1 into an else, but the resulting implementation would still take O(N^2) time, which isn't really acceptable.

Finding the index of an element that divide the array to two equal sums

take an array and find an index i where the sum of the integers to the left of i is equal to the sum of the integers to the right of i. If there is no index that would make this happen, return -1.
Why this code doesn't work all cases.
why it is wrong ,
def find_even_index(arr):
i=1
size=len(arr)
sum_left=0
sum_right=0
for i in range(size):
sum_right=sum(arr[i+1:]) #sum of all elements in right of i
sum_left=sum(arr[:i-1] ) #sum of all elements in left of i
if(sum_right==sum_left):
return i
return -1
There are a few logical errors in your program. Firstly you declare i prior to the for loop, you should do it within the range() function itself, as the value of i resets on iterating it over the for loop. It should be like this:
for i in range(1, size):
Also you are not doing correct slicing, remember that when you slice any list by giving start and end index, it ignores the end value, and only slices till end - 1.
For example,
>>> a = [10,20,30,40,50,60]
>>> print(a[2:4]) # this will print the 2nd and 3rd element only, ignoring the 4th one
[30,40]
Also, if value of i starts from 0, arr[:i-1] will return whole array at once, so loop will break at first iteration.
For example,
>>> a = [10,20,30,40,50]
>>> print(a[0:])
[10,20,30,40,50]
>>> print(a[:-1]) # index -1 refers to the last element of array
[10,20,30,40,50]
And you are using the return -1 statement within the for loop, so the loop will break in the first iteration itself, you should do it outside the for loop.
Now your formatted code should look like this:
def find_even_index(arr):
size = len(arr)
sum_left = 0
sum_right = 0
for i in range(1, size): # value of i initially set here as 1
sum_right = sum(arr[i+1:])
sum_left = sum(arr[:i]) # corrected the slicing
if(sum_right == sum_left):
return i
return -1 # return method indented outside the for loop
Hope this answer helps! :)
If you can't understand the reasoning even now, you can read about the topics here:
Negative Slicing
Python range

How to find the positions that a number falls in-between in a set of sequential integers

I have an number variable and a sequential set of integers. As the number variable changes, I need to record what positions in the sequential set of integers the number variable falls in-between.
For example, if my set of integers is:
li = [20,21,22,23,24,25,26,27,28,29,30]
And the number variable is:
num = 22.74
My desired output would be the positions in the list that num falls in-between:
2,3
I know that li.index(22) would return the position but only if that exact item is in the list. Haven't been able to come across how to find positions if the number falls in-between items.
I imagine a for loop that compares num to each of the neighboring position items in the list by checking if num falls in between those two integers could solve it. Something like:
for x and the following item in li:
if x < num < the following item:
positionone = x
positiontwo = the following item
else:
continue
Just haven't been able to complete how this works, mainly by getting the next position of the list in replace of "the following item". Maybe there's a better way of figuring this? Any help is much appreciated! Thanks!
You are close with your basic logic. You need to iterate through the list. If you do it using the index, rather than the value, you'll be able to retrieve the positions you want.
Your basic logic is fine.
for idx in range(len(li)-1):
if li[idx] < num < li[idx+1]:
pos1, pos2 = idx, idx+1
Given that, I expect that you can solve the problem of equality as a special case, right?
I'm not going to touch the ambiguities regarding num being integer or not. But I will give you the tool needed to answer your question.
Let lo and hi respectively be the lower and upper limit of your interest. Then, to get the indices of the number between these two is done as such:
indices = [i for i, x in enumerate(li)
if lo < x < hi]
Please note that I use < twice there. If you want the lower bound to be inclusive, replace the first < with <=. Similarly, if you want the upper bound to be inclusive replace the second < with <=. Or very generally:
def indices_between(iterable, lo, hi, include_lo=False, include_hi=False):
lo_req = (lambda x: lo <= x) if include_lo else (lambda x: lo < x)
hi_req = (lambda x: x <= hi) if include_hi else (lambda x: x < hi)
return [i for i, x in enumerate(iterable)
if lo_req(x) and hi_req(x)]
You can compare your number with each element and break loop if current element greater than your number:
for index, current_element in enumerate(li):
if current_element > num:
position_one, position_two = index - 1 if index else index, index
break
else:
position_one, position_two = index, index
A mathematical way of doing this is to subtract the number from the list and check for the two minimum absolute value. If li is a numpy array you can do this in one line:
li = np.array(li)
interval = sorted(np.abs(li-num).argsort()[:2])

Recursion and List Mutation

I am restrict myself to mutate a list using recursion only, I have a few small troubles when doing so.
def expand_strings(L,s,i):
if len(L) == 0:
return
else:
if len(L[0]) - 1 <= i:
L[0] += s
elif len(L[0]) - 1 > i:
new_string = L[0][:i] + s + L[0][i:]
L[0] = new_string
expand_strings(L[1:], s, i)
return
L: The input list containing possible 1 or more strings
s: The extra portion of string that I need to "insert" or "append" to the string elements within the list
i: The index of string where I want to insert or append s to.
The main goal of this function is the following:
1. if the index i within the range 0 ~ len(string_element_in_list), then I insert my s starting from index i
2. if the index i is larger than what the current string length, then I do the append s.
The problems I am having right now is that: I notice the recursion will only mutate the first element within the input list, and every element after the first element, they won't be affected by the mutation, I figure it might have something to do with the new input list I pass to the recursion, but I don't know precisely why this doesn't work.
Thanks for helping in advance. :)
The problem is in the recursive call expand_strings(L[1:], s, i). When you use slicing to get a part of your list, python creates a whole new copy of that sublist. So the recursive call creates a copy of your list, except the first element, and works on that copy.
One way of solving this problem can be returning the modified list from your method:
def expand_strings(L,s,i):
if len(L) == 0:
return []
else:
if len(L[0]) - 1 <= i:
L[0] += s
elif len(L[0]) - 1 > i:
new_string = L[0][:i] + s + L[0][i:]
L[0] = new_string
return [L[0]] + expand_strings(L[1:], s, i)
If you don't want to create a copy of the sublist every time (and return the modified list), you can add one more parameter to your method that would specify the location of the first element to modify. The base case would be where the starting index is equal to the length of the list.
def expand_strings(L,s,i,start):
if start == len(L):
return
if len(L[start]) - 1 <= i:
L[start] += s
else:
L[start] = L[start][:i] + s + L[start][i:]
expand_strings(L, s, i, start + 1)

Negative number finder in index and occuring

Write a func/on first_neg that takes a (possibly empty) list of
numbers as input parameter, finds the first occurrence of a
nega/ve number, and returns the index (i.e. the posi/on in the
list) of that number. If the list contains no nega/ve numbers or it
is empty, the program should return None. Use while loop (and
not for loop) and your while loop should stop looping once the
first nega/ve number is found.
This is the question my teacher asked me any ideas this what i did:
def first_neg(list):
count = 0
for number in list:
if number < 0:
count += 1
return count
Dosent seem to work properly i just joined 1st post hope can get some help
x = [1,2,3,-5]
def first_neg(list):
count = 0
for number in list:
count += 1 #moved it outside of the if
if number < 0:
return count
print(first_neg(x)) #prints 4
You want to increment count not when you've found the answer but everytime the forloops loops. Note that this method returns 4 which is the fourth item in the list, not the index, Index of the list starts from 0 so to access it would be 3. Take our list x = [1,2,3,-5], -5 is in the fourth slot of the list, but to access it we have to call x[3] since lists starts at 0 indexing.
If you want to return the index of the list where the first negative number is found try this:
x = [1,2,3,-5]
def first_neg(list):
for count, number in enumerate(list):
if number < 0:
return count
print(first_neg(x)) # prints 3
This is because enumerate creates a "pairing" of the item in the list and it's the current count. Enumerate just counts from 0 everytime it gets an item out of the list.
Also as a side note ( I didn't change it in my answer since I wanted you to understand what's going on ). Don't name your variables keywords like list, tuple, int, str... Just a bad idea and habit, it works as you can see but it can cause issues.
Return the index immediately once you encounter the negative element. Increment the index otherwise:
def first_neg(lst):
count = 0
while count < len(lst):
if lst[count] < 0:
return count
count = count + 1
return None
Note : Better if you use enumerate() rather than using extra count variable. The code you mentioned is not written in pythonic way.
You may try this as well:
def first_neg(lst):
res = [i for i,x in enumerate(lst) if x<0]
return None if res == [] else res[0]
The code above can be improved using generators as suggested by #Chris_Rands.

Categories