2010-06-24 4 views
9

Bien que cela puisse ressembler à des devoirs, je vous assure que ce n'est pas le cas. Cela vient cependant de certaines tâches que j'ai faites. Appelons un graphe non orienté sans auto-arêtes "cubique" si chaque sommet a un degré exactement trois. Étant donné un entier positif N, je voudrais générer un graphe cubique aléatoire sur N sommets. Je voudrais qu'il ait une probabilité uniforme, c'est-à-dire, s'il y a M graphes cubiques sur N sommets, la probabilité de générer chacun d'eux est 1/M. Une condition plus faible qui est encore bonne est que chaque graphe cubique a une probabilité non nulle.Génération d'un graphe cubique aléatoire avec probabilité uniforme (ou moins)

I sentir il y a une manière rapide et intelligente de faire ceci, mais jusqu'ici j'ai échoué.

Je suis un mauvais codeur, s'il vous plaît garder avec ce code terrible:

PRE: arêtes = (3 * nœuds)/2, les nœuds est même, les constantes sont choisies de telle sorte que les travaux de hachage (BIG_PRIME est plus grand que les bords, SMALL_PRIME est plus grand que les nœuds, LOAD_FACTOR est petit).

void random_cubic_graph() { 

int i, j, k, count; 
int *degree; 
char guard; 

count = 0; 
degree = (int*) calloc(nodes, sizeof(int)); 

while (count < edges) { 

    /* Try a new edge at random */ 

    guard = 0; 
    i = rand() % nodes; 
    j = rand() % nodes; 

    /* Checks if it is a self-edge */ 

    if (i == j) 
     guard = 1; 

    /* Checks that the degrees are 3 or less */ 

    if (degree[i] > 2 || degree[j] > 2) 
     guard = 1; 

    /* Checks that the edge was not already selected with an hash */ 

    k = 0; 
    while(A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] != 0) { 
     if (A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] % SMALL_PRIME == j) 
      if ((A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] - j)/SMALL_PRIME == i) 
       guard = 1; 
     k++; 
    } 

    if (guard == 0) 
     A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] = hash(i,j); 

    k = 0; 
    while(A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] != 0) { 
     if (A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] % SMALL_PRIME == i) 
      if ((A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] - i)/SMALL_PRIME == j) 
       guard = 1; 
     k++; 
    } 

    if (guard == 0) 
     A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] = hash(j,i); 

    /* If all checks were passed, increment the count, print the edge, increment the degrees. */ 

    if (guard == 0) { 
     count++; 
     printf("%d\t%d\n", i, j); 
     degree[i]++; 
     degree[j]++; 
    } 

} 

Le problème est que son dernier bord devant être sélectionné pourrait être un auto-contour. Cela se produit lorsque N - 1 sommets ont déjà le degré 3, seulement 1 a le degré 1. Ainsi, l'algorithme peut ne pas se terminer. De plus, je ne suis pas entièrement convaincu que la probabilité soit uniforme.

Il y a probablement beaucoup à améliorer dans mon code, mais pouvez-vous suggérer un meilleur algorithme à implémenter?

+2

Je suggère de ne pas utiliser le langage C pour les algorithmes de graphes lorsque vous êtes juste un débutant. –

+0

Alors, représentez-vous votre graphique en tant que matrice carrée? Au fait, que sont ces affaires small_prime, big_prime, et load_factor? J'ai l'impression que vous avez copié la solution de quelqu'un d'autre et que vous essayez d'en comprendre le sens. –

+2

Il n'y a pas de matrice carrée: A est un vecteur de longueur LOAD_FACTOR * arêtes qui contient les arêtes. Imaginons simplement qu'il existe une fonction blackbox is_edge_present (int i, int j) qui vérifie si le bord (i, j) a déjà été sélectionné. Cet extrait de code fait cela, et si le bord n'a pas été sélectionné, il le sélectionne pour les recherches futures. N'est-il pas un peu impoli de supposer que j'ai copié? J'ai écrit ça. C'est alambiqué et désordonné, mais c'est pourquoi il y a une balise pour débutant. –

Répondre

10

Supposons que N est pair. (Sinon, il ne peut pas y avoir de graphe cubique sur N sommets).

Vous pouvez effectuer les opérations suivantes:

prendre des points 3N et les diviser en groupes N de 3 points chacun.

Maintenant jumeler ces 3N points de façon aléatoire (note: 3N est pair). c'est-à-dire épouser deux points au hasard et former des mariages 3N/2).

S'il existe un appariement entre le groupe i et le groupe j, créez une arête entre i et j. Cela donne un graphique sur N sommets.

Si cette association aléatoire ne crée pas de bords ou de boucles multiples, vous disposez d'un graphique cubique.

Si non, réessayez. Cela s'exécute dans le temps linéaire prévu et génère une distribution uniforme.

Note: tous les graphes cubiques sur N sommets sont générés par cette méthode (répondant aux commentaires de Hamish).

Pour voir ceci:

Soit G un graphe cubique sur les sommets N.

Soit les sommets, 1, 2, ... N. Laisser les trois voisins de j être A (j), B (j) et C (j).

Pour chaque j, construisez le groupe de paires ordonnées {(j, A (j)), (j, B (j)), (j, C (j))}.

Cela nous donne 3N paires ordonnées. Nous les couplons: (u, v) est associé à (v, u).

Ainsi, tout graphique cube correspond à un appariement et vice versa ...

Plus d'informations sur cet algorithme et des algorithmes plus rapides peuvent être trouvés ici: Generating Random Regular Graphs Quickly.

+0

Et si cela manquait des "graphiques cubes"? Que faire si certains doivent être générés en utilisant une autre méthode? –

+0

Merci, je pense que cela règle ma question. Je vais attendre quelques heures avant d'accepter votre réponse au cas où il y aurait une solution encore meilleure! –

+1

@Hamish: Voir ma modification. Tous les graphiques cubes sont générés ... –

2

Avertissement: Je fais beaucoup de revendications intuitives mais peut-être fausses dans cette réponse. Vous devriez certainement les prouver si vous avez l'intention d'utiliser cette idée.

Énumération Graphiques cubes

Lorsque vous traitez avec un choix aléatoire, un bon point de départ est de savoir comment énumérer sur tous vos éléments possibles. Cela pourrait révéler une partie de la structure et vous conduire à un algorithme.

Voici ma stratégie pour l'énumération des graphes cubiques: choisissez le premier sommet, et parcourez tous les choix possibles de trois sommets adjacents. Au cours de ces itérations, recurse sur le sommet suivant, avec la mise en garde que vous gardez une trace du nombre d'arêtes nécessaires pour que chaque degré de sommet atteigne 3. Continuez de cette manière jusqu'à ce que le niveau le plus bas soit atteint. Maintenant vous avez votre premier graphique cubique. Annuler les bords ajoutés récemment et continuer à la prochaine possibilité jusqu'à ce qu'il n'y en reste plus. Il y a quelques détails d'implémentation que vous devez prendre en compte, mais généralement simples.

Généraliser Enumeration dans Choix

Une fois que vous pouvez énumérer tous les éléments, il est trivial de faire un choix au hasard. Par exemple, vous pouvez numériser la liste une fois pour calculer sa taille, puis choisir un nombre aléatoire dans [0, taille) puis scanner à nouveau la séquence pour obtenir l'élément à ce décalage. Ceci est incroyablement inefficace, prenant au MOINS temps proportionnellement au nombre de graphes cubiques O (n^3), mais cela fonctionne.

Sacrifice probabilité uniforme pour l'efficacité

La vitesse-up évidente ici est de faire des choix de pointe au hasard à chaque niveau, au lieu de itérer sur chaque possibilité. Malheureusement, cela favorisera certains graphiques en raison de la façon dont vos premiers choix affectent la disponibilité des choix ultérieurs. En tenant compte de la nécessité de suivre les sommets libres restants, vous devriez être en mesure d'atteindre le temps O (n log n) et l'espace O (n). Significativement meilleur que l'algorithme énumérant.

...

Il est sans doute possible de faire mieux. Probablement beaucoup mieux. Mais cela devrait vous aider à démarrer.

+0

Ceci est une stratégie générale intéressante que j'ai oubliée. Je pourrais vouloir recourir à cette stratégie la prochaine fois. Je vous remercie! –

+0

Notez que vous pouvez choisir un élément de manière aléatoire au hasard à partir d'une liste en l'analysant une seule fois, pas besoin de calculer la taille en premier: http://en.wikipedia.org/wiki/Reservoir_sampling (let k = 1) – Paulpro

1

Un autre terme pour cubic graph est 3- regular graph ou graphique trivalent.

Votre problème a besoin d'un peu plus de précisions, car « le nombre de graphes cubiques » pourrait signifier le nombre de graphes cubiques sur 2 n nœuds qui ne sont pas isomorphe à un autre ou le nombre de (non isomorphes) cube graphiques sur 2 n nœuds marqués. Le premier est donné par la séquence entière A005638, et il est probable qu'un problème non trivial consiste à choisir de manière uniforme une classe d'isomorphisme aléatoire de graphes cubiques de manière efficace (c'est-à-dire à ne pas les énumérer et à sélectionner une classe). Ce dernier est donné par A002829.

Il ya un article sur Wikipedia au sujet de random regular graphs que vous devriez jeter un oeil.

+1

[ Le graphique cubique] (http://en.wikipedia.org/wiki/Cubic_graph) est un terme parfaitement standard. –

+0

Merci pour la clarification. Je suis à la recherche d'un graphique 3-régulier sans boucles sur les noeuds labellisés 2n. Je dois vous corriger: je ne cherche pas de multi-octets, en fait je rejette une arête (i, j) si elle a déjà été sélectionnée. Merci pour le lien wiki, puisqu'il fournit le même algorithme de la première réponse. –

+0

@BlueRaja - Danny Pflughoeft: C'est vrai. Je dois mettre à jour ma réponse. –

Questions connexes