1+ """
2+ An implementation of Floyd's Cycle-Finding Algorithm.
3+ Also known as the "tortoise and the hare" algorithm.
4+
5+ This algorithm is used to detect a cycle in a sequence of iterated function values.
6+ It can also find the starting index and length of the cycle.
7+
8+ Wikipedia: https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare
9+ """
10+ from typing import Any , Callable , Optional , Tuple
11+
12+
13+ def floyds_cycle_finding (
14+ f : Callable [[Any ], Any ], x0 : Any
15+ ) -> Optional [Tuple [int , int ]]:
16+ """
17+ Finds a cycle in the sequence of values generated by the function f.
18+
19+ Args:
20+ f: A function that takes a value and returns the next value in the sequence.
21+ x0: The starting value of the sequence.
22+
23+ Returns:
24+ A tuple containing the index of the first element of the cycle (mu)
25+ and the length of the cycle (lam), or None if no cycle is found.
26+
27+ Doctest examples:
28+ >>> # Example with a cycle
29+ >>> f = lambda x: (2 * x + 3) % 17
30+ >>> floyds_cycle_finding(f, 0)
31+ (0, 8)
32+
33+ >>> # Example with a different starting point and cycle
34+ >>> f = lambda x: [1, 2, 3, 4, 5, 3][x]
35+ >>> floyds_cycle_finding(f, 0)
36+ (2, 3)
37+
38+ >>> # Example with no cycle (sequence terminates)
39+ >>> nodes = {0: 1, 1: 2, 2: 3, 3: None}
40+ >>> f = lambda x: nodes.get(x)
41+ >>> floyds_cycle_finding(f, 0)
42+
43+ """
44+ # Main phase of the algorithm: finding a repetition x_i = x_2i.
45+ # The hare moves twice as fast as the tortoise.
46+ tortoise = f (x0 )
47+ hare = f (f (x0 ))
48+ while tortoise != hare :
49+ # If the hare reaches the end of the sequence, there is no cycle.
50+ if hare is None or f (hare ) is None :
51+ return None
52+ tortoise = f (tortoise )
53+ hare = f (f (hare ))
54+
55+ # At this point, the tortoise and hare have met.
56+ # Now, find the position of the first repetition.
57+ # The distance from the start to the cycle's beginning is mu.
58+ mu = 0
59+ tortoise = x0
60+ while tortoise != hare :
61+ tortoise = f (tortoise )
62+ hare = f (hare )
63+ mu += 1
64+
65+ # Finally, find the length of the cycle.
66+ # The hare moves one step at a time while the tortoise stays put.
67+ # The number of steps until they meet again is the cycle length (lam).
68+ lam = 1
69+ hare = f (tortoise )
70+ while tortoise != hare :
71+ hare = f (hare )
72+ lam += 1
73+
74+ return mu , lam
75+
76+
77+ if __name__ == "__main__" :
78+ import doctest
79+
80+ doctest .testmod ()
0 commit comments