web-dev-qa-db-fra.com

Recherche de tous les cycles dans les graphiques non orientés

J'ai besoin d'un algorithme de travail pour trouver tous les cycles simples dans un graphe non orienté. Je sais que le coût peut être exponentiel et que le problème est NP-complet, mais je vais l'utiliser dans un petit graphique (jusqu'à 20 à 30 sommets) et les cycles sont peu nombreux.

Après de longues recherches (principalement ici), je n'ai toujours pas de méthode de travail. Voici un résumé de ma recherche:

Trouver tous les cycles dans un graphe non orienté

Les cycles dans un graphe non dirigé -> détecte uniquement s'il y a un cycle ou non

Trouver des polygones dans un graphe non orienté -> description très agréable, mais pas de solution

Recherche de tous les cycles dans un graphe orienté -> recherche les cycles uniquement dans les graphes orientés

Détecter les cycles dans un graphe non orienté à l'aide de la bibliothèque de graphes boost

La seule réponse que j'ai trouvée, qui aborde mon problème, est celle-ci:

Trouver tous les cycles dans graphe, redux

Il semble que la recherche d'un ensemble de cycles de base et leur XOR-ing pourrait faire l'affaire. Trouver un ensemble de cycles de base est facile, mais je ne comprends pas comment les combiner pour obtenir tous les cycles du graphique ...

51
Violin Yanev

Pour un graphe non orienté, l’approche standard consiste à rechercher une base dite de cycle: un ensemble de cycles simples à partir desquels on peut générer par des combinaisons tous les autres cycles. Ce ne sont pas nécessairement tous des cycles simples dans le graphique. Considérons par exemple le graphique suivant:

    A 
  /   \
B ----- C
  \   /
    D

Il y a 3 cycles simples ici: A-B-C-A, B-C-D-B et A-B-D-C-A. Vous pouvez toutefois prendre chacun deux des deux comme base et obtenir le troisième comme une combinaison des deux. Il s'agit d'une différence substantielle par rapport aux graphes orientés dans lesquels on ne peut pas combiner des cycles aussi librement en raison de la nécessité d'observer la direction du contour.

L'algorithme de base standard permettant de trouver une base de cycle pour un graphe non dirigé est le suivant: Construisez un arbre recouvrant, puis pour chaque bord n'appartenant pas à l'arborescence, créez un cycle à partir de ce bord et de quelques arêtes de l'arborescence. Un tel cycle doit exister car sinon, Edge ferait partie de l’arbre.

Par exemple, l'un des arbres recouvrants possibles pour l'exemple de graphique ci-dessus est le suivant:

    A 
  /   \
B      C
  \ 
    D

Les 2 arêtes qui ne sont pas dans l'arbre sont B-C et C-D. Et les cycles simples correspondants sont A-B-C-A et A-B-D-C-A. 

Vous pouvez également créer l'arbre couvrant suivant:

    A 
  /   
B ----- C
  \   
    D

Et pour cet arbre couvrant, les cycles simples seraient A-B-C-A et B-C-D-B.

L'algorithme de base peut être affiné de différentes manières. Au meilleur de ma connaissance, le meilleur raffinement appartient à Paton (K. Paton, algorithme permettant de trouver un ensemble fondamental de cycles pour un graphe linéaire non orienté, Comm. ACM 12 (1969), p. 514-518.). Une implémentation open source en Java est disponible ici: http://code.google.com/p/niographs/

J'aurais dû mentionner comment vous combinez des cycles simples à partir de la base de cycles pour former de nouveaux cycles simples. Vous commencez par lister dans n'importe quel ordre (mais fixé ci-après) tous les bords du graphique. Ensuite, vous représentez les cycles par des séquences de zéros et des uns en plaçant ceux-ci dans les positions des arêtes appartenant au cycle et les zéros dans les positions des arêtes ne faisant pas partie du cycle. Ensuite, vous faites un bitwise OR exclusif (XOR) des séquences. La raison pour laquelle vous utilisez XOR est que vous souhaitez exclure les arêtes appartenant aux deux cycles et rendre ainsi le cycle combiné non simple. Vous devez également vérifier que les 2 cycles ont CERTAINES arêtes communes en vérifiant que le AND au niveau du bit des séquences ne correspond pas à des zéros. Sinon, le résultat de XOR sera 2 cycles disjoints plutôt qu'un nouveau cycle simple.

Voici un exemple pour l'exemple de graphique ci-dessus:

Nous commençons par énumérer les arêtes: ((AB), (AC), (BC), (BD), (CD)). Ensuite, les cycles simples A-B-C-A, B-D-C-B et A-B-D-C-A sont représentés par (1, 1, 1, 0, 0), (0, 0, 1, 1, 1) et (1, 1, 0, 1, 1). Maintenant, nous pouvons par exemple XOR A-B-C-A avec B-D-C-B et le résultat est (1, 1, 0, 1, 1), ce qui correspond exactement à A-B-D-C-A. Ou nous pouvons XOR A-B-C-A et A-B-D-C-A avec le résultat obtenu (0, 0, 1, 1, 1). Qui est exactement B-D-C-B.

Avec une base de cycle, vous pouvez découvrir tous les cycles simples en examinant toutes les combinaisons possibles d'au moins 2 cycles de base distincts. La procédure est décrite plus en détail ici: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf à la page 14.

Par souci de complétude, je remarquerais qu'il semble possible (et inefficace) d'utiliser des algorithmes pour trouver tous les cycles simples d'un graphe dirigé. Chaque bord du graphe non dirigé peut être remplacé par 2 bords dirigés dans des directions opposées. Alors, les algorithmes pour les graphes dirigés devraient fonctionner. Il y aura 1 "faux" cycle à 2 nœuds pour chaque bord du graphe non dirigé qui devra être ignoré et il y aura une version dans le sens des aiguilles d'une montre et dans le sens inverse des aiguilles d'une montre de chaque cycle simple du graphe non dirigé. L'implémentation open source en Java d'algorithmes permettant de trouver tous les cycles dans un graphe orienté se trouve sur le lien que j'ai déjà cité.

38
Nikolay Ognyanov

Ce qui suit est une implémentation de démonstration en C # (et Java, voir fin de réponse) basée sur la recherche en profondeur en premier.

Une boucle externe analyse tous les nœuds du graphique et lance une recherche à partir de chaque nœud. Les voisins de nœud (en fonction de la liste des arêtes) sont ajoutés à la piste cyclable. La récursivité se termine si aucun autre voisin non visité ne peut être ajouté. Un nouveau cycle est trouvé si le chemin est plus long que deux nœuds et que le prochain voisin est le début du chemin. Pour éviter les cycles en double, les cycles sont normalisés en faisant tourner le plus petit noeud au début. Les cycles dans l'ordre inversé sont également pris en compte.

Ceci est juste une implémentation naïve . Le papier classique est: Donald B. Johnson. Trouver tous les circuits élémentaires d'un graphe dirigé. SIAM J. Comput., 4 (1): 77-84, 1975.

Une étude récente des algorithmes modernes peut être trouvée ici

using System;
using System.Collections.Generic;

namespace akCyclesInUndirectedGraphs
{
    class Program
    {
        //  Graph modelled as list of edges
        static int[,] graph =
            {
                {1, 2}, {1, 3}, {1, 4}, {2, 3},
                {3, 4}, {2, 6}, {4, 6}, {7, 8},
                {8, 9}, {9, 7}
            };

        static List<int[]> cycles = new List<int[]>();

        static void Main(string[] args)
        {
            for (int i = 0; i < graph.GetLength(0); i++)
                for (int j = 0; j < graph.GetLength(1); j++)
                {
                    findNewCycles(new int[] {graph[i, j]});
                }

            foreach (int[] cy in cycles)
            {
                string s = "" + cy[0];

                for (int i = 1; i < cy.Length; i++)
                    s += "," + cy[i];

                Console.WriteLine(s);
            }
        }

        static void findNewCycles(int[] path)
        {
                int n = path[0];
                int x;
                int[] sub = new int[path.Length + 1];

                for (int i = 0; i < graph.GetLength(0); i++)
                    for (int y = 0; y <= 1; y++)
                        if (graph[i, y] == n)
                        //  Edge referes to our current node
                        {
                            x = graph[i, (y + 1) % 2];
                            if (!visited(x, path))
                            //  neighbor node not on path yet
                            {
                                sub[0] = x;
                                Array.Copy(path, 0, sub, 1, path.Length);
                                //  explore extended path
                                findNewCycles(sub);
                            }
                            else if ((path.Length > 2) && (x == path[path.Length - 1]))
                            //  cycle found
                            {
                                int[] p = normalize(path);
                                int[] inv = invert(p);
                                if (isNew(p) && isNew(inv))
                                    cycles.Add(p);
                            }
                        }
        }

        static bool equals(int[] a, int[] b)
        {
            bool ret = (a[0] == b[0]) && (a.Length == b.Length);

            for (int i = 1; ret && (i < a.Length); i++)
                if (a[i] != b[i])
                {
                    ret = false;
                }

            return ret;
        }

        static int[] invert(int[] path)
        {
            int[] p = new int[path.Length];

            for (int i = 0; i < path.Length; i++)
                p[i] = path[path.Length - 1 - i];

            return normalize(p);
        }

        //  rotate cycle path such that it begins with the smallest node
        static int[] normalize(int[] path)
        {
            int[] p = new int[path.Length];
            int x = smallest(path);
            int n;

            Array.Copy(path, 0, p, 0, path.Length);

            while (p[0] != x)
            {
                n = p[0];
                Array.Copy(p, 1, p, 0, p.Length - 1);
                p[p.Length - 1] = n;
            }

            return p;
        }

        static bool isNew(int[] path)
        {
            bool ret = true;

            foreach(int[] p in cycles)
                if (equals(p, path))
                {
                    ret = false;
                    break;
                }

            return ret;
        }

        static int smallest(int[] path)
        {
            int min = path[0];

            foreach (int p in path)
                if (p < min)
                    min = p;

            return min;
        }

        static bool visited(int n, int[] path)
        {
            bool ret = false;

            foreach (int p in path)
                if (p == n)
                {
                    ret = true;
                    break;
                }

            return ret;
        }
    }
}

Les cycles pour le graphique de démonstration:

1,3,2
1,4,3,2
1,4,6,2
1,3,4,6,2
1,4,6,2,3
1,4,3
2,6,4,3
7,9,8

L'algorithme codé en Java:

import Java.util.ArrayList;
import Java.util.List;

public class GraphCycleFinder {

    //  Graph modeled as list of edges
    static int[][] graph =
        {
            {1, 2}, {1, 3}, {1, 4}, {2, 3},
            {3, 4}, {2, 6}, {4, 6}, {7, 8},
            {8, 9}, {9, 7}
        };

    static List<int[]> cycles = new ArrayList<int[]>();

    /**
     * @param args
     */
    public static void main(String[] args) {

        for (int i = 0; i < graph.length; i++)
            for (int j = 0; j < graph[i].length; j++)
            {
                findNewCycles(new int[] {graph[i][j]});
            }

        for (int[] cy : cycles)
        {
            String s = "" + cy[0];

            for (int i = 1; i < cy.length; i++)
            {
                s += "," + cy[i];
            }

            o(s);
        }

    }

    static void findNewCycles(int[] path)
    {
            int n = path[0];
            int x;
            int[] sub = new int[path.length + 1];

            for (int i = 0; i < graph.length; i++)
                for (int y = 0; y <= 1; y++)
                    if (graph[i][y] == n)
                    //  Edge refers to our current node
                    {
                        x = graph[i][(y + 1) % 2];
                        if (!visited(x, path))
                        //  neighbor node not on path yet
                        {
                            sub[0] = x;
                            System.arraycopy(path, 0, sub, 1, path.length);
                            //  explore extended path
                            findNewCycles(sub);
                        }
                        else if ((path.length > 2) && (x == path[path.length - 1]))
                        //  cycle found
                        {
                            int[] p = normalize(path);
                            int[] inv = invert(p);
                            if (isNew(p) && isNew(inv))
                            {
                                cycles.add(p);
                            }
                        }
                    }
    }

    //  check of both arrays have same lengths and contents
    static Boolean equals(int[] a, int[] b)
    {
        Boolean ret = (a[0] == b[0]) && (a.length == b.length);

        for (int i = 1; ret && (i < a.length); i++)
        {
            if (a[i] != b[i])
            {
                ret = false;
            }
        }

        return ret;
    }

    //  create a path array with reversed order
    static int[] invert(int[] path)
    {
        int[] p = new int[path.length];

        for (int i = 0; i < path.length; i++)
        {
            p[i] = path[path.length - 1 - i];
        }

        return normalize(p);
    }

    //  rotate cycle path such that it begins with the smallest node
    static int[] normalize(int[] path)
    {
        int[] p = new int[path.length];
        int x = smallest(path);
        int n;

        System.arraycopy(path, 0, p, 0, path.length);

        while (p[0] != x)
        {
            n = p[0];
            System.arraycopy(p, 1, p, 0, p.length - 1);
            p[p.length - 1] = n;
        }

        return p;
    }

    //  compare path against known cycles
    //  return true, iff path is not a known cycle
    static Boolean isNew(int[] path)
    {
        Boolean ret = true;

        for(int[] p : cycles)
        {
            if (equals(p, path))
            {
                ret = false;
                break;
            }
        }

        return ret;
    }

    static void o(String s)
    {
        System.out.println(s);
    }

    //  return the int of the array which is the smallest
    static int smallest(int[] path)
    {
        int min = path[0];

        for (int p : path)
        {
            if (p < min)
            {
                min = p;
            }
        }

        return min;
    }

    //  check if vertex n is contained in path
    static Boolean visited(int n, int[] path)
    {
        Boolean ret = false;

        for (int p : path)
        {
            if (p == n)
            {
                ret = true;
                break;
            }
        }

        return ret;
    }

}
29
Axel Kemper

Axel, j'ai traduit votre code en python. Environ 1/4 des lignes de code et plus clair à lire.

graph = [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4], [2, 6], [4, 6], [8, 7], [8, 9], [9, 7]]
cycles = []

def main():
    global graph
    global cycles
    for Edge in graph:
        for node in Edge:
            findNewCycles([node])
    for cy in cycles:
        path = [str(node) for node in cy]
        s = ",".join(path)
        print(s)

def findNewCycles(path):
    start_node = path[0]
    next_node= None
    sub = []

    #visit each Edge and each node of each Edge
    for Edge in graph:
        node1, node2 = Edge
        if start_node in Edge:
                if node1 == start_node:
                    next_node = node2
                else:
                    next_node = node1
        if not visited(next_node, path):
                # neighbor node not on path yet
                sub = [next_node]
                sub.extend(path)
                # explore extended path
                findNewCycles(sub);
        Elif len(path) > 2  and next_node == path[-1]:
                # cycle found
                p = rotate_to_smallest(path);
                inv = invert(p)
                if isNew(p) and isNew(inv):
                    cycles.append(p)

def invert(path):
    return rotate_to_smallest(path[::-1])

#  rotate cycle path such that it begins with the smallest node
def rotate_to_smallest(path):
    n = path.index(min(path))
    return path[n:]+path[:n]

def isNew(path):
    return not path in cycles

def visited(node, path):
    return node in path

main()
27
LetterRip

Voici une version MATLAB très boiteuse de cet algorithme, adaptée du code python ci-dessus, pour tous ceux qui pourraient en avoir besoin.

function cycleList = searchCycles(edgeMap)

    tic
    global graph cycles numCycles;
    graph = edgeMap;
    numCycles = 0;
    cycles = {};
    for i = 1:size(graph,1)
        for j = 1:2
            findNewCycles(graph(i,j))
        end
    end
    % print out all found cycles
    for i = 1:size(cycles,2)
        cycles{i}
    end
    % return the result
    cycleList = cycles;
    toc

function findNewCycles(path)

    global graph cycles numCycles;
    startNode = path(1);
    nextNode = nan;
    sub = [];

    % visit each Edge and each node of each Edge
    for i = 1:size(graph,1)
        node1 = graph(i,1);
        node2 = graph(i,2);
        if node1 == startNode
            nextNode = node2;
        elseif node2 == startNode
            nextNode = node1;
        end
        if ~(visited(nextNode, path))
            % neighbor node not on path yet
            sub = nextNode;
            sub = [sub path];
            % explore extended path
            findNewCycles(sub);
        elseif size(path,2) > 2 && nextNode == path(end)
            % cycle found
            p = rotate_to_smallest(path);
            inv = invert(p);
            if isNew(p) && isNew(inv)
                numCycles = numCycles + 1;
                cycles{numCycles} = p;
            end
        end
    end

function inv = invert(path)
    inv = rotate_to_smallest(path(end:-1:1));

% rotate cycle path such that it begins with the smallest node
function new_path = rotate_to_smallest(path)
    [~,n] = min(path);
    new_path = [path(n:end), path(1:n-1)];

function result = isNew(path)
    global cycles
    result = 1;
    for i = 1:size(cycles,2)
        if size(path,2) == size(cycles{i},2) && all(path == cycles{i})
            result = 0;
            break;
        end
    end

function result = visited(node,path)
    result = 0;
    if isnan(node) && any(isnan(path))
        result = 1;
        return
    end
    for i = 1:size(path,2)
        if node == path(i)
            result = 1;
            break
        end
    end
3
Alan Wang

Inspiré par @LetterRip et @Axel KemperVoici une version plus courte de Java:

public static int[][] graph =
        {
                {1, 2}, {2, 3}, {3, 4}, {2, 4},
                {3, 5}
        };
public static Set<List<Integer>> cycles = new HashSet<>();

static void findNewCycles(ArrayList<Integer> path) {
    int start = path.get(0);
    int next = -1;
    for (int[] Edge : graph) {
        if (start == Edge[0] || start == Edge[1]) {
            next = (start == Edge[0]) ? Edge[1] : Edge[0];
            if (!path.contains(next)) {
                ArrayList<Integer> newPath = new ArrayList<>();
                newPath.add(next);
                newPath.addAll((path));
                findNewCycles(newPath);
            } else if (path.size() > 2 && next == path.get(path.size() - 1)) {
                List<Integer> normalized = new ArrayList<>(path);
                Collections.sort(normalized);
                cycles.add(normalized);
            }
        }
    }
}

public static void detectCycle() {
    for (int i = 0; i < graph.length; i++)
        for (int j = 0; j < graph[i].length; j++) {
            ArrayList<Integer> path = new ArrayList<>();
            path.add(graph[i][j]);
            findNewCycles(path);
        }
    for (List<Integer> c : cycles) {
        System.out.println(c);
    }
}
3
Patrick

Voici une version C++ du code python ci-dessus:

std::vector< std::vector<vertex_t> > Graph::findAllCycles()
{
    std::vector< std::vector<vertex_t> > cycles;

    std::function<void(std::vector<vertex_t>)> findNewCycles = [&]( std::vector<vertex_t> sub_path )
    {
        auto visisted = []( vertex_t v, const std::vector<vertex_t> & path ){
            return std::find(path.begin(),path.end(),v) != path.end();
        };

        auto rotate_to_smallest = []( std::vector<vertex_t> path ){
            std::rotate(path.begin(), std::min_element(path.begin(), path.end()), path.end());
            return path;
        };

        auto invert = [&]( std::vector<vertex_t> path ){
            std::reverse(path.begin(),path.end());
            return rotate_to_smallest(path);
        };

        auto isNew = [&cycles]( const std::vector<vertex_t> & path ){
            return std::find(cycles.begin(), cycles.end(), path) == cycles.end();
        };

        vertex_t start_node = sub_path[0];
        vertex_t next_node;

        // visit each Edge and each node of each Edge
        for(auto Edge : edges)
        {
            if( Edge.has(start_node) )
            {
                vertex_t node1 = Edge.v1, node2 = Edge.v2;

                if(node1 == start_node)
                    next_node = node2;
                else
                    next_node = node1;

                if( !visisted(next_node, sub_path) )
                {
                    // neighbor node not on path yet
                    std::vector<vertex_t> sub;
                    sub.Push_back(next_node);
                    sub.insert(sub.end(), sub_path.begin(), sub_path.end());
                    findNewCycles( sub );
                } 
                else if( sub_path.size() > 2 && next_node == sub_path.back() )
                {
                    // cycle found
                    auto p = rotate_to_smallest(sub_path);
                    auto inv = invert(p);

                    if( isNew(p) && isNew(inv) )
                        cycles.Push_back( p );
                }
            }
        }
    };

    for(auto Edge : edges)
    {
        findNewCycles( std::vector<vertex_t>(1,Edge.v1) );
        findNewCycles( std::vector<vertex_t>(1,Edge.v2) );
    }
}
2
ennetws

Voici une version vb .net du code python ci-dessus:

Module Module1
'  Graph modelled as list of edges
Public graph As Integer(,) = {{{1, 2}, {1, 3}, {1, 4}, {2, 3},
        {3, 4}, {2, 6}, {4, 6}, {7, 8},
        {8, 9}, {9, 7}}

Public cycles As New List(Of Integer())()
Sub Main()
    For i As Integer = 0 To graph.GetLength(0) - 1
        For j As Integer = 0 To graph.GetLength(1) - 1
            findNewCycles(New Integer() {graph(i, j)})
        Next
    Next

    For Each cy As Integer() In cycles
        Dim s As String
        s = cy(0)
        For i As Integer = 1 To cy.Length - 1
            s = s & "," & cy(i)
        Next

        Console.WriteLine(s)
        Debug.Print(s)
    Next

End Sub
Private Sub findNewCycles(path As Integer())
    Dim n As Integer = path(0)
    Dim x As Integer
    Dim [sub] As Integer() = New Integer(path.Length) {}

    For i As Integer = 0 To graph.GetLength(0) - 1
        For y As Integer = 0 To 1
            If graph(i, y) = n Then
                '  Edge referes to our current node
                x = graph(i, (y + 1) Mod 2)
                If Not visited(x, path) Then
                    '  neighbor node not on path yet
                    [sub](0) = x
                    Array.Copy(path, 0, [sub], 1, path.Length)
                    '  explore extended path
                    findNewCycles([sub])
                ElseIf (path.Length > 2) AndAlso (x = path(path.Length - 1)) Then
                    '  cycle found
                    Dim p As Integer() = normalize(path)
                    Dim inv As Integer() = invert(p)
                    If isNew(p) AndAlso isNew(inv) Then
                        cycles.Add(p)
                    End If
                End If
            End If
        Next
    Next
End Sub

Private Function equals(a As Integer(), b As Integer()) As Boolean
    Dim ret As Boolean = (a(0) = b(0)) AndAlso (a.Length = b.Length)

    Dim i As Integer = 1
    While ret AndAlso (i < a.Length)
        If a(i) <> b(i) Then
            ret = False
        End If
        i += 1
    End While

    Return ret
End Function

Private Function invert(path As Integer()) As Integer()
    Dim p As Integer() = New Integer(path.Length - 1) {}

    For i As Integer = 0 To path.Length - 1
        p(i) = path(path.Length - 1 - i)
    Next

    Return normalize(p)
End Function

'  rotate cycle path such that it begins with the smallest node
Private Function normalize(path As Integer()) As Integer()
    Dim p As Integer() = New Integer(path.Length - 1) {}
    Dim x As Integer = smallest(path)
    Dim n As Integer

    Array.Copy(path, 0, p, 0, path.Length)

    While p(0) <> x
        n = p(0)
        Array.Copy(p, 1, p, 0, p.Length - 1)
        p(p.Length - 1) = n
    End While

    Return p
End Function

Private Function isNew(path As Integer()) As Boolean
    Dim ret As Boolean = True

    For Each p As Integer() In cycles
        If equals(p, path) Then
            ret = False
            Exit For
        End If
    Next

    Return ret
End Function

Private Function smallest(path As Integer()) As Integer
    Dim min As Integer = path(0)

    For Each p As Integer In path
        If p < min Then
            min = p
        End If
    Next

    Return min
End Function

Private Function visited(n As Integer, path As Integer()) As Boolean
    Dim ret As Boolean = False

    For Each p As Integer In path
        If p = n Then
            ret = True
            Exit For
        End If
    Next

    Return ret
End Function

Module de fin

1
Triyan

Il semble que le Finder de cycle ci-dessus présente quelques problèmes. La version C # ne parvient pas à trouver certains cycles. Mon graphique est:

  {2,8},{4,8},{5,8},{1,9},{3,9},{4,9},{5,9},{6,9},{1,10},
  {4,10},{5,10},{6,10},{7,10},{1,11},{4,11},{6,11},{7,11},
  {1,12},{2,12},{3,12},{5,12},{6,12},{2,13},{3,13},{4,13},
  {6,13},{7,13},{2,14},{5,14},{7,14}

Par exemple, le cycle: 1-9-3-12-5-10 est introuvable. J’ai aussi essayé la version C++, elle renvoie un très grand nombre de dizaines de millions de cycles, ce qui est apparemment faux. Probablement, il ne correspond pas aux cycles.

Désolé, je suis un peu en crise et je n'ai pas enquêté davantage. J'ai écrit ma propre version basée sur le post de Nikolay Ognyanov (merci beaucoup pour votre post). Pour le graphique ci-dessus, ma version renvoie 8833 cycles et j'essaie de vérifier qu'elle est correcte. La version C # renvoie 8397 cycles.

0
Sergey

Voici une version de noeud du code python.

const graph = [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4], [2, 6], [4, 6], [8, 7], [8, 9], [9, 7]]
let cycles = []

function main() {
  for (const Edge of graph) {
    for (const node of Edge) {
      findNewCycles([node])
    }
  }
  for (cy of cycles) {
    console.log(cy.join(','))
  }
}

function findNewCycles(path) {
  const start_node = path[0]
  let next_node = null
  let sub = []

  // visit each Edge and each node of each Edge
  for (const Edge of graph) {
    const [node1, node2] = Edge
    if (Edge.includes(start_node)) {
      next_node = node1 === start_node ? node2 : node1
    }
    if (notVisited(next_node, path)) {
      // eighbor node not on path yet
      sub = [next_node].concat(path)
      // explore extended path
      findNewCycles(sub)
    } else if (path.length > 2 && next_node === path[path.length - 1]) {
      // cycle found
      const p = rotateToSmallest(path)
      const inv = invert(p)
      if (isNew(p) && isNew(inv)) {
        cycles.Push(p)
      }
    }
  }
}

function invert(path) {
  return rotateToSmallest([...path].reverse())
}

// rotate cycle path such that it begins with the smallest node
function rotateToSmallest(path) {
  const n = path.indexOf(Math.min(...path))
  return path.slice(n).concat(path.slice(0, n))
}

function isNew(path) {
  const p = JSON.stringify(path)
  for (const cycle of cycles) {
    if (p === JSON.stringify(cycle)) {
      return false
    }
  }
  return true
}

function notVisited(node, path) {
  const n = JSON.stringify(node)
  for (const p of path) {
    if (n === JSON.stringify(p)) {
      return false
    }
  }
  return true
}

main()
0
Kenzo