Cuente los ciclos terminales de un gráfico dirigido

8

Tarea

Debe escribir un programa o función en el idioma de su elección que cuente con precisión el número de ciclos terminales de un gráfico dirigido simple.

Este tipo particular de gráfico dirigido se representa como una matriz de n enteros, cada uno con un valor aleatorio elegido independientemente entre 1 yn (o 0 y n-1, si su idioma cuenta desde 0). El gráfico puede considerarse como flechas que apuntan desde un índice (nodo) a un índice que coincide con el valor encontrado en el índice inicial.

Su función debe ser capaz de aceptar gráficos grandes, hasta n = 1024, o cualquier tamaño entero más pequeño.

Ejemplo

Considere este gráfico para n = 10:

[9, 7, 8, 10, 2, 6, 3, 10, 6, 8]

El índice 1 contiene un 9, por lo que hay una flecha desde el índice 1 al índice 9. El índice 9 contiene un 6, por lo que hay una flecha 9 -> 6. El índice 6 contiene 6, que es un ciclo terminal, que apunta hacia sí mismo.

El índice 2 contiene un 7. El índice 7 contiene un 3. El índice 3 contiene un 8. El índice 8 contiene un 10. El índice 10 contiene un 8, entonces ese es un segundo ciclo terminal (8 -> 10 -> 8 -> 10, etc. )

Índice 4 -> 10, que ingresa al segundo ciclo terminal. Asimismo, indexe 5 -> 2 -> 7 -> 3 -> 8, que también es parte del segundo ciclo terminal.

En este punto, se han verificado todos los índices (nodos), se han seguido todas las rutas y se han identificado dos ciclos terminales únicos. Por lo tanto, la función debe devolver 2 , ya que ese es el número de ciclos terminales en este gráfico dirigido.

Puntuación

Apunte al código más pequeño, pero asegúrese de que cuente los ciclos de terminal correctamente. El código más corto después de 1 semana gana.

Casos de prueba

Aquí hay algunos casos de prueba para verificar la exactitud de su código. Si su idioma cuenta los índices de la matriz a partir de 0, por supuesto, debe restar 1 del valor de cada elemento de la matriz, para evitar un índice fuera de límite.

n = 32, 5 ciclos:

[8, 28, 14, 8, 2, 1, 13, 15, 30, 17, 9, 8, 18, 19, 30, 3, 8, 25, 23, 12, 6, 7, 19, 24, 17, 7, 21, 20, 29, 15, 32, 32]

n = 32, 4 ciclos:

[20, 31, 3, 18, 18, 18, 8, 12, 25, 10, 10, 19, 3, 9, 18, 1, 13, 5, 18, 23, 20, 26, 16, 22, 4, 16, 19, 31, 21, 32, 15, 22]

n = 32, 3 ciclos:

[28, 13, 17, 14, 4, 31, 11, 4, 22, 6, 32, 1, 13, 15, 7, 19, 10, 28, 9, 22, 5, 26, 17, 8, 6, 13, 7, 10, 9, 30, 23, 25]

n = 32, 2 ciclos:

[25, 23, 22, 6, 24, 3, 1, 21, 6, 18, 20, 4, 8, 5, 16, 10, 15, 32, 26, 25, 27, 14, 13, 12, 9, 9, 29, 8, 13, 31, 32, 1]

n = 32, 1 ciclo:

[6, 21, 15, 14, 22, 12, 5, 32, 29, 3, 22, 23, 6, 16, 20, 2, 16, 25, 9, 22, 13, 2, 19, 20, 26, 19, 32, 3, 32, 19, 28, 16]

n = 32, 1 ciclo:

[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 1, 2, 3, 4, 5, 6, 7]

n = 1024, 6 ciclos:

[239, 631, 200, 595, 178, 428, 582, 191, 230, 551, 223, 61, 564, 463, 568, 527, 143, 403, 154, 236, 928, 650, 14, 931, 236, 170, 910, 782, 861, 464, 378, 748, 468, 779, 440, 396, 467, 630, 451, 130, 694, 167, 594, 115, 671, 853, 612, 238, 464, 771, 825, 471, 167, 653, 561, 337, 585, 986, 79, 506, 192, 873, 184, 617, 4, 259, 4, 662, 623, 694, 859, 6, 346, 431, 181, 703, 823, 140, 635, 90, 559, 689, 118, 117, 130, 248, 931, 767, 840, 158, 696, 275, 610, 217, 989, 640, 363, 91, 129, 399, 105, 770, 870, 800, 429, 473, 119, 908, 481, 337, 504, 45, 1011, 684, 306, 126, 215, 729, 771, 5, 302, 992, 380, 824, 868, 205, 807, 917, 407, 759, 181, 640, 685, 795, 258, 180, 900, 20, 773, 546, 866, 564, 761, 632, 895, 968, 980, 651, 225, 676, 18, 685, 784, 208, 227, 3, 267, 852, 57, 487, 566, 633, 849, 309, 543, 145, 575, 811, 621, 560, 492, 24, 665, 66, 851, 168, 262, 259, 754, 481, 565, 768, 172, 1012, 241, 3, 370, 985, 389, 82, 779, 744, 829, 836, 249, 975, 909, 840, 226, 867, 499, 192, 909, 972, 735, 252, 785, 545, 486, 186, 1011, 89, 939, 649, 110, 119, 185, 836, 717, 545, 938, 621, 946, 94, 363, 721, 177, 747, 59, 819, 146, 283, 821, 547, 654, 941, 755, 18, 449, 367, 499, 944, 62, 553, 435, 344, 900, 25, 251, 920, 902, 99, 326, 98, 495, 385, 929, 865, 327, 725, 674, 33, 173, 429, 873, 558, 90, 460, 366, 543, 583, 954, 792, 213, 536, 670, 49, 738, 802, 1015, 23, 915, 119, 263, 307, 601, 474, 971, 826, 613, 446, 37, 145, 894, 901, 307, 906, 886, 990, 89, 798, 384, 487, 822, 354, 768, 902, 163, 179, 134, 920, 439, 619, 215, 94, 709, 744, 366, 543, 349, 347, 2, 438, 141, 486, 19, 998, 500, 857, 955, 932, 1, 587, 195, 646, 550, 887, 626, 400, 348, 154, 808, 678, 873, 186, 282, 168, 993, 722, 56, 345, 5, 226, 328, 22, 894, 658, 264, 13, 803, 791, 359, 217, 997, 168, 578, 952, 734, 964, 898, 659, 628, 980, 15, 31, 439, 13, 875, 687, 1004, 1023, 165, 642, 561, 897, 711, 124, 404, 346, 723, 774, 352, 784, 276, 395, 14, 443, 343, 153, 510, 590, 172, 215, 130, 106, 295, 906, 133, 758, 483, 898, 391, 760, 702, 972, 721, 611, 592, 1001, 724, 934, 59, 831, 171, 253, 869, 431, 538, 20, 648, 76, 351, 103, 33, 385, 852, 437, 470, 95, 434, 408, 430, 994, 366, 706, 809, 532, 161, 388, 668, 245, 965, 365, 913, 471, 927, 245, 256, 805, 540, 380, 995, 446, 657, 545, 573, 955, 499, 322, 949, 635, 401, 185, 421, 626, 534, 429, 930, 633, 563, 348, 626, 518, 682, 233, 775, 444, 42, 199, 57, 271, 683, 397, 883, 620, 768, 8, 331, 497, 19, 340, 900, 919, 497, 276, 78, 252, 164, 764, 927, 242, 270, 759, 824, 945, 886, 262, 59, 439, 217, 720, 519, 862, 626, 326, 339, 589, 16, 565, 947, 604, 144, 87, 520, 256, 240, 336, 685, 361, 998, 805, 678, 24, 980, 203, 818, 855, 85, 276, 822, 183, 266, 347, 8, 663, 620, 147, 189, 497, 128, 357, 855, 507, 275, 420, 755, 131, 469, 672, 926, 859, 156, 127, 986, 489, 803, 433, 622, 951, 83, 862, 108, 192, 167, 862, 242, 519, 574, 358, 549, 119, 630, 60, 925, 414, 479, 330, 927, 94, 767, 562, 919, 1011, 999, 908, 113, 932, 632, 403, 309, 838, 341, 179, 708, 847, 472, 907, 537, 516, 992, 944, 615, 778, 801, 413, 653, 690, 393, 452, 394, 596, 545, 591, 136, 109, 942, 546, 57, 626, 61, 587, 862, 829, 988, 965, 781, 849, 843, 815, 60, 928, 784, 388, 341, 491, 565, 83, 110, 164, 38, 1024, 859, 297, 520, 327, 733, 699, 631, 78, 178, 671, 895, 818, 637, 99, 425, 933, 248, 299, 333, 144, 323, 105, 849, 942, 767, 265, 72, 204, 547, 934, 916, 304, 919, 273, 396, 665, 452, 423, 471, 641, 675, 60, 388, 97, 963, 902, 321, 826, 476, 782, 723, 99, 735, 893, 565, 175, 141, 70, 918, 659, 935, 492, 751, 261, 362, 849, 593, 924, 590, 982, 876, 73, 993, 767, 441, 70, 875, 640, 567, 920, 321, 46, 938, 377, 905, 303, 736, 182, 626, 899, 512, 894, 744, 254, 984, 325, 694, 6, 367, 532, 432, 133, 938, 74, 967, 725, 87, 502, 946, 708, 122, 887, 256, 595, 169, 101, 828, 696, 897, 961, 376, 910, 82, 144, 967, 885, 89, 114, 215, 187, 38, 873, 125, 522, 884, 947, 962, 45, 585, 644, 476, 710, 839, 486, 634, 431, 475, 979, 877, 18, 226, 656, 573, 3, 29, 743, 508, 544, 252, 254, 388, 873, 70, 640, 918, 93, 508, 853, 609, 333, 378, 172, 875, 617, 167, 771, 375, 503, 221, 624, 67, 655, 465, 272, 278, 161, 840, 52, 1016, 909, 567, 544, 234, 339, 463, 621, 951, 962, 1019, 383, 523, 279, 780, 838, 984, 999, 29, 897, 564, 762, 753, 393, 205, 31, 150, 490, 156, 796, 586, 676, 773, 465, 489, 1024, 433, 214, 701, 480, 604, 280, 241, 563, 943, 911, 12, 400, 261, 883, 999, 207, 618, 141, 959, 767, 978, 461, 992, 982, 272, 143, 404, 645, 331, 348, 783, 698, 827, 82, 145, 536, 449, 852, 750, 789, 413, 913, 420, 14, 499, 285, 533, 223, 75, 591, 994, 884, 237, 63, 411, 563, 611, 801, 173, 759, 278, 318, 772, 1018, 48, 440, 333, 611, 834, 423, 583, 22, 716, 393, 794, 83, 83, 864, 859, 600, 525, 808, 569, 95, 952, 852, 567, 651, 2, 984, 906, 992, 747, 602, 143, 547, 1008, 940, 245, 633, 378, 193, 771, 965, 648, 437, 873, 591, 664, 271, 777, 274, 742, 68, 429, 825, 144, 55, 272, 279, 6, 400, 485, 66, 311, 663, 441, 23, 988, 726, 48, 624, 302, 617, 120, 653, 810, 641, 142]
fosgeno
fuente
Claro, aquí hay un caso de prueba grande y gordo n = 1024. Espero no haberlo matado mientras lo empaquetaba para Stack Exchange.
fosgeno
Aclarar: ¿Si el lenguaje usa matrices basadas en cero, entonces los números en la matriz serán todos basados ​​en cero, o no?
Ypnypn
Si eso es correcto. Los números en la matriz siempre apuntarán a un índice de matriz válido. Utilicé el método de conteo 'estilo humano' para comenzar desde 1 porque eso es lo que usa mi lenguaje, y no quería arruinar mis ejemplos.
fosgeno

Respuestas:

2

GolfScript, 25 caracteres

:I{{I=.}I.+,*]I,>$0=}%.&,

El mismo enfoque que la solución de Keith Randall pero en GolfScript. Tenga en cuenta que GolfScript tiene matrices indexadas a cero. Probador en línea .

:I        # Assign input to variable I
{         # Foreach item in I
  {I=.}   # Code block: take the n-th list item
  I.+,*   # Iterate the code block 2*len(I) times
  ]       # and gather result in an array
  I,>     # Take last len(I) items
  $0=     # Get minimum
}%
.&        # Take unique items
,         # Count
Howard
fuente
Es difícil de creer que se logre tanto con tan poco código.
DavidC
Eso es impresionantemente corto.
fosgeno
5

Mathematica 69

Código

Esto encuentra el número de componentes del gráfico.

f@l_ := Length@WeaklyConnectedComponents@Graph@Thread[Range@Length@l -> l]

El primer caso de prueba:

v = {8, 28, 14, 8, 2, 1, 13, 15, 30, 17, 9, 8, 18, 19, 30, 3, 8, 25, 23, 12, 6, 7, 19, 24, 17, 7, 21, 20, 29, 15, 32, 32}
f[v]

5 5


Análisis

Haga una lista de aristas dirigidas entre índices (con el ejemplo 1).

Thread[Range@Length@v -> v

{1 -> 8, 2 -> 28, 3 -> 14, 4 -> 8, 5 -> 2, 6 -> 1, 7 -> 13, 8 -> 15, 9 -> 30, 10 -> 17 , 11 -> 9, 12 -> 8, 13 -> 18, 14 -> 19, 15 -> 30, 16 -> 3, 17 -> 8, 18 -> 25, 19 -> 23, 20 -> 12 , 21 -> 6, 22 -> 7, 23 -> 19, 24 -> 24, 25 -> 17, 26 -> 7, 27 -> 21, 28 -> 20, 29 -> 29, 30 -> 15 , 31 -> 32, 32 -> 32}


Graph dibuja un gráfico que muestra los componentes del gráfico.

ImagePaddingy VertexLabels se usan aquí para mostrar los índices.

Graph[Thread[Range[Length@v] -> v], ImagePadding -> 30, VertexLabels -> "Name"]

componentes

WeaklyConnectedComponents devuelve la lista de vértices para cada componente.

Length Devuelve el número de componentes.

c = WeaklyConnectedComponents[g]
Length[c]

{{17, 10, 25, 8, 18, 1, 4, 12, 15, 13, 6, 20, 30, 7, 21, 28, 9, 22, 26, 27, 2, 11, 5}, { 14, 3, 19, 16, 23}, {32, 31}, {24}, {29}}

5 5


Momento de la lista de muestra con 1024 elementos:

Tiempo: 0.002015 seg.

f[z] // AbsoluteTiming

{0.002015, 6}


Solo por diversión, aquí hay una foto del caso de prueba final, graficado. Omití las etiquetas de vértice; hay demasiados.

Graph[Thread[Range[Length@z] -> z], GraphLayout -> "RadialEmbedding"]

gráfico z

DavidC
fuente
1
Muy conciso, a pesar del nombre de la función de 25 caracteres. Esto puede resultar difícil de superar. Además, se parece más bien a una historia de Elige tu propia aventura.
fosgeno
WeaklyConnectedComponentsEs muy detallado. Pero hace mucho trabajo. (Uno nunca debe subestimar J y lenguajes similares por concisión.)
DavidC
En algún momento, sospecho que Wolfram se quedará sin ideas y simplemente comenzará a convertir las respuestas de Stack Exchange en funciones de biblioteca, con el fin de justificar nuevos lanzamientos.
fosgeno
Sí, entiendo a qué te refieres. Hay literalmente miles de funciones integradas en Mathematica en la actualidad. Lo extraño es que trabajan muy bien juntos.
DavidC
Ese marco inicial de reescritura de expresiones fue una maldita buena idea. Ahora, si solo implementaran múltiples niveles de deshacer ...
phosgene
2

Python, 132 116 caracteres

def T(V):
 n=len(V);r=range(n);s={}
 for i in r:
    p=[i]
    for k in r+r:p+=[V[p[-1]]]
    s[min(p[n:])]=1
 return len(s)

Para cada índice, seguimos los bordes de n saltos, lo que garantiza que estamos en un ciclo terminal. Luego seguimos n más saltos y encontramos el índice mínimo en ese ciclo. El número total de ciclos terminales es entonces el número de mínimos diferentes que encontramos.

Keith Randall
fuente
Me gusta este metodo. ¿Quizás hay una manera de combinar bucles 'i in r'?
fosgeno
Triste que no puedas #define F for i in ren Python, C (++) - estilo ... :)
tomsmeding
1

En Python:

def c(l):
    if(l==[]):
        return 0
    elif (l[-1]==len(l)):
        return c(l[:-1])+1
    else:
        return c([[x,l[-1]][x==len(l)] for x in l[:-1]])
usuario2228078
fuente
1
Este es el código de golf. Agregue el número de bytes en su envío. Es posible que desee eliminar espacios en blanco innecesarios.
Martin Ender
1
Así como paréntesis innecesarios. No son necesarios en las ifcondiciones.
user12205
c=lambda l:0 if l==[] else c(l[:-1])+1 if l[-1]==len(l) else c([[x,l[-1]][x==len(l)] for x in l[:-1]])es mejor: 102 bytes.
5ıʇǝɥʇuʎs
1

J - 61 53 char

Este fue un doozy.

#@([:#/.~[:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:)

Luego, <@<:convierte la lista en un gráfico J, que parece una lista de cuadros, y el cuadro en el índice icontiene todos los nodos a los que se iconecta el nodo . J indexa desde cero, por lo que usamos <:para disminuir todo en uno antes de boxear con <.

   (<@<:) 9 7 8 10 2 6 3 10 6 8
+-+-+-+-+-+-+-+-+-+-+
|8|6|7|9|1|5|2|9|5|7|
+-+-+-+-+-+-+-+-+-+-+

La <~.@(],;@:{~)^:_&.>]gira cada nodo en una lista de todos los nodos que se pueden alcanzar de ella. El <...&.>]es responsable de hacer que esto suceda a cada nodo, y en ~.@(],;@:{~)^:_realidad proviene de un golf J de esta tarea de 'nodos accesibles' que hice hace un par de semanas.

   (<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
+---+-------+---+---+---------+-+-----+---+-+---+
|8 5|6 2 7 9|7 9|9 7|1 6 2 7 9|5|2 7 9|9 7|5|7 9|
+---+-------+---+---+---------+-+-----+---+-+---+

e.realiza una tarea interesante Si el cierre de "accesibilidad" del gráfico (la versión del gráfico es tal que si hay bordes dirigidos X → Y e Y → Z agregamos el borde X → Z.) Tiene N nodos y bordes E, entonces e.en este gráfico una matriz booleana de N filas y columnas E, con True si el nodo correspondiente comparte un nodo accesible con este borde. Confuso, pero ten paciencia conmigo.

   ([:e.<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0
0 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0
0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 1 1
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0
0 0 0 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1

Luego, tenemos que encontrar el número de ciclos terminales, es decir, el número de grupos que comparten Trues entre sus columnas. Queremos hacer una especie de tabla de multiplicación de las filas ( "1/~), y usamos un tipo de producto interno como la multiplicación, uno que ANDs en pares y luego ORs todos los resultados juntos ( +./ .*). La matriz resultante es una tabla cuadrada con un Verdadero en cada posición en la que dos filas comparten al menos una columna entre ellas.

   ([:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
1 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
1 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 1 1 0 1
0 1 1 1 1 0 1 1 0 1
1 0 0 0 0 1 0 0 1 0
0 1 1 1 1 0 1 1 0 1

Ahora todo lo que queda por hacer es verificar cuántos tipos diferentes de patrones de fila hay. Así que hacemos exactamente eso: agrupar filas del mismo tipo ( /.~), informar el número en cada grupo ( #) y luego tomar el número de grupos ( #@).

   #@([:#/.~[:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:) 9 7 8 10 2 6 3 10 6 8
2

Uso en otros ejemplos:

   tcc =: #@([:#/.~[:+./ .*"1/~@e.<~.@(],;@:{~)^:_&.>])@:(<@<:)  NB. name
   tcc 8 28 14 8 2 1 13 15 30 17 9 8 18 19 30 3 8 25 23 12 6 7 19 24 17 7 21 20 29 15 32 32
5
   tcc 20 31 3 18 18 18 8 12 25 10 10 19 3 9 18 1 13 5 18 23 20 26 16 22 4 16 19 31 21 32 15 22
4
   tcc 6 21 15 14 22 12 5 32 29 3 22 23 6 16 20 2 16 25 9 22 13 2 19 20 26 19 32 3 32 19 28 16
1
   tcc tentwentyfour  NB. the 1024-node example
6

Desafortunadamente, el caso de 1024 elementos ahora tarda mucho tiempo en terminar. La versión anterior <:@#@((#~0={.+/@:*"1])^:a:)@e.@(~.@(],;@:{~)^:_&.>~<)@:(<@<:)(61 caracteres) tardó un poco más de un segundo en hacer esto.

Algoritmo de tiburón
fuente
Ah sí, la buena e. función, debería haberlo sabido. o_O Me estoy poniendo alucinado mirando tu código. Bien hecho.
fosgeno
0

Pitón (96)

Muy, muy, muy, muy basado en la respuesta del usuario 2228078, ya que funciona exactamente igual, pero tiene más golf:

c=lambda l:(1+c(l[:-1])if l[-1]==len(l)else c([[x,l[-1]][x==len(l)]for x in l[:-1]]))if l else 0

(Pregunta técnica: ¿debería una respuesta de golf de alguien más ser un wiki comunitario?)

ɐɔıʇǝɥʇuʎs
fuente
Tienes mi bendición para plagiar. Que sea un esfuerzo comunitario.
fosgeno
@ user2790167 Claro, ya está;)
5ıʇǝɥʇuʎs
0

Pitón (89) (87)

def c(G):
 u=i=0
 for _ in G:
  j=i;i+=1
  while j>=0:G[j],j=~i,G[j]
  u+=j==~i
 return u

La idea principal es comenzar en cada nodo a la vez y caminar a lo largo del camino desde él, marcando cada nodo que visitamos con una etiqueta única para el nodo en el que comenzamos. Si alguna vez golpeamos un nodo marcado, dejamos de caminar y verificamos si la marca es la que corresponde a nuestro nodo inicial. Si es así, debemos haber recorrido un ciclo, por lo que incrementamos el conteo de ciclos en uno.

En el código, ues el contador de ciclos, ies el nodo inicial, jes el nodo actual que estamos caminando. La etiqueta correspondiente al nodo inicial ies su complemento de bits ~ique siempre es negativo. Marcamos un nodo señalándolo a ese valor, sobrescribiendo el nodo al que realmente señaló (teniendo cuidado de caminar hacia ese nodo antes de que se olvide).

Sabemos que hemos alcanzado un nodo marcado cuando caminamos hacia un "nodo" de valor negativo inmediatamente después. Verificamos si ese valor negativo es la etiqueta actual para ver si hemos recorrido un ciclo. Como cada nodo que caminamos se elimina efectivamente, cada ciclo solo se caminará una vez.

Guarda los caracteres para contarlos imanualmente con una variable de bucle ficticio y _luego como for i in range(len(G)). Las listas de Python están indexadas en 0. Si en su lugar estuvieran indexados en 1, podríamos guardar dos caracteres escribiendo j=i=i+1para tener iy jser 1 para el primer bucle, y escribir j>0en lugar de j>=0.

Editar:

Podemos guardar dos caracteres iterando isobre los elementos de la lista en lugar de los índices, ya que los nodos a los que no apunta ningún borde no importan. Sin set(G)embargo, tenemos que iterar para evitar repetir un nodo de inicio al que apuntaron varios otros nodos.

def c(G):
 u=0
 for i in set(G):
  j=i
  while j>=0:G[j],j=~i,G[j]
  u+=j==~i
 return u
xnor
fuente
El código Python más corto publicado hasta la fecha, y un interesante método de 'marcado de tiza'. Me gusta.
fosgeno