Estoy buscando ayuda para comprender el algoritmo de detección de ciclos de Floyd. He revisado la explicación en wikipedia ( http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare )

Puedo ver cómo el algoritmo detecta el ciclo en el tiempo O (n). Sin embargo, estoy incapaz de visualizar el hecho de que una vez que los punteros de la tortuga y la liebre se encuentran por primera vez, el inicio del ciclo se puede determinar moviendo el puntero de la tortuga de regreso al inicio y luego moviendo tanto a la tortuga como a la liebre un paso a la vez. El primer encuentro es el comienzo del ciclo.

¿Alguien puede ayudar proporcionando una explicación, con suerte diferente a la de wikipedia, ya que no puedo entenderla / visualizarla?

Comentarios

  • Encontré la respuesta en stackoverflow. Gracias si alguien estaba investigando esto por mí. Y para aquellos que como yo querían una explicación, consulte: stackov erflow.com/questions/3952805/… La respuesta elegida a la pregunta, ¡lo explica!
  • Hola @Anurag. Solo para su información, ‘ he hecho una publicación de blog sobre la » Tortuga y Liebre » algoritmo aquí
  • ¿Sabe por qué la fast variable o la » hare » necesita moverse al doble de velocidad que una tortuga, en lugar de solo una por delante?
  • Bien explicado con programa: javabypatel.blogspot.in/2015/12/detect-loop-in-linked-list.html

Respuesta

Puede consultar «Detectar el inicio de un bucle en una lista enlazada individualmente» , aquí hay un extracto:

ingrese la descripción de la imagen aquí

Distancia recorrida por slowPointer antes de reunirse con $ = x + y $

Distancia recorrida por fastPointer antes de reunirse con $ = (x + y + z) + y = x + 2y + z $

Dado que fastPointer viaja con duplica la velocidad de slowPointer, y el tiempo es constante para ambos cuando lleguen al punto de encuentro. Entonces, usando una relación simple de velocidad, tiempo y distancia (slowPointer viajó la mitad de la distancia):

\ begin {align *} 2 * \ operatorname {dist} (\ text {slowPointer}) & = \ operatorname {dist} (\ text {fastPointer}) \\ 2 (x + y) & = x + 2y + z \\ 2x + 2y & = x + 2y + z \\ x & = z \ end {align *}

Por lo tanto, al mover slowPointer al inicio de la lista vinculada, y hacer que ambos slowPointer y fastPointer para mover un nodo a la vez, ambos deben cubrir la misma distancia .

Llegarán al punto donde comienza el ciclo en la lista enlazada.

Comentarios

  • Aquí asumió que se encontrarán después de una rotación. Puede haber casos (donde el ciclo es pequeño) en los que pueden encontrarse después de cierto no. de rotaciones.
  • @JotWaraich la imagen no es representativa de todos los casos; sin embargo, la lógica sigue siendo válida
  • esta es la respuesta más sencilla sobre este algoritmo en todo Internet
  • Yo ‘ no estoy muy seguro si esta prueba es correcta. La distancia recorrida por el puntero lento debe ser (x + (y + z) * α + y) y la distancia recorrida por el puntero rápido debe ser (x + (y + z) * β + y) donde α y β son el número de ciclos atravesados por el puntero lento y rápido. La prueba parece fallar con esta adición,
  • @ hashcode55 sí, estoy de acuerdo con usted, esto no me queda claro
  • Responder

    También he visto la respuesta aceptada como prueba en otros lugares. Sin embargo, aunque es fácil de asimilar, es incorrecto. Lo que demuestra es

    $ x = z $ (lo cual obviamente es incorrecto, y el diagrama simplemente lo hace parecer plausible debido a la forma en que está esbozado).

    Lo que realmente quieres probar es (usando las mismas variables que se describen en el diagrama en la respuesta aceptada anterior):

    $ z = x \ mod \ (y + z) $

    $ (y + z) $ es la longitud del bucle, $ L $

    entonces, lo que queremos demostrar es:

    $ z = x \ mod \ L $

    O que z es congruente ax (módulo L)

    La siguiente demostración tiene más sentido para mí:

    Punto de encuentro, $ M = x + y $

    $ 2 (x + y) = M + kL $, donde $ k $ es una constante.Básicamente, la distancia recorrida por el puntero rápido es $ x + y $ más algún múltiplo de la longitud del bucle, $ L $

    $ x + y = kL $

    $ x = kL – y $

    La ecuación anterior demuestra que $ x $ es lo mismo que algún múltiplo de la longitud del ciclo, $ L $ menos $ y $. Entonces, si el puntero rápido comienza en el punto de encuentro, $ M $ o en $ x + y $, terminará al comienzo del ciclo.

    Comentarios

  • No ‘ no sé por qué se subestima esta respuesta. Esta parece ser la prueba más formal.
  • @ I8De nuevo, si x = z es incorrecto, entonces la lógica al mover el puntero rápido de regreso al punto de inicio & mover ambos El puntero rápido & lento para encontrar el inicio del ciclo no funcionaría ‘. No entiendo lo que quieres decir con x = z es incorrecto, por favor explica cómo es eso?
  • @divine x = z es incorrecto porque implica que ambos son iguales, pero es aceptable tener un » cola ”(x grande) y un círculo muy pequeño (z pequeña). Al usar el módulo estamos formalmente correctos, solo queremos demostrar que x% L == z, en otras palabras, el puntero que viaja en el círculo esperando unirse al otro puede hacer tantos bucles como quiera, siempre que los últimos pasos (es decir, el resto después del bucle) conducirán al inicio del círculo mismo.
  • ¡¡Esto es genial !!
  • Para aquellos de ustedes que aún no ‘ No entiendo por qué x = k * L – y conduce a z = x mod L. De x = k * L – y — > x = (k – 1) * L + L – y y entonces ahora puedes decir que L – y tiene que ser z. Ahora, obtiene x = (k – 1) * L + z
  • Respuesta

    Digamos que hay $ l $ elementos antes de que comience el ciclo y $ n $ elementos en el ciclo. Y $ e_l $ es el primer elemento del bucle que se ve cuando recorremos la lista vinculada. Cuando diremos » un elemento $ x $ se adelanta a $ e $ «, eso significa que podemos llegar a ese elemento tomando $ x $ pasos de $ e $ .

    Ahora, cuando Tr (tortuga) alcanza $ e_l $ , después de $ l $ iteraciones, digamos, Hr (Hare) está $ x $ pasos por delante de $ e_l $ . Dado que Hr ha tomado un total de $ 2l $ pasos para entonces ( $ l $ pasos antes del ciclo), entonces:

    $ x = l \ bmod n $ .

    En cada iteración futura, Tr y Hr progresarán por 1 y 2 pasos respectivamente, por lo que cada iteración aumentará su » gap » en 1. Por lo tanto, se encontrarán después de $ nx $ iteraciones posteriores, cuando su espacio se convierta en $ x + (nx) = n $ . Por lo tanto, el elemento de reunión $ M $ será $ nx $ pasos por delante de $ e_l $ . Eso significa que, si sigues $ x $ pasos después de $ M $ , volveremos a llevarnos a $ e_l $ . Nuestro objetivo es ubicar $ e_l $ .

    Entonces, cuando comenzamos con una referencia Tr en $ M $ y otra Hr de referencia al principio de la lista vinculada, y progresar ambos paso a paso, después de $ l $ iteraciones :

    • Hr será $ l $ pasos por delante de la cabeza, que es $ e_l $ .

    • Tr será $ (l \ bmod n) $ pasos por delante de $ M $ , es decir, $ x $ pasos por delante de $ M $ , que es $ e_l $ .

    Por lo tanto, cuando tienen conocido, sabemos que es $ e_l $ .

    Consulte este artículo para detalles. Allí encontrará un método ligeramente modificado para localizar $ e_l $ .

    Responder

    Encontré la respuesta en stackoverflow. Gracias si alguien estaba investigando esto por mí. Y para aquellos que como yo querían una explicación, consulte: https://stackoverflow.com/questions/3952805/proof-of-detecting-the-start-of-cycle-in-linked-list La respuesta elegida a la pregunta, explica ¡eso!

    Respuesta

    También creo que la respuesta principal está incompleta, y aunque algunas de las otras explicaciones también son buenas, tengo otra versión sin MOD para mostrar lo mismo, lo que quizás sea más fácil para algunas personas.

    La misma configuración, considere $ X \ ge 0 $ siendo la distancia antes del bucle, $ N \ le \ text {size of list} $ es el tamaño del bucle (la cantidad $ y + z $ en la imagen), y el punto de encuentro será $ Y $ en el bucle. En este punto, observe que también hemos creado $ X + Y \ le N $ .

    Ahora, el puntero lento viaja $ X + Y + pN $ , donde $ p $ es un entero arbitrario. Luego, el puntero rápido viajó $ 2 (X + Y + pN) $ .

    Ahora, afirman que se encuentran en $ Y $ .

    Prueba :

    Si se encuentran en $ Y $ , debe ser que el puntero más rápido viajó exactamente $ qN $ más que el puntero más lento, donde $ q $ es un número entero . Entonces tenemos: $$ \ text {diferencia de distancia =} 2 (X + Y + pN) – (X + Y + pN) = X + Y + pN $$ . Pero teníamos $ Y $ como un punto de encuentro arbitrario en el ciclo, por lo que podemos elegir simplemente $ X + Y = N $ , que se mantiene como $ X + Y \ le N $ .

    Por lo tanto, tenemos: $$ X + Y + pN = N + pN = (p + 1) N = qN $$ que es cierto si $ p + 1 = q $ . Ahora, dado que tanto $ p $ y $ q $ son números enteros arbitrarios, podemos elegir el mínimo para ese $ p = 0, q = 1 $ , que corresponde a: $$ \ text {distancia recorrida por el puntero lento} = X + Y + pN = X + Y $$ y $$ \ text {distancia recorrida por el puntero rápido} = (X + Y + pN) + qN = X + Y + N $$ por lo que el puntero rápido, al encontrarse por primera vez con el puntero lento, viajó exactamente $ N $ más.

    Respuesta

    La tortuga y la liebre son dos punteros que se inicializan con el valor del «top» de la lista. En cada ciclo del ciclo, la liebre aumenta en 2 elementos incrementales, mientras que la tortuga aumenta en uno. Si en algún momento la liebre es igual a la liebre, se ha detectado un bucle. Si la liebre es igual al final, no se encuentra un bucle.

    Esta publicación lo explica mejor línea por línea:

    https://www.thiscodeworks.com/floyd-s-cycle-detection-algorithm-aka-the-tortoise-and-the-hare-algorithms-logic-interesting/5e10a9b796432f6f7b798b29

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *