0

I would like your help with something that's been bugging me. I really have tried to figure this out on my own, but after several hours I feel thoroughly stuck.

So, I'm new to python (my second language), and I'm writing an implementation of Dijkstra's algorithm as part of my undergrad research.

For some odd reason, when I try to remove the current node from the unvisited set using Unvisited.remove(Current), I get the following error:

Traceback (most recent call last):
Current Node: 
  File "H:\DijkstraShortestPath\src\Main.py", line 92, in <module>
    Unvisited.remove(Current)
ValueError: list.remove(x): x not in list
1
Unvisited Nodes: 
['1', '2', '3', '4', '5', '6', '7', '8']
Current Node: 
8
Unvisited Nodes: 
['2', '3', '4', '5', '6', '7', '8']

Note that Current is a value pulled from a list, while Unvisited is a list.

After looking through several threads here and otherwise scouring the internet, I've decided to post my whole code to avoid any confusion. I noticed some kind of issue with list.remove(x) when nested in loops (mine is nested in a for loop, as well), but I don't understand what's going on beneath all the techno-jargon.

Could somebody please explain to me why this is an issue? What can I do to fix it?

#Main File
#Excecutes the Dijkstra Shortest Path Algorithm

#import Create_Network #Generates Network with 5 OD pairs as replications

testfile = "\DijkstraShortestPath\Backup And Tests\TEST"
Arcfile = testfile + "\Arcs.txt"
ODfile = testfile + "\ODpairs.txt"
Nodefile = testfile + "\Nodes.txt"

#INITIALIZE ALGORITHM
#Populate label, visited, and unvisited sets
#For initial conditions, label is infinite for all nodes except origin
#For initial conditions, all nodes are unvisited (including origin)

LabelArray = [] #Stores the distance labels for each of the nodes; has length
                #equal to the number of nodes in the network
Unvisited = [] #Stores unvisited nodes, by NodeID
Visited = [] #Stores visited nodes, by NodeID
ODArray = [] #Stores Origin and Destination Pairs

#Get the origin and destination pair
with open(ODfile,'r') as f:
    for line in f:
        ODArray = line.strip().split(",")
Origin = ODArray[0]
Destination = ODArray[1]
#Set the first current node as the origin
Current = Origin

#Generate Unvisited nodes and Labels (all infinite, except origin)
with open(Nodefile,'r') as f:
    for line in f:
        LabelArray.append(float("inf")) #make all node distance labels infinite
        NodeID = line.strip().split(",")[0]
        Unvisited.append(NodeID) #Add NodeID to Unvisited

#Set distance for origin to zero
LabelArray[int(ODArray[0])-1] = float(0) #float(0) to match float("inf")

#Count number of lines and items in each line
#i.e., find out how many nodes and params for storage in ArcParams list
numarcs = 0
with open(Arcfile,'r') as f:
    for line in f:
        if line != '\n':
            numarcs = numarcs + 1 #integer
            numparams = len(line.strip().split(",")) #integer

#Store arc origin, destination, length to ArcParams list
ArcParams = [[0 for i in range(numparams)] for i in range(numarcs)]

with open(Arcfile,'r') as f:

    for line in f:
        params = line.strip().split(",")
        ArcParams[int(params[0])-1][0] = params[0]
        ArcParams[int(params[0])-1][1] = params[1]
        ArcParams[int(params[0])-1][2] = params[2]
        ArcParams[int(params[0])-1][3] = float(params[3])

#END INITIALIZATION

#BEGIN DIJKSTRA SHORTEST PATH, LOOPING OVER NODES IN NETWORK

for node in Unvisited:

    #Find the nodes adjacent to Current AND in Unvisited
    Adjacent = []

    for i in range(numarcs):
        if ArcParams[i][1] == Current: #search for origin = current
            if ArcParams[i][1] in Unvisited: #checks if nodes found are in Unvisited
                if ArcParams[i][1] != ArcParams[i][2]: #excludes Current as adjacent
                    Adjacent.append(ArcParams[i][2]) #Add node to Adjacent

    #For each adjacent node, update distance labels

    for node in Adjacent:
        temp = float(LabelArray[int(Current)-1]) + float(ArcParams[int(node)][3])

        if temp < LabelArray[int(node)-1]:
            LabelArray[int(node)-1] = temp

    #Add current node to Visited set
    print "Current Node: "
    print Current
    print "Unvisited Nodes: "
    print Unvisited

    Visited.append(Current)
    Unvisited.remove(Current)

    #Check for end-conditions; has destination entered Visited?
    #Or is the smallest tentative distance infinite? Stop both ways.

    if Destination in Visited:
        if LabelArray[int(Destination)-1] == inf:
            print "There is no feasible path"
        print "Shortest distance found!"
        print "Distance is: " + str(LabelArray[Destination-1])


    #Select the Unvisited node marked with smallest tentative distance
    MinDist = min(LabelArray[1:])
    MinNode = LabelArray.index(MinDist) + 1

    #Clear existing Current, set new Current
    Current = MinNode
Chris
  • 71
  • 5
  • It seems that your list contains strings ("1", "2",...), not integers. When you try to delete it, you are passing the wrong argument (an integer). – Iñigo Mar 15 '13 at 14:49

1 Answers1

0

Your code essentially does this:

for item in container:
    # stuff
    container.remove(item)
    # more stuff

Modifying a container while you're iterating over it will usually break your code. The Java standard library has a specific exception (ConcurrentModificationException) which usually makes your program bail out on this kind of problem, but Python unfortunately does not; your loop will just do the Wrong Thing.

The situation here is kind of like the old joke, Patient: "It hurts when I do this." Doctor: "Don't do that."

Try something like this instead:

container = [6, 7, 8]

while len(container) > 0:
    item = container.pop()
    # do something useful with item

This processes the last-used item first. So for example the first iteration would give you 8, then 7, then 6. If you prefer to process left-to-right, you should use the collections.deque class:

import collections

container = collections.deque([6, 7, 8])

while len(container) > 0:
    item = container.popleft()
    # do something useful with item

Also, a couple style nitpicks about your code:

  • Your code violates the PEP 8 coding style guide. Your variables should be named with words_separated_by_underscores. They should begin with a capital letter only if they are class names. See http://www.python.org/dev/peps/pep-0008/#global-variable-names. (The class I suggested, deque, also violates this guide, but I assume that's because it was created in the Very Early Days of Python, before PEP 8 became widely accepted, and could not be changed thereafter due to backward compatibility. Why it wasn't changed in Py3K, I have no clue.)

  • Your use of backslashes is also questionable. You should use Python's raw string feature for strings that contain backslashes, like this:

    testfile = r"c:\new folder"

Alternatives include using forward slashes instead (Windows filesystems use them), using os.path.join for cross-platform compatibility, accepting a filename in command-line arguments, or using double backslashes.

picomancer
  • 1,786
  • 13
  • 15