2

Well, I have this graph: Graf

I have to make a code based in Branch and Bound and using backtracking, that has to show the optimum way to match the nodes of a graph. So in this example, the optimum solution must be >> [(1,4),(2,3)]. But my algorithm, shows this possible solution solution, which is not the optimum >> [(1,2),(3,4)]. I think that the problem perhaps is at the 'Undo' line, but I'm not sure...If someone can help me to solve this, I'd be very grateful!

Here is my code:

import networkx as nx
import sys
import matplotlib.pyplot as plt
import copy
import operator
from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

''' Method to create a Graf from a file '''
def leerGrafo():                                                                                                                   
    #name = raw_input("Enter the name of the Graph please: ")
    name = "grafo2.dat"                                                                        
    G = nx.read_edgelist(name,nodetype=int,data=(('weight',float),))
    return G    

''' Method to create the adjacency matrix '''
def matrixAdj(G):
    ''' Tener en cuenta: diagonal = 0, y no conex. = Inf '''
    nodes = G.number_of_nodes()
    edges = G.edges()

    listaAdj = [[float("Inf") for j in range(nodes)] for i in range(nodes)]

    for i in range(len(edges)):
        valor1,valor2 = edges[i][0],edges[i][1]
        listaAdj[valor1-1][valor2-1] = G.edge[valor1][valor2]['weight']
        listaAdj[valor2-1][valor1-1] = G.edge[valor1][valor2]['weight']

    return listaAdj

''' returns the weight from the adjacency matrix '''
def weight_of(s,G,son):
    return matrix[s-1][int(son)-1]

''' Backtracking Method '''
def backtracking(s,G,l,cMax,cMin,finalSol):
    # We insert the current valid node, from our current way
    l.append(s)
    # We iterate over the sons of our current node
    for son in G.neighbors(s):
        # If the current son is not one of the predecessors of the current node 's'...
        if not son in l:
            # We calculate the bound of the current son, adding the weight of his father (s) + the weight of the current son
            # Note: At the start (the first node), we add the lower bound + the weight of that node.
            c = weight_of(son,G,s) + cMin
            # If this bound is lesser or iqual than the upper bound...
            if c <= cMax:
                # If this current node is a leaf, means that we've found a possible way...
                if len(l)+1 == G.number_of_nodes():
                    # We insert this current node (son)
                    l.append(son)
                    # We update the upper bound with the bound of this current node
                    cMax = c
                    # We store a copy of our possible way
                    finalSol = copy.copy(l)
                    # We reset our list that conteins our possible way
                    l = []
                    return finalSol
                # Si no...seguimos recorriendo las ramas hasta encontrar un camino entero
                else:
                    backtracking(son,G,l,cMax,c,finalSol)
    # Undo
    del l[-1]

    return 

''' Main Function ''' 
def main():
    # We make the graf
    G1 = leerGrafo()
    global matrix
    # We create the adjacency matrix
    matrix = matrixAdj(G1)
    # We make a ordered list that contains just the weight of all nodes
    pesos = [a[2]['weight'] for a in sorted(G1.edges(data=True), key=lambda aux: aux[2])]
    # We calculate the default upper bound
    cotaMax = sum(pesos)
    # We calculate the lower bound
    cotaMin = pesos[0]
    l = []
    global finalSol
    finalSol = 0
    # We call the backtracking method
    bestSol = backtracking(G1.nodes()[0],G1,l,cotaMax,cotaMin,finalSol)
    # We print the solution
    print "Best Solution: "
    for x, y in grouped(bestSol, 2):
        print "(%d,%d)" % (x, y)
wj127
  • 118
  • 1
  • 12
  • Do you mean Branch and Bound? – Koebmand STO Dec 26 '15 at 22:51
  • 1
    Tried look at your code, but without knowing how G1 is made up I can't figure it out sorry. – Koebmand STO Dec 26 '15 at 22:59
  • hard to know, and more if I can't test the code. Also the doc string of a function go in the first line of the same, not before it... – Copperfield Dec 26 '15 at 23:09
  • G1 is like the graf showed up, and with it, I make a normal adjacency matrix @KoebmandSTO – wj127 Dec 26 '15 at 23:19
  • I've updated my code here, now you have it all @Copperfield. But if you want to test it, you need to make a "file.dat" with this structure: node1 node2 weight – wj127 Dec 26 '15 at 23:25
  • Easier, you can copy the table in the center of the image @Copperfield – wj127 Dec 26 '15 at 23:26
  • Can you explain what your code is trying to accomplish? What does your desired output, `[(1,4),(2,3)]` mean? – Blckknght Dec 26 '15 at 23:34
  • I need to match the nodes, in an optimum way. Optimum way means, that I have to match the nodes with a minimum weight. You can see it at the image, but I explain you: node 1 to node 4 are '40' , then, node 2 to node 3 are '30' , so 40 + 30 = 70. This is the optimum matching @Blckknght I'm trying to do so :) – wj127 Dec 26 '15 at 23:41

1 Answers1

1

I think I am starting to see the problem here, your algorithms only choose a path, the first path that encounter, it need to check all path and choose the min one, and by your example that is choosing the min of all non-walked paths to the next node...

here my solution

def minimun_path(s,G,camino,cMax,cMin):
    if len(camino) == G.number_of_nodes():
        # I found the complete path
        return camino
    temp = []
    for son in G.neighbors(s):
        # I record all path to a node not visited yet
        if son not in camino:
            peso = weight_of(son,G,s)+cMin
            temp.append( (son,peso) )
    if temp:
        # I choose a minimun of those
        sig,w = min( temp, key=lambda x:x[1])
    else:
        # I don't have where to go, so I stay put
        sig = s
    return minimun_path(sig,G,camino+(s,),cMax,cMin)

For camino I use tuple instead of list, as a inmutable object I will not found weird border effect, just in case...

call it as

bestSol = minimun_path(G1.nodes()[0],G1,tuple(),cotaMax,cotaMin)

output

Best Solution: 
(1,4)
(3,2)
Copperfield
  • 8,131
  • 3
  • 23
  • 29
  • Awesome! Yes,you were right about my problem! I had some idea in mind but I couldn't see it well... anyway, you've solved my problem!! And pretty fast! Thank you so much @Copperfield ! I really appreciate it! – wj127 Dec 27 '15 at 10:51
  • (1,4),(3,2) is not a path, to be a path you would need to connect the 2 groups. Since the answer is accepted I guess it is meaningless to use more time on it. But don't see backtracking_v2 defined anywhere? – Koebmand STO Dec 27 '15 at 14:54
  • 1
    @KoebmandSTO my bad, that is supposed to be minimum_path I change the name later to reflect its purpose. And judging by he original backtraking and the example, that is what he needed, then he later grouped in pairs to print it... – Copperfield Dec 27 '15 at 15:05
  • Oh, it definitely is what OP wanted (not convinced it is what is needed, sounds like a partly misunderstood homework assignment). Not saying your code is wrong, just bad wording for the matches / pairs. Thanks for the update, now I can make it work to satisfy curiosity :). – Koebmand STO Dec 27 '15 at 15:12
  • @KoebmandSTO what do you mean by bad wording? my variable name selection? well that is my Spanish talking... By the way what OP stand for? – Copperfield Dec 27 '15 at 15:54
  • You say "choose a path" etc instead of "choose a pair". A path would be a set of lines that is connected, like (1,2),(2,3) would be a path starting in 1 and ending in 3, but not visiting all nodes. (1,2)(3,4) is not connected to each other, so it is not a path. – Koebmand STO Dec 27 '15 at 16:02
  • that is in reference to the problematic algorithm backtracking, that it give a list of nodes in certain order, that in this case by the given data is the visit order of the nodes in the minimum path, the pair selection is done later accordingly to the result of that and I don't touch that... – Copperfield Dec 27 '15 at 16:43
  • I'm sorry @KoebmandSTO if I didn't explain the problem perfectly. I tried to do my best. But as Copperfield has said on his last comment, that is what I needed to do. – wj127 Dec 27 '15 at 16:52
  • I think you are misunderstanding me, I am not talking about how the code works when I refer to the word path.. – Koebmand STO Dec 27 '15 at 20:17