Hi I'm running this code in Anaconda Python 3.9. Problem is it orints just fine in the for loop all the elemtns, but in the if and else statement it does not. It printed 1.422 85 which is correct (means it also accesed the last element) then procceded to give the error "IndexError: list index out of range" for my else statement. When right beofre it. printed the same array with almost identical code
How do i fix this ?
sr = [(5001, 1.17135, 2), (5003, 1.14463, 1), (5028, 1.24852, 2), (5031, 1.23169, 1), (5039, 1.24726, 2), (5050, 1.2475, 2), (5065, 1.21645, 1), (5068, 1.23839, 2), (5082, 1.24832, 2), (5100, 1.26471, 2), (5104, 1.24362, 1), (5122, 1.2416, 1), (5129, 1.2247, 1), (5151, 1.24543, 2), (5171, 1.26426, 2), (5182, 1.2405, 1), (5221, 1.22509, 1), (5224, 1.23396, 2), (5230, 1.21654, 1), (5261, 1.22493, 2), (5279, 1.23628, 2), (5285, 1.2204, 1), (5290, 1.22335, 1), (5335, 1.27552, 2), (5354, 1.25449, 1), (5367, 1.26872, 2), (5385, 1.24557, 2), (5389, 1.2335, 1), (5401, 1.25425, 2), (5408, 1.24037, 1), (5423, 1.22509, 1), (5440, 1.25296, 2), (5471, 1.26693, 2), (5495, 1.26492, 2), (5550, 1.29521, 2), (5556, 1.3013, 2), (5567, 1.31699, 2), (5573, 1.30044, 1), (5577, 1.31078, 2), (5597, 1.3009, 1), (5603, 1.31029, 2), (5614, 1.30047, 1), (5621, 1.31246, 2), (5631, 1.31213, 2), (5655, 1.32552, 2), (5657, 1.30588, 1), (5674, 1.31168, 1), (5698, 1.3482, 2), (5711, 1.32419, 1), (5736, 1.30226, 2), (5742, 1.27725, 1), (5746, 1.2865, 2), (5753, 1.29189, 2), (5756, 1.28146, 1), (5781, 1.2966, 2), (5788, 1.28666, 2), (5812, 1.29296, 2), (5819, 1.29024, 2), (5830, 1.29784, 2), (5847, 1.30067, 2), (5859, 1.29699, 2), (5872, 1.30051, 1), (5891, 1.289, 1), (5896, 1.28562, 1), (5903, 1.30241, 2), (5908, 1.29105, 1), (5908, 1.29793, 2), (5915, 1.31764, 2), (5939, 1.30794, 2), (5951, 1.28804, 1), (5993, 1.31182, 1), (5999, 1.32783, 2), (6005, 1.31909, 1), (6021, 1.32426, 2), (6064, 1.33038, 1), (6078, 1.32877, 1), (6083, 1.33845, 2), (6101, 1.35001, 2), (6107, 1.35397, 2), (6154, 1.35538, 2), (6161, 1.36245, 2), (6172, 1.31877, 1), (6190, 1.36193, 2), (6197, 1.34293, 1), (6214, 1.36863, 2), (6220, 1.37037, 2), (6221, 1.35418, 1), (6245, 1.36356, 2), (6261, 1.37008, 2), (6263, 1.36117, 1), (6279, 1.35196, 1), (6292, 1.37186, 2), (6304, 1.36356, 1), (6330, 1.37455, 2), (6333, 1.36573, 1), (6339, 1.37579, 2), (6345, 1.37105, 2), (6347, 1.36113, 1), (6350, 1.36831, 2), (6393, 1.37756, 1), (6413, 1.38296, 1), (6454, 1.38877, 1), (6466, 1.38588, 1), (6468, 1.39769, 2), (6479, 1.40165, 2), (6501, 1.38449, 1), (6511, 1.40046, 2), (6515, 1.38628, 1), (6550, 1.38763, 2), (6587, 1.37057, 1), (6593, 1.38115, 2), (6597, 1.37458, 1), (6603, 1.38514, 2), (6624, 1.37238, 1), (6658, 1.38085, 2), (6663, 1.37157, 1), (6675, 1.40087, 2), (6690, 1.38238, 1), (6699, 1.39291, 2), (6707, 1.3924, 2), (6712, 1.38611, 1), (6716, 1.39767, 2), (6731, 1.39319, 2), (6736, 1.38382, 1), (6767, 1.41662, 2), (6796, 1.422, 2), (6819, 1.41116, 1), (6825, 1.42109, 2), (6832, 1.41759, 2), (6836, 1.40914, 1), (6840, 1.42193, 2), (6879, 1.41115, 1), (6882, 1.41911, 2), (6897, 1.40733, 1), (6903, 1.41853, 2), (6916, 1.40344, 1), (6922, 1.41327, 2), (6929, 1.38959, 1), (6930, 1.38959, 1), (6942, 1.3937, 2), (6946, 1.38603, 1), (6953, 1.40013, 2), (6976, 1.3814, 1), (6996, 1.38445, 2), (7005, 1.3898, 2), (7007, 1.3773, 1), (7012, 1.38418, 2), (7018, 1.37422, 1), (7028, 1.39099, 2), (7053, 1.38618, 2), (7067, 1.35719, 1), (7117, 1.38839, 1), (7126, 1.39389, 2), (7139, 1.39489, 2), (7170, 1.3795, 1), (7176, 1.38751, 2), (7194, 1.37865, 2), (7212, 1.37321, 2), (7222, 1.36968, 1), (7225, 1.37681, 2), (7231, 1.36797, 1), (7232, 1.36797, 1), (7235, 1.37803, 2), (7251, 1.37304, 1), (7253, 1.37976, 2), (7261, 1.38453, 2), (7266, 1.38914, 2), (7294, 1.38884, 2), (7307, 1.39131, 2), (7310, 1.3792, 1), (7319, 1.37642, 1), (7332, 1.36406, 1), (7344, 1.36092, 1), (7345, 1.36092, 1), (7349, 1.37505, 2), (7361, 1.37287, 2), (7368, 1.35205, 1), (7373, 1.34117, 1), (7389, 1.35316, 1), (7391, 1.364, 2), (7397, 1.36478, 2), (7402, 1.35438, 1), (7409, 1.3638, 2), (7413, 1.35835, 1), (7426, 1.36374, 2), (7438, 1.37343, 2), (7456, 1.3834, 2), (7474, 1.38148, 2), (7486, 1.38295, 2), (7517, 1.36051, 1), (7525, 1.36979, 2), (7534, 1.34242, 1), (7539, 1.34494, 1), (7541, 1.3579, 2)]
plotlist1 = [x[1] for x in sr if x[2]==1] # stores values support
plotlist2 = [x[1] for x in sr if x[2]==2] # stores values resistance
plotlist1.sort() # sorts supports
plotlist2.sort() # sorts resistance
pipdif = 0.0005
print (len(plotlist2))
print (plotlist2)
for i in range(1,len(plotlist2)): # starting from 1 to len of resitance val
print (plotlist2[i], (i+1))
if(i>=len(plotlist2)): #once done break
break
if abs(plotlist2[i]-plotlist2[i-1])<=pipdif: # if abs val of current - [previous] is less or equal then pipdif
pop = plotlist2.pop(i) # delete curent one
print ("zone is bad", plotlist2[i],(i+1))
#print (plotlist2[i], (i+1), "-",plotlist2[i-1],(i), "=", abs(plotlist2[i]-plotlist2[i-1]), "Deleting", (plotlist2[i]),(i+1))
else:
print ("zone is valid", plotlist2[i],(i+1))
OUT PUT recieved
zone is valid 1.42193 84
1.422 85
Traceback (most recent call last):
File "/Users/Subhan109/Documents/Untitled Folder/test index.py", line 16, in <module>
print ("zone is bad", plotlist2[i],(i+1))
IndexError: list index out of range
On the line before the error occours, you call plotlist2.pop(i). This will reduce the length of the list by 1, and yet after each call, the for loop will increase i by 1.
In practice, this means that you are iterating over only every other element (skipping over odd-indexed elements). As this will be performed an amount of times equal to the length of the unpruned list, you will quickly attempt to access an invalid index.
Instead of removing elements from the list, consider constructing a secondary list with only the elements that would not have been removed. This avoids the problem of removing elements while iterating index-wise.
Edit
I'm unable to comment on #Ayush's answer (as I do not have the reputation requirement), but their solution will not work, as i will still exceed the length of the pruned list.
Edit: I fixed it the order of the print and pop needed to be reversed
for i in range(1,len(plotlist2)): # starting from 1 to len of resitance val
if(i>=len(plotlist2)): #once done break
break
if abs(plotlist2[i]-plotlist2[i-1])<=pipdif: # if abs val of current - [previous] is less or equal then pipdif
print ("zone is bad", plotlist2[i],(i+1), plotlist2[i], (i+1), "-",plotlist2[i-1],(i), "=", abs(plotlist2[i]-plotlist2[i-1]) ,"<", pipdif,(abs(plotlist2[i]-plotlist2[i-1])<=pipdif) ,"Deleting", (plotlist2[i]),(i+1))
pop = plotlist2.pop(i) # delete curent one
else:
print ("zone is valid", plotlist2[i],(i+1))
I have the following function responsible to generate a nested dictionary with integer keys that works inside a for loop, and I want to create a updated list when equal values are found:
# The number of times I want to run
n = 2
# Number of Loops
count = 0
# Init the Hash Table
castar_hash = {}
def run_discrete_time(start, path, count):
'''
Create or Update the Nested Dic, look for equal values and append
a new list based on the 'path' input
Args:
start (list)
path (list)
count (int)
Vars:
start = A simple list with one element
path = A list of Tuples
count = The atual Loop Interaction
'''
# Inserted the 0 because the "discrete time" will init at 0
path_list.insert(0, start)
time_run = list(range(0, len(path_list)+1))
hash = {t:p for t,p in zip(time_run,path_list)}
#* Create a new Dic Key
value_list = ['value', str(count)]
value = "".join(value_list)
castar_hash.update({value:hash})
print(f'\nThe time steps is: {time_run}')
print(f'The Hash table is: {castar_hash}\n')
'''
Need the code here to find the equal values
in the castar_hash and append to a updated list
'''
return updated_list
def main():
for _ in range(n):
'''
some code here who picks the start and path from a deque
already implemented (see the inputs bellow)
'''
count += 1
run_discrete_time(start, path, count)
if __name__ == '__main__':
main()
Let me explain how the function works with inputs: Considering that the loop will run 2 times (since the number of times "n" is 2), for the first call, considering the input:
run_discrete_time([4, 6], [(4, 5), (4, 4),(4, 3),(5, 3),(6, 3),
(7, 3), (8, 3), (8, 2), (8, 1),(9, 1)],
count)
The generated nested dic will be:
castar_hash = {'value1': {0:(4, 6), 1:(4, 5), 2:(4, 4), 3:(4, 3),
4:(5, 3), 5:(6, 3), 6:(7, 3), 7:(8, 3),
8:(8, 2), 9:(8, 1), 10:(9, 1)},
For the second loop with inputs:
run_discrete_time([1, 6], [(2, 6), (4, 4), (4, 6),(4, 5), (4, 4),
(4, 3), (5, 3), (6, 3), (8, 1), (8, 3), (9, 3)],
count)
The updated nest dic will now be:
castar_hash = {'value1': {0:(4, 6), 1:(4, 5), 2:(4, 4), 3:(4, 3),
4:(5, 3), 5:(6, 3), 6:(7, 3), 7:(8, 3),
8:(8, 2), 9:(8, 1), 10:(9, 1)},
'value2': {0:(1, 6), 1:(2, 6), 2:(4, 4), 3:(4, 6),
4:(4, 5), 5:(4, 4), 6:(4, 3), 7:(5, 3),
8:(6, 3), 9:(8, 1), 10:(8, 3), 11:(9,3)}}
My question is: What is the best and most efficient way to extract the equal values in the nested dics for every loop (considering that I can have more than two)? I'm struggling a lot to find a solution for that.
For example, the repeated values in the 'value2' dic is 2:(4, 4) and 9:(8, 1) (in relation to the 'value1' dic), and I would like to return a new list updated as (4,4) inserted in the index 2, and (8,1) at index 9, for example:
#The Path List of Tuples inputed at the second loop
path = [(2, 6), (4, 4), (4, 6),(4, 5), (4, 4),
(4, 3), (5, 3), (6, 3), (8, 1), (8, 3), (9, 3)]
#The New Updated Path List that I want to return since
#the method finded equals values compared to the 'value1' dic:
new_path = [(2, 6), (4, 4), (4, 4) (4, 6),(4, 5), (4, 4),
(4, 3), (5, 3), (6, 3), (8, 1), (8, 1), (8, 3),
(9, 3)]
I'm making a game of battleships. I created a Ship class to give the ships a location.
After making the class, I had to create all the instances, and I was wondering if there is a way to automate that.
Most of the program is irrelevant, but I'm leaving it in just in case it might affect whether or not it can be automated.
import random
class Ship(object):
def __init__(self, length):
self.length = length
def direction(self):
vh = random.choice(['v','h'])
return vh
def location(self):
space = []
row = random.randint(0, 10-self.length)
column = random.randint(0, 10-self.length)
if self.direction == 'v':
for x in range(self.length):
space.append(f'{column}{row+x}')
else:
for x in range(self.length):
space.append(f'{column}{row+x}')
return space
ships_amount = {
'carrier' : 1,
'battleship' : 2,
'cruiser' : 3,
'destroyer' : 4
}
ships_length = {
'carrier' : 5,
'battleship' : 4,
'cruiser' : 3,
'destroyer' : 2
}
I want to do this:
carrier1 = Ship(ships_length['carrier'])
battleship1 = Ship(ships_length['battleship'])
battleship2 = Ship(ships_length['battleship'])
cruiser1 = Ship(ships_length['cruiser'])
cruiser2 = Ship(ships_length['cruiser'])
cruiser3 = Ship(ships_length['cruiser'])
destroyer1 = Ship(ships_length['destroyer'])
destroyer2 = Ship(ships_length['destroyer'])
destroyer3 = Ship(ships_length['destroyer'])
destroyer4 = Ship(ships_length['destroyer'])
but automated
You can iterate over the ships you want and lookup their lengths to make them:
ships = []
for type_of_ship in ships_amount:
ships.append(Ship(ships_length[type_of_ship]))
or even
ships = [Ship(ships_length[k]) for k in ships_amount]
(In the second example, ky is a shorthand for key, or what is now called type_of_ship in the for loop)
This will give you one of each type of ship.
This will not give you variables named 'carrier1' etc. but you will be able to do stuff with each item in the ships.
e.g.
for ship in ships:
print(ship.length)
To get sthe stated number, or amount of each type of ships, you need to make extra ships in the loop.
By iterating over items() you get a key and value back, which I have called k and v though deserve better names.
The value in your dictionary tells you how many:
ships = []
for k, v in ships_amount.items():
ships.extend([Ship(ships_length[k]) for _ in range(v)])
This gives you the 10 ships you asked for.
If you create a child class of Ship for each Ship model you need, you can group them in a Fleet, then create the fleet directly in one line of code...
Maybe something like this:
import random
class Ship: # this becomes an abstract class, not to be instanciated
# you could have it inherit from ABC (Abstract Base Class)
def __init__(self):
self.length = self.__class__.length
self.heading = None
self.set_heading()
self.location = None
self.set_location()
def set_heading(self):
self.heading = random.choice(['v','h'])
def set_location(self): # this method needs more work to prevent
# ships to occupy the same spot and overlap
space = []
row = random.randint(0, 10 - self.length)
column = random.randint(0, 10 - self.length)
if self.heading == 'v':
for c in range(self.length):
space.append((row, column + c))
elif self.heading == 'h':
for r in range(self.length):
space.append((row + r, column))
self.location = space
def __str__(self):
return f'{self.__class__.__name__} at {self.location}'
class AircraftCarrier(Ship): # every type of ship inherits from the base class Ship
length = 5 # Each class of ship can have its own specifications
# here, length, but it could be firepower, number of sailors, cannons, etc...
class BattleShip(Ship):
length = 4
class Cruiser(Ship):
length = 3
class Destroyer(Ship):
length = 2
class Fleet:
ships_number = {AircraftCarrier : 1,
BattleShip: 2,
Cruiser: 3,
Destroyer: 4}
def __init__(self):
self.ships = [ship() for ship, number in Fleet.ships_number.items()
for _ in range(number)]
def __str__(self):
return '\n'.join([str(ship) for ship in self.ships])
if __name__ == '__main__':
fleet = Fleet() # <-- the creation of the entire Fleet of Ships
print(fleet) # takes now one line of code now
Example Output:
(The locations are randomly assigned and will vary from run to run.)
AircraftCarrier at [(1, 2), (2, 2), (3, 2), (4, 2), (5, 2)]
BattleShip at [(5, 3), (6, 3), (7, 3), (8, 3)]
BattleShip at [(5, 1), (6, 1), (7, 1), (8, 1)]
Cruiser at [(4, 7), (5, 7), (6, 7)]
Cruiser at [(0, 5), (0, 6), (0, 7)]
Cruiser at [(6, 6), (7, 6), (8, 6)]
Destroyer at [(4, 8), (5, 8)]
Destroyer at [(3, 5), (4, 5)]
Destroyer at [(1, 5), (1, 6)]
Destroyer at [(2, 1), (2, 2)]
Adding a new type of ship:
Adding a new type of ship is very easy: It suffices to create a new class inheriting from the abstract base class Ship, and adding the number of the new ships to the fleet composition:
class Submarine(Ship):
length = 1
Fleet.ships_number[Submarine] = 5 # or add this entry directly in the class Fleet data
The fleet has now an additional 5 submarines:
AircraftCarrier at [(4, 1), (5, 1), (6, 1), (7, 1), (8, 1)]
BattleShip at [(5, 5), (6, 5), (7, 5), (8, 5)]
BattleShip at [(0, 0), (1, 0), (2, 0), (3, 0)]
Cruiser at [(5, 2), (5, 3), (5, 4)]
Cruiser at [(2, 0), (3, 0), (4, 0)]
Cruiser at [(7, 7), (8, 7), (9, 7)]
Destroyer at [(4, 3), (5, 3)]
Destroyer at [(2, 1), (2, 2)]
Destroyer at [(0, 8), (1, 8)]
Destroyer at [(3, 6), (3, 7)]
Submarine at [(8, 8)]
Submarine at [(0, 7)]
Submarine at [(3, 4)]
Submarine at [(5, 9)]
Submarine at [(9, 3)]
I have the following code written in python 2.7 to find n time Cartesian product of a set (AxAxA...xA)-
prod=[]
def cartesian_product(set1,set2,n):
if n>=1:
for x in set1:
for y in set2:
prod.append('%s,%s'%(x,y))
#prod='[%s]' % ', '.join(map(str, prod))
#print prod
cartesian_product(set1,prod,n-1)
else:
print prod
n=raw_input("Number of times to roll: ")
events=["1","2","3","4","5","6"]
cartesian_product(events,events,1)
This works properly when n=1. But changing the parameter value from cartesian_product(events,events,1) to cartesian_product(events,events,2) doesn't work. Seems there's an infinite loop is running. I can't figure where exactly I'm making a mistake.
When you pass the reference to the global variable prod to the recursive call, you are modifying the list that set2 also references. This means that set2 is growing as you iterate over it, meaning the iterator never reaches the end.
You don't need a global variable here. Return the computed product instead.
def cartesian_product(set1, n):
# Return a set of n-tuples
rv = set()
if n == 0:
# Degenerate case: A^0 == the set containing the empty tuple
rv.add(())
else:
rv = set()
for x in set1:
for y in cartesian_product(set1, n-1):
rv.add((x,) + y)
return rv
If you want to perserve the order of the original argument, use rv = [] and rv.append instead.
def cartesian_product(*X):
if len(X) == 1: #special case, only X1
return [ (x0, ) for x0 in X[0] ]
else:
return [ (x0,)+t1 for x0 in X[0] for t1 in cartesian_product(*X[1:]) ]
n=int(raw_input("Number of times to roll: "))
events=[1,2,3,4,5,6]
prod=[]
for arg in range(n+1):
prod.append(events)
print cartesian_product(*prod)
Output:
Number of times to roll: 1
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6)]
you can also pass string in your events list but it'll print string in tuple also.
inside the recursive call cartesian_product(set1,prod,n-1) you are passing the list prod, and you are again appending values to it, so it just grows over time and the inner loop never terminates. Perhaps you might need to change your implementation.
I am trying to write a function that completes an A* search with multiple goals. Basically it is searching a grid like structure of the form:
%%%%%%%%%%%%%%%%%%%%
%. ...P .%
%.%%.%%.%%.%%.%% %.%
% %% %..... %.%
%%%%%%%%%%%%%%%%%%%%
for a path from P that goes through all the dots (basically Pacman).
However I have run into a problem with my algorithm (which I attempted to adapt from my A* search for a single goal) as the path it returns does not go through all the dots. This is the path it returns for the above maze:
Path = [(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), (1, 14), (1, 15), (1, 16)]
while a print statement shows that the visited variable has a value at return of:
[(1, 16), (1, 15), (2, 16), (1, 17), (1, 14), (3, 16), (1, 18), (1, 13), (3, 15), (2, 18), (1, 12), (2, 13), (3, 18), (3, 14), (1, 11), (3, 13), (3, 12), (1, 10), (1, 9), (3, 11), (2, 10), (1, 8), (3, 10), (1, 7), (3, 9), (1, 6), (3, 8), (2, 7), (1, 5), (3, 7), (1, 4), (3, 6), (2, 4), (1, 3), (3, 4), (1, 2), (1, 1), (2, 1)]
I think that that problem is how I am storing the current path (where each node stores its parent node, and then I return the end node and go backwards recursively to get the path). Does anyone have any advice for what I should change? I attached my current code below. Thanks!
What your algorithm is currently doing is trying to find the goal by expending its area around the starting point and finding the best path for every node its visiting.
In a single-goal situation, it works well and you can get the path to this goal.
However how you have adapted it to a multi-goal purpose is that only the stop condition changes (when all goals as been visited once), meaning that you found the shortest path from the start point to each goal but not a single path visiting all nodes.
In the case, you just want the paths from the start point to each goal, just get the path (via parents) from each goal point.
If you really want to implement a pacman-like search, this is NP-Hard problem (see this answer).
As one of the comment proposes, if you have a small list of goals, you can find a solution with brute-force:
Let's say you have 3 goals: A,B,C (which were dots):
%%%%%%%%%%%%%%%%%%%%
%A P %
% %% %% %% %%C%% % %
% %% % B % %
%%%%%%%%%%%%%%%%%%%%
Using your algorithm, you can find the shortest path from P to A, then A to B then B to C. Do the same for other permutations ((P,A,C,B),(P,B,A,C) ...): see itertools.combinations(goals, len(goals))
You can then use your algorithm to find the path from one point to the other:
def A_multiple_goals(maze, start, goals):
paths = []
for itinerary in itertools.combinations(goals, len(goals)):
path = get_path(A_search_multiple(maze, start, itinerary[0])) # First go to first goal from start
for i in range(1 , len(itinerary)): # Then from each goal, goto the next one
path += get_path(A_search_multiple(maze, itinerary[i-1], itinerary[i]))
paths.append(paths)
return min(paths, key=len)
This is a brute-force approach, if you have a lot of goals, you would need a better algorithm based around the Traveling Salesman Problem.