0

I've been struggling for a long time after writing my A* Search algorithm with the fact that when the number of cities is greater than 8, the algorithm won't return any answer (or is ridiculously slow).

The cities are stored in a 2-d array where point cityList[x][y] is the distance between city x and city y (it is also the same as cityList[y][x]).

It's slightly messy as I had to use both the city class and the beginning of each route to remember route lengths and which routes had already been attempted.

A new route is also created for each city that is added on.

Could anyone help to optimize it or ensure it can work on an increasingly large number of cities.

class city(object):
    def __init__(self, number):
        self.number = number
        global size
        self.possibleChildren = []
        for i in range(0,size):
            if i != number:
                self.possibleChildren.append(i)
    def getNo(self):
        return self.number
    def getSize(self):
        return len(self.possibleChildren)
    def getOptions(self, i):
        self.i = i
        return self.possibleChildren[self.i]
    def deleteNo(self, option):
        self.option = option
        if(self.option in self.possibleChildren):
            self.possibleChildren.remove(self.option)
    def addFinalStep(self, beginning):
        self.beginning = beginning
        self.possibleChildren.append(self.beginning)


def Astar():
    routeList = []
    #routeList[i][0] = distance travelled, routeList[i][1] = number of cities past
    for i in range(0, size):
        newRoute = []
        newCity = city(i)
        newRoute.append(0)
        newRoute.append(1)
        newRoute.append(newCity)
        routeList.append(newRoute)
    while True:
        toUse = 0
        smallest = -2
        #Now search through the routeList to find the shortest route length
        for i in range(0, len(routeList)):
            #getSize() checks if there are any cities that can be visited by
             this route that have not already been tried, this list is
             stored in the city class
            if routeList[i][1 + routeList[i][1]].getSize() != 0:
                if routeList[i][0] < smallest or smallest == -2:
                    smallest = routeList[i][0]
                    toUse = i
            elif routeList[i][1 + routeList[i][1]].getSize() == 0:
                routeList.remove(i)
        routeRec = []
        #Creates the new route
        for i in range (0, len(routeList[toUse])):
            routeRec.append(routeList[toUse][i])

        currentCity = routeRec[1 + routeRec[1]]
        possibleChildren = []
        for i in range(0, currentCity.getSize()):
            possibleChildren.append(currentCity.getOptions(i))

        smallest = 0
        newCityNo = 2    
        for i in range(0, size):
            if(i in possibleChildren):
                #Finds the closest city
                if smallest > cityList[currentCity.getNo()][i] or smallest == 0:
                    newCityNo = i
                    smallest = cityList[currentCity.getNo()][i]
        #If the new city to visit is the same as the first, the algorithm
        #has looped to the beginning and finished
        if newCityNo == routeRec[2].getNo():
            print("Tour Length")
            print(routeRec[0] + smallest)
            print("Route: ")
            for i in range(2,len(routeRec)):
                print(routeRec[i].getNo())
            return(routeRec[0] + smallest)

        #deletes all cities that have been tried
        routeList[toUse][1 + routeRec[1]].deleteNo(newCityNo)
        nextCity = city(newCityNo)
        #deletes all cities that are already in the route
        for i in range(2, 2 + routeRec[1]):
               nextCity.deleteNo(routeRec[i].getNo())

        #When the route is full, it can return to the first city                   
        routeRec[1] = routeRec[1] + 1
        print(routeRec[1])
        if routeRec[1] == size:
            #first city added to potential cities to visit
            nextCity.addFinalStep(routeRec[2].getNo())

        routeRec.append(nextCity)
        routeRec[0] = routeRec[0] + smallest
        routeList.append(routeRec)
Thomasleveil
  • 95,867
  • 15
  • 119
  • 113
Jaiymo
  • 9
  • 2
  • You should not use A* for traveling-salesman problem: a) It finds the shortest path and not the shortest path through all cities. b) there is no gain from A* for let's say 20 cities. For more than 20 cities traveling salesman is hard to solve with brute force. For a dynamic approach see for example http://www.geeksforgeeks.org/travelling-salesman-problem-set-1/ – ead Jan 17 '16 at 19:19

1 Answers1

0

You need to do some decomposition here. You need data structure for saving and getting nodes to expand, in this case heap. You also need some heuristic which you can pass it as a parameter.

Implementation of A*

# A* Algorithm
#   heuristic - Heuristic function expecting state as operand
#   world - Global
#   sstate - Start state
#   endstate - End state, state to find
#   expandfn - function(world, state) returning array of tuples (nstate, cost)
#       where nstate is the state reached from expanded state and cost is the 'size'
#       of the edge.
def Astar(heuristic, world, sstate, endstate, expandfn):
    been = set()
    heap = Heap()
    end = None

    # We hape tuples (state, pathlen, parent state tuple) in heap
    heap.push((sstate, 0, None), 0) # Heap inserting with weight 0

    while not heap.isempty():

        # Get next state to expand from heap
        nstate = heap.pop()

        # If already seen state not expand
        if nstate[0] in been:
            continue

        # If goal reached
        if nstate[0] === endstate:
            end = nstate
            break

        # Add state as expanded
        been.add(nstate[0])

        # For each state reached from current state
        for expstate in expandfn(world, nstate[0]):
            pathlen = nstate[1] + expstate[1]

            # Expanding state with heap weight 'path length + heuristic'
            heap.push((expstate[0], pathlen, nstate), pathlen + heuristic(expstate[0]))
    # End of while loop

    if end is None:
        return None

    # Getting path from end node
    pathlen = end[1]
    res = []
    while end[2] is not None:
        res.append(end[0])
        end = end[2]
    res.reverse()

    return res, pathlen
8bra1nz
  • 717
  • 7
  • 19