Why is this going out of range? - python

I am getting a IndexError: list index out of range error. I'm not sure why. Any advice?
The code is trying to see if a list of numbers is an arithmatic progression, in this case every number is added by 2.
def is_arith_progession(num_list):
delta = num_list[1] - num_list[0]
for num in num_list:
if not (num_list[num + 1] - num_list[num] == delta):
return False
else:
return True
print(is_arith_progession([2, 4, 6, 8, 10]))

You are trying access 5th element of num_list array in the second iteration of for loop. After the first iteration num becomes 4, so program crashes when it tries to evaluate num_list[num + 1].
num variable holds the actual element in the list. It is not index to element.
To iterate over indices, you may try for num in range(len(num_list) - 1) which should solve the issue. (Note -1 in the paranthesis)

This:
for num in num_list:
if not (num_list[num + 1] - num_list[num] == delta):
return False
almost certainly doesn't do what you think it does. When you define for num in num_list:, this means that num is an item from the list num_list. num is NOT an index. So, if your list is [2, 4, 6, 8, 10], you go out of bounds when num is 4 (i.e. the second item in your list), because your input list is only length 5 and you try to access index num+1, which is 5 (indexes are 0 based, so 5 is out of bounds)
You probably want something like this:
# Start at index 1, or you'll always return false since delta == index1 - index0
for index in range(1, len(num_list)-1):
if not (num_list[num + 1] - num_list[num] == delta):
return False
or the more pythonic (note there are no indices):
# Again start at index1, zip will handle the edge case of ending nicely so we don't go OB
for num, next_num in zip(num_list[1:], num_list[2:]):
if not (next_num - num == delta):
return False

You are iterating over the values, not the indexes of the array. So, num_list[num] can be out of range. Since you refer to the i+1 element, iterate up to i < n-1
for i, _ in enumerate(num_list[:-1]):
if num_list[i+1] - num_list[i]...

2 things:
num is an element of num_list, not just an index. Getting an index would be for num in range(len(num_list)):, you're effectively calling num_list[num_list[i]];
Even if it was an index, for the last index num in array you are calling numlist[num+1], which is out of array bounds as num is already last;
Do for INDEX in range(len(num_list)-1): and if not (num_list[INDEX + 1] - num_list[INDEX] == delta):. That should do it.

Related

Smallest Missing Number

I'd like some insight on the following code. The problem says, devise an algorithm that finds the smallest missing number in an array
My Approach:
def small(arr: list) -> int:
s = {*arr}
i = 1
while True:
if i not in s:
return i
i += 1
Easy right?
The problem is this uses space complexity of (n) when I create that extra set
Better Approach:
# Linear time routine for partitioning step of Quicksort
def partition(arr):
pIndex = 0
# each time we find a positive number, `pIndex` is incremented, and
# that element would be placed before the pivot
for i in range(len(arr)):
if arr[i] > 0: # pivot is 0:
arr[i], arr[pIndex] = arr[pIndex], arr[i]
pIndex += 1
# return index of the first non-positive number
return pIndex
# Function to find the smallest missing positive number from an unsorted array
def findSmallestMissing(arr, n):
# Case 1. The missing number is in range 1 to `n`
# do for each array element
for i in range(n):
# get the value of the current element
val = abs(arr[i])
# make element at index `val-1` negative if it is positive
if val - 1 < n and arr[val - 1] >= 0:
arr[val - 1] = -arr[val - 1]
# check for missing numbers from 1 to `n`
for i in range(n):
if arr[i] > 0:
return i + 1
# Case 2. If numbers from 1 to `n` are present in the array,
# then the missing number is `n+1` e.g. [1, 2, 3, 4] —> 5
return n + 1
if __name__ == '__main__':
arr = [1, 4, 2, -1, 6, 5]
k = partition(arr)
print("The smallest positive missing number is",
findSmallestMissing(arr, k))
I don't understand why do we need
if val - 1 < n and arr[val - 1] >= 0:
arr[val - 1] = -arr[val - 1]
findSmallestMissing is really very similar to your set-based solution. The difference is that it uses the sign-bit in the input array as that set. That sign has nothing to do with the value that is stored at the same spot, but with the index. If the sign bit is set it means: I have encountered a value that is this index (after translating a value to an index by subtracting one).
The code you asked about:
if val - 1 < n and arr[val - 1] >= 0:
arr[val - 1] = -arr[val - 1]
First of all, the subtraction of one is there to covert a 1-based value to a 0-based index.
Since we use the array also as a set, this code first checks that the index is in range (of the "set") and then it checks that this index is not yet in the set, i.e. the sign bit at that index is still 0. If so, we add the index to the set, i.e. we set the sign bit at that index to 1.
All in all, one may argue that we cheat here: it is as if we didn't allocate extra memory for maintaining a set, but in reality we used an unused bit. In theory this means we still use extra memory, while in practice that memory was already allocated, but not used. The algorithm assumes that the none of the values in the array are negative.

Index out of Range despite check

t = [1, 2, 3]
def cumsum(t):
t2 = []
total = 0
i = 0
while i < len(t):
total += t[i]
t2[i].append(total)
i += 1
return t2
cumsum(t)
This code takes the sum of the first two list integers and appends it to another list.
I feel like this should logically work and I don't understand why it is producing an index error if i < len(t) when len(t)= 3. So ideally t2 =[1, 3, 6]
while the iterator is less than len(t) (which is 3) add the list item to the total variable then append the total to the new list then iterate.
Because you are using index i to access t2 list that is empty. To append an element to a list you should use <list>.append(<element>), that is t2.append(total) in your case.

Assigning elements to a list using for loop?

I have to figure out which is larger, the first or last element in a given list, and set all the other elements to be that value.
I wrote a code using a for loop, but I get an error that the list index is out of range. What is my mistake?
The code is below:
def max_end3(nums):
for i in nums:
if (nums[0] > nums[len(nums)-1]):
nums[i] = nums[0]
else:
nums[i] = nums[len(nums)-1]
return (nums)
Here is an option:
the_list = [6, 7, 2, 4, 5, 7]
val = max([the_list[0], the_list[-1])
the_list = [val]*len(the_list)
You've confused an index with a list element. Consider the input list [17, 3, -42, -3]. Your for statement steps i through the values 17, 3, -42, and -3, in that order. However, the assignments you do assume that i is taking on the position or index values, 0, 1, 2, 3.
Try this, instead:
def max_end3(nums):
for i in range(len(nums)):
if (nums[0] > nums[len(nums)-1]):
nums[i] = nums[0]
You do not have to do for loop since 'nums is already a list. I believe coding bat is giving array length is 3. So try this
def max_end3(nums):
if nums[0] > nums[-1]:
return nums[:1] *3
elif nums[-1] > nums[0]:
return nums[-1:] * 3
elif nums[0] == nums[-1]:
return nums[:1] * 3
else:
return nums
for i in nums / nums[i] is not the proper way to index the list nums. I think you can try this instead:
def max_end3(nums):
for i,num in enumerate(nums):
if (nums[0] > nums[-1]):
nums[i] = nums[0]
else:
nums[i] = nums[-1]
return (nums)
Use the built-in enumerate function to return "a tuple containing a count (from start which defaults to 0) and the values obtained from iterating over sequence." This allows you to concurrently index & iterate the values.
Use nums[-1] to represent the last item in the list, since Python supports negative indexing :)
Note: other solutions are more elegant, and this is not how I would solve the problem, but for a beginner it is sometimes easier to understand if working with simple constructs like for loop.
To replace each element in the given list I would pre-compute the wanted value, and then assign it to each element, thus:
def max_end3(nums):
larger = max((nums[0], nums[-1]))
for i in range(len(nums)):
nums[i] = larger
You could alternatively leave the original list unchanged, and generate a new list of the wanted values. If the new list is assigned to the same name as the old one, it will have a similar effect, eg.
nums = [max((nums[0], nums[-1]))] * len(nums)

python - checking if an array consisting of N integers is a permutation

I am analyzing the routine which checks if an array of N integers is a permutation (sequence containing each element from 1 to N).
I am new to python. I can't grasp how this routine gets the correct answer. Could anybody explain the logic behind the loop? especially the use of the counter[element-1].
Is the counter a built-in function working on every element of A? does the counter[element-1] reference position/value of elements of A by default because the loop is defined on an array?
A=[4,1,3,2]
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return 0
else:
if counter[element-1] != 0:
return 0
else:
counter[element-1] = 1
return 1
Update:
I modified the code to see the values used within the loop, for example
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
print element
print 'outside'
return 0
else:
if counter[element-1] != 0:
print 'element %d' % element
print [element-1]
print counter[element-1]
return 0
else:
counter[element-1] = 1
print 'element %d' % element
print [element-1]
print counter[element-1]
return 1
gives me
element 4
[3]
1
element 1
[0]
1
element 3
[2]
1
element 2
[1]
1
1
I still don't get the logic. For example fot the first element, why [3] gives 1?
The idea behind the code is twofold. A permutation of the list [1, 2, ..., N] has two properties. It has only elements between 1 and N and each element just appear one time in the list.
I will try explain it to you part by part this idea in the code.
def solution(A):
counter = [0]*len(A)
limit = len(A)
Assume as an example, a list [1, 3, 2].
counter is initialized as a list of zeros of size len(A) = 3. Each 0 correspond to one of the elements of the list
for element in A:
if not 1 <= element <= limit:
return 0
This part condition is the most easy one. If the element is not in this range, the list cannot be a permutation of [1, 2,...N]. For instance, [1, 3, 2] is a permutation of [1, 2, 3] but [1, 6, 2] is not.
else:
if counter[element-1] != 0:
return 0
else:
counter[element-1] = 1
This next part is related with the uniqueness of each term. The if checks if a number = element has already passed through this loop. The second else make sure that this number is marked, so if a repeated number is found in the next iterations, the if will be true and return 0.
For instance, for the list [1, 2, 2]. The first 2 would not trigger the if, while the second 2 would trigger it, returning 0. On the other hand, [1, 3, 2], would never trigger the if.
If all the number pass this conditions, the two properties were true and the list is a permutation.
Quite a cunning algorithm actually.
The input is a sequence of length N.
Each element of input is presumed to be an integer (if not, either comparison or indexing will throw an exception).
counter is an array of flags - of length N, too.
No integers outside of [1,N] range are allowed
No duplicates are allowed (see how it's done)
Can you now prove that the only way for both conditions to stay true is for the sequence to be a permutation?

Check list lst if it's a geometric sequence in python

I have,
def geometric(lst):
'checks whether the integers in list lst form a geometric sequence'
for i in range(1, len(lst) - 1):
if lst[i] * 2 == "don't know what to check here":
return True
else:
return False
I'm not sure how to apply that to check if all the indices. Would prefer this done within a for-loop. any help would be appreciated!
I think this works:
def geometric(lst):
for i in range(len(lst) - 2):
if lst[i] * lst[i + 2] != lst[i + 1] ** 2:
return False
return True
This is based on the idea that for any three consecutive terms a, b, c, the ratio between the first two must equal the ratio between the last two, i.e. b/a = c/b. Rearranging this gives a * c == b ** 2. It's much better to use the form without division, because division introduces rounding errors. This function even works if the common ratio is not an integer (e.g. [4, 6, 9]).
Edit
The above answer does not handle lists containing 0 correctly.
A correct version is:
def geometric(lst):
for i in range(len(lst) - 2):
if lst[i] * lst[i + 2] != lst[i + 1] ** 2:
return False
for i in range(len(lst) - 1):
if lst[i] == 0 and lst[i + 1] != 0:
return False
return True
This returns True for [1, 0, 0] (common ratio 0) but False for [0, 0, 1]. The original version returned True for both.
It may be better to disallow common ration 0. This would mean changing the second loop to return False for any list of length 2 or more containing 0.
As sam2090 has pointed out, the right side of the condition in line 4 has been left empty. If the common ratio of the geometric sequence is 2 (maybe it's something that you would actually want to pass as a parameter of the function), then the expression should be:
if lst[i] == lst[i-1] * 2
The left-hand side of the statement refers to the current number in the itertion (i), while the right-hand side of the statement refers to the previous number (i-1) and multiplies its value by 2.
Also, in the foor loop, be sure you iterate until the last element in the list, so you should write:
for i in range(1, len(lst)):
and not
for i in range(1, len(lst) - 1):

Categories

Resources