How to solve problem with the items in a list - python

Question originally posted in Spanish, on es.stackoverflow.com, by Ezequiel:
I am trying to make a program that removes certain elements from a
group of lists if a certain condition passes, and that duplicates them
if a certain other happens. But I have the following problem: when
the program is in iteration 50 of 100, it marks IndexError and
closes.
Here is the code snippet in which I have the problem:
nnl_len = len(neuralNetworkList) # longitud de lista de objetos
fitl_copy = fitness # lista de floats
best_fitness_reproduced = 0
if worst_fitness < 0 and first_iteration == 0: # I guess this can
worst_fitness = 0 # be ignored
for i in range(nnl_len):
print(i)
if fitl_copy[i] < best_fitness: # EXACT LINE OF THE ERROR <------------------
print("I DIED WITH A FITNESS OF ",fitness[i], ", BEING THE LIMIT ",best_fitness)
neuralNetworkList.pop(i)
x.pop(i)
y.pop(i)
fitness.pop(i)
colors.pop(i)
elif fitl_copy[i] == best_fitness and best_fitness_reproduced:
print("I DIED BECAUSE A TOP FITNESS CREATURE ALREADY REPRODUCED ",fitness[i])
neuralNetworkList.pop(i)
x.pop(i)
y.pop(i)
fitness.pop(i)
colors.pop(i)
else:
best_fitness_reproduced = 1
for j in range(99): # plus the mother is 100
print("I SURVIVED WITH A FITNESS OF ",fitness[i], ", BEING THE LIMIT ",best_fitness)
neuralNetworkList.append(neuralNetworkList[i])
if random.randint(1,3) == 1:
neuralNetworkList[i].mutate(i)
x.append(width)
y.append(height)
fitness.append(0)
newcolor = []
for h in range(3):
newcolor.append(round( colors[i][h]*random.choice((0.9, 1.1)) ))
colors.append(newcolor)
#except IndexError:
# pass
# print("I NEITHER DIED NOR REPRODUCED BECAUSE OF AN INDEX ERROR")
nnl_len = len(neuralNetworkList)
for i in range(nnl_len):
x[i] = width
y[i] = height
fitness[i] = 0
print("population after reproduction:", len(neuralNetworkList))
this is the traceback:
Traceback (most recent call last):
File "C:\Users\Ezequiel\Desktop\Archivos pavos\Sublime Text 3\pruebas_phyton.pyw", line 4921, in <module>
if fitl_copy[i] < best_fitness: # fitness[i] <= worst_fitness*4
IndexError: list index out of range

when the program is in iteration 50 of 100, it marks IndexError and closes.
That is the clue; the problem happens halfway through the process. This is a sign of trying to remove from a list while iterating over it; each time through the loop you remove an element, so after 50 times you have i == 50 and only 50 elements still in the list, so the index is out of bounds.
Why does this happen? Well...
fitl_copy = fitness # lista de floats
This does not make a copy of fitness; it makes another name for fitness.
Consequently, these two lines...
if fitl_copy[i] < best_fitness:
# ...
fitness.pop(i)
operate on the same list; and boom.
I think you should first try to rethink your algorithm. My guess is that you don't really need to want to do all this popping from lists at all. I think you will have a much easier time if you:
Rewrite so that you produce a new list with all of the creatures for the new generation, starting from scratch (empty list)
Leave the old creature list alone while you do this, and then just replace it with the new list at the end
You may also benefit from using some kind of structured data for your creatures, instead of having parallel lists with all their attributes; and also by using list comprehensions and generators instead of for-loops.

Related

Python how do i make list appends / extends quicker?

Heyo all.
Trying to get better at python and started doing leetcode problems.
Im currently doing one, were the goal is to capture water.
Link => https://leetcode.com/problems/trapping-rain-water/
problem is; it times me out for taking too long. My code is certainly inefficient. Afer googling around i found that .append is supposedly very slow / inefficient. So is .extend.
Cant find any obvious ways of making my code faster; hence my arrival here.
any response is much appreciated
class Solution:
def trap(self, height: List[int]) -> int:
max_height = max(height)
water_blocks = 0
for element in range(max_height):
local_map = []
element = element + 1
for block in height:
if block >= element:
local_map.extend([1])
else:
local_map.extend([0])
if local_map.count(1) > 1:
first_index = local_map.index(1)
reversed_list = local_map[::-1]
last_index = len(local_map) - 1 - reversed_list.index(1)
water_count = last_index - first_index - 1 - (local_map.count(1) - 2)
water_blocks += water_count
else:
continue
return water_blocks
Although many of your count and index calls can be avoided, the two big nested loops might still be a problem. For the outer loop, max_height can be large number and the inner loop iterates over the full list. You might need to come up with a different algorithm.
I don't have a leetcode account, so I can't really test my code, but this would be my suggestion: It iterates over the height-list only once, with a small inner loop to find the next matching wall.
class Solution:
def trap(self, h):
water = 0
current_height = 0
for i, n in enumerate(h):
# found a "bucket", add water
if n < current_height:
water += current_height - n
else: # found a wall. calculate usable height
current_height = self.n_or_max(h[i+1:], n)
return water
def n_or_max(self, h, n):
local_max = 0
for i in h:
if i > local_max:
local_max = i
# that's high enough, return n
if i >= n:
return n
return local_max
Here are some pointers:
Do not use list.count() or list.index() (that is, try to remove local_map.count(1), local_map.index(1) and reversed_list.index(1)). The first will loop (internally) over the whole list, which is obviously expensive if the list is large. The second will loop over the list until a 1 is found. Currently you even have two calls to local_map.count(1) which will always return the same answer, so at least just store the result in a variable. In your loop over blocks, you construct local_map yourself, so you do in fact know exactly what it contains, you should not have to search through it afterwards. Just put a few ifs into the first loop over blocks.
The operation local_map[::-1] not only runs over the whole list, but additionally copies the whole thing into a new list (backwards, but that's not really contributing to the issue). Again, this new list does not contain new information, so you can figure out the value of water_count without doing this.
The above is really the major issues. A slight further optimization can be obtained by eliminating element = element + 1. Just shift the range, as in range(1, max_height + 1).
Also, as written in the comments, prefer list.append(x) to list.extend([x]). It's not huge, but the latter has to create an additional list (of length 1), put x into it, loop over the list and append its elements (just x) to the large list. Finally, the length-1 list is thrown away. On the contrary, list.append(x) just appends x to the list, no temporary length-1 list needed.
Note that list.append() is not slow. It's a function call, which is always somewhat slow, but the actual data operation is fast: constant time and even cleverly amortized, as juanpa.arrivillaga writes.
Here's another way of looking at the problem. This scans left to right over the bins, and at each point, I track how many units of water are dammed up at each level. When there's a tall wall, we tally up whatever units it was damming, and clear them. However, this still gets an "overtime" flag on the next to the last test, which has about 10,000 entries. It takes 20 seconds on my relatively old box.
class Solution():
def trap(self, height):
trapped = 0
accum = [0]*max(height)
lastwall = -1
for h in height:
# Take credit for everything up to our height.
trapped += sum(accum[0:h])
accum[0:h] = [0]*h
for v in range(h,lastwall):
accum[v] += 1
lastwall = max(lastwall,h)
return trapped
print(Solution().trap([0,1,0,2,1,0,1,3,2,1,2,1])) # 6
print(Solution().trap([4,2,0,3,2,5])) # 9

From while loop to ValueError

I want integer input for the variable x, space separated. User input example 20 15 7 5 4 2. This list should be entered into x and then split into two parts. The parts should be then substracted with each other and output the smallest possible difference. The below code splits the input already partially into to lists but it does not complete it. I thought I would either create a while loop encapsulating all if-statements which however gives me the following error.
Error message
Traceback (most recent call last):
File "C:/Users/Array Partitioning.py", line 28, in <module>
arrpartitioning(input().split())
File "C:/Users/Array Partitioning.py", line 18, in arrpartitioning
b.append(max(x))
ValueError: max() arg is an empty sequence
I assume the while statement does not stop and attempts after a while to loop over an empty list of variable x. Initially, I thought to check the length of the x variable prior starting the while loop again but that does not work. The if-statement in the second while-loop also does not help even if I indent it to include it in the while loop.
# User input, space separated
ui = input()
x = list(map(int, ui)
half = sum(x)//2
# two empty lists to enter the x-values
a = []
b = []
flag = False
# while len(x) != 0:
# while loop to divide the values
while flag == False:
if sum(a) < half:
a.append(max(x))
x.remove(max(x))
while sum(b) < sum(a):
b.append(max(x))
x.remove(max(x))
# Same error message even if I indent the if-statement to the while-block
if len(x) == 0:
flag == True
Can somebody please explain me first, whether the issue is here my while loop and if so, second, how I can exit the while loop once there are no values in x anymore?
you need to add a condition so it exits the loop when x is no longer valid:
# User input, space separated
ui = input()
x = list(map(int, ui.split(' ')))
half = sum(x)//2
# two empty lists to enter the x-values
a = []
b = []
# while len(x) != 0:
# while loop to divide the values
while len(x) > 1:
if sum(a) < half:
a.append(max(x))
x.remove(max(x))
while sum(b) < sum(a):
b.append(max(x))
x.remove(max(x))
# check last element independently
if sum(b) < sum(a) < half:
b.append(x.pop())
else:
a.append(x.pop())
print(x)
print(a)
print(b)
You need to split the user input into separate strings before mapping int onto it. Otherwise, it tries to convert the whole string of 20 15 7 5 4 2 into one integer value. To fix this, try adding a split() to the map input to convert it to a list of strings:
x = list(map(int, ui.split())
EDIT: I was mainly pointing out the problem that caused the error, but the bigger problem is probably the infinite loop as mentioned above.

IndexError: list index out of range with a while loop [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 2 years ago.
I've been trying to fix this but the same error message came up every time:
while number_list[i] <= number_list[j]:
IndexError: list index out of range
I've searched for the same type of bug, but not very similar cases found.
Here it is the head program (orders the numbers of my list, from the little one to the bigger):
number_list=[]
list_lenght=int(input("List lenght: "))
while len(number_list)<list_lenght:
item=input("Enter new item to the list:")
number_list.append(item)
print(number_list)
print("That's your number list: ",number_list)
number_list_final=[]
def order_number_list(number_list):
i=0
j=1
while (j)<=(len(number_list)-1):
while number_list[i]<=number_list[j]:
j=j+1
i=j
j=i+1
final_item=number_list[i]
number_list_final.append(final_item)`
del number_list[i]
order_number_list(number_list)
order_number_list(number_list)
print(number_list_final)
I know this is about iterating with the list while modifying it, but no idea how to fix it.
Can anyone help me to debug this, or give me some tips?
Thank you!
number_list=[]
list_lenght=int(input("List length: "))
while len(number_list)<list_lenght:
item=input("Enter new item to the list:")
number_list.append(int(item))
print(number_list)
print("That's your number list: ",number_list)
number_list_final=[]
def order_number_list(number_list):
current_low = ["number"]
current_low[0] = number_list[0]
x = 1
current_low_pos = 0
while x < len(number_list):
if current_low[0] > number_list[x]:
current_low[0] = number_list[x]
current_low_pos = x
x = x + 1
del number_list[current_low_pos]
if number_list == []:
remaining_list = []
else:
remaining_list = order_number_list(number_list)
return (current_low + remaining_list)
number_list_final = order_number_list(number_list)
print(number_list_final)
This is code that has been clarified and corrected.
j was not working right, as other answers have pointed out.
number_list.append(item)
needed to be changed to:
number_list.append(int(item))
because you cant compare strings with the '<' operator.
I also added a return statement, added this line of code:
number_list_final = order_number_list(number_list)
because your final print statement would print an empty list because:
order_number_list(number_list)
doesn't change the variable number_list_final.
I changed some of the function code to simply and make it more readable.
It also explicitly sets the current lowest item to be the first, and changes that if a lower item is found later on.
Hope this helps, let me know if you have any questions!
If I understand you correctly, you're trying to sort a list of numbers. There's a built-in Python method for this, that has been optimized for ages to be fast and use minimal memory, it's called sorted.
Instead of running order_number_list, try:
print(sorted(number_list))
If this is some kind of homework, and you're not allowed to use the built-in sorted, for educational purposes I would recommend you to use a sorting algorithm called bubble sort, more information here - Bubble Sort Homework.
Updating your code to include a couple of print statements shows that the j increases to past the length of the list. Then the "while number_list[i]<=number_list[j]:" no longer can be completed as there is no "number_list[j]" for the j value.
number_list=[1,2,3,4,5]
list_lenght=5
#=int(input("List lenght: "))
#while len(number_list)<list_lenght:
# item=input("Enter new item to the list:")
# number_list.append(item)
# print(number_list)
print("That's your number list: ",number_list)
number_list_final=[]
def order_number_list(number_list):
i=0
j=1
while (j)<=(len(number_list)):
while number_list[i]<=number_list[j]:
print (j)
print (i)
j=j+1
i=j
j=i+1
final_item=number_list[i]
number_list_final.append(final_item)
del number_list[i]
order_number_list(number_list)
order_number_list(number_list)
print(number_list_final)
This outputs :
That's your number list: [1, 2, 3, 4, 5]
1
0
2
0
3
0
4
0
Traceback (most recent call last):
File "C:/Python3/numberlist.py", line 30, in <module>
order_number_list(number_list)
File "C:/Python3/numberlist.py", line 19, in order_number_list
while number_list[i]<=number_list[j]:
IndexError: list index out of range
The code is falling over once j is 5 for this example, it is trying to compare to a number that doesnt exist

List Index Out of Range in Python; Iteration

I am using the simple program below to see how long an iterative process takes to terminate. However, in line 15, I cannot figure out why I am getting index out range error.
An example of what I am trying to count is the number of steps it takes for the following example iteration: User inputs 4 and then 1234. Then we have: [1,2,3,4] --> [1,1,1,1] --> [0,0,0,0] and then termination. 2 steps is required to get to [0,0,0,0]. I have proven that for the values of n that I am inserting, the system goes to [0,0,0,0] eventually.
import math
index = input("Enter length: ")
n = int(index)
game = input("Enter Coordinates of length n as a number: ")
s = list(game)
Game = []
for k in s:
Game.append(int(k))
l = len(game)
while sum(Game) > 0:
Iteration = []
k = 0
j = 0
while j < l-1:
Iteration.append(math.fabs(Game[j]-Game[j+1])) # line 15
j = j+1
k = k+1
Game = Iteration
print(k)
Game = Iteration is probably why. When j = 1, Game will be a list with only one item because of that. Then, Game[1]-Game[2] will be out of bounds.
Your code is written in a very un-Pythonic style that suggests you're translating directly from C code. (Also, you should basically never use input(); it's insecure because it evaluates arbitrarily user-entered Python code! Use raw_input() instead.)
If you rewrite it in a more Pythonic style, it becomes clear what the problem is:
import math
# you don't do anything with this value, but okay
s = index = int(raw_input("Enter length: "))
# game/Game naming will lead to confusion in longer code
game = raw_input("Enter Coordinates of length n as a list of comma-separated numbers: ")
Game = [int(k) for k in game.split(',')]
l = len(Game)
while sum(Game) > 0:
Game = [math.fabs(Game[j]-Game[j+1]) for j in range(l-1)] # problem here
# no idea what k is for, but it's not used in the loop anywhere
The problem is that in every iteration through your inner while loop, or the line marked # problem here in my version, your Game list gets shorter by one element! So on the second time through the outer while loop, it reads an element past the end of Game.
I have no idea what this code is trying to do, so I can't really suggest a fix, but if you truly intend to shorten the list on every pass, then you of course need to account for its shorter length by putting l=len(Game) inside the while loop.

List Index Out Of Range : PYTHON

Even though I am getting valid Prints but still I am getting List Index out of range. The list where I am getting "Out of Range" error is "lstSHADOW_LOG_TABLE"
if((int(len(lstSHADOW_LOG_TABLE[index_shadow_log][1]))) > 1):
print("Length={0} and Value={1}".format(len(lstSHADOW_LOG_TABLE[index_shadow_log][1]), lstSHADOW_LOG_TABLE[index_shadow_log][1]))
for l_inner_element in (lstSHADOW_LOG_TABLE[index_shadow_log:][1]):
if(lstSHADOW_LOG_TABLE[index_shadow_log][1] == lstCAN_LOG_TABLE[index_can_log][1]):
#Some Calculation
else:
break
OUTPUT:
Length=3 and Value=340
Traceback (most recent call last):
for l_inner_element in (lstSHADOW_LOG_TABLE[index_shadow_log:][1]):
IndexError: list index out of range
EDITED FROM HERE (CODE MODIFIED TO INCORPORATE SUGGESTION):
The List "lstSHADOW_LOG_TABLE" is a List of Lists. Now let us say I want the comparison to start fresh from index "index_shadow_log" (SubList "index_shadow_log" onwards)
for l_inner_element in (lstSHADOW_LOG_TABLE[index_shadow_log:]):
Thanks for your answers, I now understood that the meaning of this for loop would be start iteration for List "lstSHADOW_LOG_TABLE" starting from index "index_shadow_log:"
This is my extracted code:
for index in range(len(lstCAN_LOG_TABLE)):
for l_index in range(len(lstSHADOW_LOG_TABLE)):
#print(lstSHADOW_LOG_TABLE[l_index][0])
#print(lstSHADOW_LOG_TABLE[l_index][1])
if(lstSHADOW_LOG_TABLE[l_index][1] == lstCAN_LOG_TABLE[index][1]): #Consider for comparison only CAN IDs
print("matching")
#print(lstCAN_LOG_TABLE[index][0])
#print(lstSHADOW_LOG_TABLE[l_index][0])
index_can_log = index #Position where CAN Log is to be compared
index_shadow_log = l_index #Position from where CAN Shadow Log is to be considered
print("Length={0} and Value={1}".format(len(lstSHADOW_LOG_TABLE[index_shadow_log][1]), lstSHADOW_LOG_TABLE[index_shadow_log][1]))
bMatchFound = 1
for l_inner_element in (lstSHADOW_LOG_TABLE[index_shadow_log:]): #Start comparison
if(lstSHADOW_LOG_TABLE[index_shadow_log][1] == lstCAN_LOG_TABLE[index_can_log][1]): #Compare individual element
dump_file.write("\n")
dump_file.write("SHADOW: " + str(lstSHADOW_LOG_TABLE[index_shadow_log])) #Dump if equal
writer_two.writerow(lstSHADOW_LOG_TABLE[index_shadow_log][0]) #Update CSV File
dump_file.write("\n")
dump_file.write("CAN: " + str(lstCAN_LOG_TABLE[index_can_log])) #Dump if equal
writer_one.writerow(lstCAN_LOG_TABLE[index_can_log][0]) #Update CSV File
if(index_can_log < (input_file_one_row_count - 1)): #Update CAN LOG Index
index_can_log = index_can_log + 1
if(index_can_log >= (input_file_one_row_count - 1)):
break
else:
bMatchFound = 0
break
if(bMatchFound == 0):
break
dump_file.close()
I need to get rid of parenthesis (Sorry coming from C/C++ background we love braces and parenthesis :-P) and make the code a lot cleaner. Thanks all for your suggestions
Compare:
lstSHADOW_LOG_TABLE[index_shadow_log][1]
with
lstSHADOW_LOG_TABLE[index_shadow_log:][1]
The first indexes lstSHADOW_LOG_TABLE, then indexes whatever that returned. The second slices lstSHADOW_LOG_TABLE; this returns a new list. You then indexed that sliced list. If that sliced list has only 1 element, then indexing the second is going to fail.
You really need to cut back on the parentheses here, and simplify the code somewhat. Use a temporary variable to store the indexed element:
value = lstSHADOW_LOG_TABLE[index_shadow_log][1]
if value:
print("Length={0} and Value={1}".format(len(value), value))
for l_inner_element in value:
if value == lstCAN_LOG_TABLE[index_can_log][1]:
#Some Calculation
else:
break

Categories