Пример #1
0
    def genericSearch(self, rootId):
        """
        Execute a generic search in the graph starting from the specified node.
        :param rootId: the root node ID (integer).
        :return: the generic exploration tree.
        """
        if rootId not in self.nodes:
            return None

        treeNode = TreeNode(rootId)
        tree = Tree(treeNode)
        vertexSet = {treeNode}  # nodes to explore
        markedNodes = {rootId}  # nodes already explored

        while len(vertexSet) > 0:  # while there are nodes to explore ...
            treeNode = vertexSet.pop()  # get an unexplored node
            adjacentNodes = self.getAdj(treeNode.info)
            for nodeIndex in adjacentNodes:
                if nodeIndex not in markedNodes:  # if not explored ...
                    newTreeNode = TreeNode(nodeIndex)
                    newTreeNode.father = treeNode
                    treeNode.sons.append(newTreeNode)
                    vertexSet.add(newTreeNode)
                    markedNodes.add(nodeIndex)  # mark as explored
        return tree
Пример #2
0
    def calculateSubNode(self, rootId):
        """
        Questa funzione, dato un grafo e l'Id di un suo nodo, mi restituisce il numero di nodi raggiungibili a partire dal nodo
        :param rootId: Id del nodo
        :return: Numero di elementi che posso raggiungere a partire dal nodo
        """
        # Utilizzo l'algoritmo per la visita generica visto a lezione

        if rootId not in self.nodes:
            return None
        counter = 0  # Contatore dei igli del nodo
        treeNode = TreeNode(rootId)
        vertexSet = {treeNode}
        markedNodes = {rootId}

        while len(vertexSet) > 0:
            treeNode = vertexSet.pop()
            adjacentNodes = self.getAdj(treeNode.info)
            for nodeIndex in adjacentNodes:
                if nodeIndex not in markedNodes:
                    counter = counter + 1  # Incremento il contatore
                    newTreeNode = TreeNode(nodeIndex)
                    vertexSet.add(newTreeNode)
                    markedNodes.add(nodeIndex)
        return counter
Пример #3
0
    def findLeaf(self, rootId):
        """
        Questa funzione, dato un grafo e l'id di un nodo, esegue una visita generica. Per ogni foglia, chiama la funzione
        leafDistance, confrontando i valori ottenuti con quelli della lista massima.

        :param Id della radice da cui far partire la visita
        :return: lista contenente le informazioni sul nodo massimo e sul percorso
        """

        max = [0, [], 0, []]  # Inizializzo a 0

        # max[0] = lunghezza del percorso
        # max[1] = Lista dei nodi più profondi
        # max[2] = lista dei nodi visitati
        # max[3] = Id dei nodi più profondi

        # Utilizzando l'algoritmo per la visita generica visto a lezione, scansiono l'albero
        if rootId not in self.nodes:
            return None

        treeNode = TreeNode(rootId)
        vertexSet = {treeNode}
        markedNodes = [rootId]  # Nodi visitati

        while len(vertexSet) > 0:
            treeNode = vertexSet.pop()
            adjacentNodes = self.getAdj(treeNode.info)

            if len(
                    adjacentNodes
            ) == 1:  # Se il nodo che sto considerando ha solamente un nodo adiacente, dunque è una foglia:
                lunghezzaPercorso = self.leafDistance(
                    treeNode.info
                )  # Calcolo la lunghezza del percorso massimo raggiungibile dalla foglia
                if lunghezzaPercorso[0] > max[
                        0]:  # Se il percorso è più lungo dell'attuale massimo, imposto i valori della foglia
                    max[0] = lunghezzaPercorso[0]
                    max[1] = lunghezzaPercorso[1]
                    max[3] = max[3] + lunghezzaPercorso[2]
                if (lunghezzaPercorso[0] == max[0]):
                    for i in lunghezzaPercorso[
                            1]:  # Per ogni nodo il cui percorso risulta massimo,
                        if (
                                i.info not in max[3]
                        ):  # controllo che non sia già presente nella lista dei nodi massimi
                            max[1] = max[1] + lunghezzaPercorso[
                                1]  # In caso non sia presente, lo aggiungo alla lista dei nodi "più profondi"

            for nodeIndex in adjacentNodes:
                if nodeIndex not in markedNodes:
                    newTreeNode = TreeNode(nodeIndex)
                    newTreeNode.father = treeNode
                    treeNode.sons.append(newTreeNode)
                    vertexSet.add(newTreeNode)
                    markedNodes.append(nodeIndex)

        max[2] = markedNodes  # In max[2] avrò la lista dei nodi visitati durante la visita
        return max
Пример #4
0
    def leafDistance(self, rootId):
        """
        Questa funzione, dato un grafo e l'id di una foglia, restituisce il percoso più lungo e la sua lunghezza.

        :param rootId: the root node ID (integer).
        :return: the generic exploration tree.
        """

        lunghezzaPercorso = [0, [], []]  # Inizializzo a 0

        # lunghezzaPercorso[0] = distanza del nodo dalla radice
        # lunghezzaPercorso[1] = nodo
        # lunghezzaPercorso[2] = Id del nodo

        # Eseguo una visita generica utilizzando l'algoritmo visto a lezione
        if rootId not in self.nodes:
            return None

        treeNode = TreeNode(rootId)
        vertexSet = {treeNode}
        markedNodes = [rootId]

        while len(vertexSet) > 0:
            treeNode = vertexSet.pop()
            adjacentNodes = self.getAdj(treeNode.info)

            if len(
                    adjacentNodes
            ) == 1:  # Quando trovo una foglia, controllo la sua distanza dalla foglia considerata in questo caso come radice
                if lunghezzaPercorso[
                        0] < treeNode.distanza:  # Se la foglia ha una distanza superiore a quella dell'attuale percorso, imposto i nuovi valori
                    lunghezzaPercorso[0] = treeNode.distanza
                    lunghezzaPercorso[1] = []
                    lunghezzaPercorso[1].append(treeNode)
                    lunghezzaPercorso[2].append(treeNode.info)
                elif lunghezzaPercorso[
                        0] == treeNode.distanza:  # Altrimenti aggiungo agli altri valori già presenti in lista
                    lunghezzaPercorso[1].append(treeNode)
                    lunghezzaPercorso[2].append(treeNode.info)

            for nodeIndex in adjacentNodes:
                if nodeIndex not in markedNodes:
                    newTreeNode = TreeNode(nodeIndex)
                    newTreeNode.father = treeNode
                    newTreeNode.distanza = treeNode.distanza + 1  # Incremento la distanza del nodo
                    treeNode.sons.append(newTreeNode)
                    vertexSet.add(newTreeNode)
                    markedNodes.append(nodeIndex)

        return lunghezzaPercorso  # Restituisco la lista con i valori
Пример #5
0
    def mediumNode(self, rootId):
        """
        Questa funzione, dato un grafo e l'id di un nodo, esegue una visita generica. Per ogni foglia, chiama la funzione
        leafDistance, confrontando i valori ottenuti con quelli della lista massima.

        :param Id della radice da cui far partire la visita
        :return: lista contenente le informazioni sul nodo massimo e sul percorso
        """

        max = [0, 0, 0]  # Inizializzo a 0 le informazioni riguardo al nodo massimo

        # max[0] = lunghezza del percorso
        # max[1] = foglia più profonda
        # max[2] = lista dei nodi visitati
        # max[3] = nodi appartenenti al percorso più lungo

        # Utilizzando l'algoritmo per la visita generica visto a lezione, scansiono l'albero
        if rootId not in self.nodes:
            return None

        treeNode = TreeNode(rootId)
        vertexSet = {treeNode}
        markedNodes = [rootId]  # Nodi visitati

        while len(vertexSet) > 0:
            treeNode = vertexSet.pop()
            adjacentNodes = self.getAdj(treeNode.info)

            if len(
                    adjacentNodes) == 1:  # Se il nodo che sto considerando ha solamente un nodo adiacente, dunque è una foglia:
                lunghezzaPercorso = self.leafDistance(
                    treeNode.info)  # Calcolo la lunghezza del percorso massimo raggiungibile dalla foglia
                if lunghezzaPercorso[0] > max[
                    0]:  # Se il percorso è più lungo dell'attuale massimo, imposto i valori della foglia
                    max[0] = lunghezzaPercorso[0]
                    max[1] = lunghezzaPercorso[1]

            for nodeIndex in adjacentNodes:
                if nodeIndex not in markedNodes:
                    newTreeNode = TreeNode(nodeIndex)
                    newTreeNode.father = treeNode
                    treeNode.sons.append(newTreeNode)
                    vertexSet.add(newTreeNode)
                    markedNodes.append(nodeIndex)

        max[2] = markedNodes
        return max
Пример #6
0
def prim(graph):
    """
    Prim's algorithm is a greedy algorithm for the computation of the
    Minimum Spanning Tree (MST).
    The algorithm assumes a graph implemented as incidence list.
    This implementation leverages the BinaryTree and PriorityQueue data
    structures.
    ---
    Time Complexity: O(|E|*log(|V|)
    Memory Complexity: O(|E|)

    :param graph: the graph.
    :return the MST, represented as a tree.
    """
    # initialize the root
    root = 0

    # initialize the tree
    tree = Tree(TreeNode(root))  # MST as a tree
    mst_nodes = {root}  # nodes added to MST

    # initialize weights
    current_weight = len(graph.nodes) * [INFINITE]
    current_weight[root] = 0
    mst_weight = 0

    # initialize the frontier (priority-queue)
    pq = PriorityQueue()
    pq.insert((root, Edge(root, root, 0)), 0)

    # while the frontier is not empty ...
    while not pq.isEmpty():
        # pop from the priority queue the node u with the minimum weight
        pq_elem = pq.popMin()
        node = pq_elem[0]
        # if node u not yet in MST, update the tree
        if node not in mst_nodes:
            edge = pq_elem[1]
            tree_node = TreeNode(node)
            tree_father = tree.foundNodeByElem(edge.tail)
            tree_father.sons.append(tree_node)
            tree_node.father = tree_father
            mst_nodes.add(node)
            mst_weight += edge.weight

        # for every edge (u,v) ...
        curr = graph.inc[node].getFirstRecord()
        while curr is not None:
            edge = curr.elem  # the edge
            head = edge.head  # the head node of the edge
            # if node v not yet added into the tree, push it into the priority
            # queue and apply the relaxation step
            if head not in mst_nodes:
                weight = edge.weight
                pq.insert((head, edge), weight)
                # relaxation step
                if current_weight[head] > weight:
                    current_weight[head] = weight
            curr = curr.next

    return mst_weight, tree
Пример #7
0
            else:
                nodeList = [[primoElemento, secondoElemento], first]
        else:
            # Se il numero di elementi nel percorso è dispari, prendo quello che sta a metà
            nodeList = [percorso[int(len(percorso) / 2)], int(len(percorso) / 2)]

        return nodeList

    def calculateSubNode(self, rootId):
            counter = 0  # Contatore degli elementi figli del nodo
          # Utilizzo l'algoritmo per la visita generica visto a lezione

        if rootId not in self.nodes:
            return None

        treeNode = TreeNode(rootId)
        vertexSet = {treeNode}
        markedNodes = {rootId}


        while len(vertexSet) > 0:
                treeNode = vertexSet.pop()
                adjacentNodes = self.getAdj(treeNode.info)
                for nodeIndex in adjacentNodes:
                    if nodeIndex not in markedNodes:
                        counter = counter + 1  # Incremento il contatore
                        newTreeNode = TreeNode(nodeIndex)
                        vertexSet.add(newTreeNode)
                        markedNodes.add(nodeIndex)

        return counter
def Dijkstra(graph, root):
    """
    Dijkstra's algorithm for the computation of the shortest path.
    The algorithm assumes a graph implemented as incidence list.
    This implementation leverages the BinaryTree and PriorityQueue data
    structures.
    ---
    Time Complexity: O(|E|*log(|V|))
    Memory Complexity: O()

    :param graph: the graph.
    :param root: the root node to start from.
    :return: the shortest path.
    """
    n = len(graph.nodes)

    # initialize weights:
    # d[i] = inf for every i in E
    # d[i] = 0, if i == root
    currentWeight = n * [INFINITE]
    currentWeight[root] = 0

    # initialize the tree
    tree = Tree(TreeNode(root))
    mstNodes = {root}
    mstWeight = 0

    # initialize the frontier (priority queue)
    pq = PriorityQueue()
    pq.insert((root, Edge(root, root, 0)), 0)

    # while the frontier is not empty ...
    while not pq.isEmpty():
        # pop from the priority queue the node u with the minimum weight
        pq_elem = pq.popMin()
        node = pq_elem[0]
        # if node not yet in the tree, update the tree
        if node not in mstNodes:
            edge = pq_elem[1]
            treeNode = TreeNode(node)
            father = tree.foundNodeByElem(edge.tail)
            father.sons.append(treeNode)
            treeNode.father = father
            mstNodes.add(node)
            mstWeight += edge.weight

        # for every edge (u,v) ...
        curr = graph.inc[node].getFirstRecord()
        while curr is not None:
            edge = curr.elem  # the edge
            head = edge.head  # the head node of the edge
            # if node v not yet added into the tree, push it into the priority
            # queue and apply the relaxation step
            if head not in mstNodes:
                tail = edge.tail
                weight = edge.weight
                currWeight = currentWeight[head]
                distTail = currentWeight[tail]
                pq.insert((head, edge), distTail + weight)
                # relaxation step
                if currWeight == INFINITE:
                    currentWeight[head] = distTail + weight
                elif distTail + weight < currWeight:
                    currentWeight[head] = distTail + weight
            curr = curr.next

    return currentWeight