11//! Identify a cycle in an infinite sequence.
22
3- /// Identify a cycle in an infinite sequence using Floyd's algorithm.
4- /// Return the cycle size, the first element, and the index of first element.
5- ///
6- /// # Warning
7- ///
8- /// If no cycle exist, this function loops forever.
9- pub fn floyd < T , FS > ( start : T , successor : FS ) -> ( usize , T , usize )
3+ /// Find the meeting point and lambda using Floyd's algorithm.
4+ /// Returns (`lambda`, `meeting_element`, `tortoise_steps`).
5+ fn floyd_find_cycle < T , FS > ( start : & T , successor : & FS ) -> ( usize , T , usize )
106where
117 T : Clone + PartialEq ,
128 FS : Fn ( T ) -> T ,
139{
1410 let mut tortoise = successor ( start. clone ( ) ) ;
1511 let mut hare = successor ( successor ( start. clone ( ) ) ) ;
12+ let mut tortoise_steps = 1 ;
1613 while tortoise != hare {
1714 ( tortoise, hare) = ( successor ( tortoise) , successor ( successor ( hare) ) ) ;
15+ tortoise_steps += 1 ;
1816 }
19- let mut mu = 0 ;
20- tortoise = start;
21- while tortoise != hare {
22- ( tortoise, hare, mu) = ( successor ( tortoise) , successor ( hare) , mu + 1 ) ;
23- }
17+ // tortoise and hare met at position tortoise_steps
2418 let mut lam = 1 ;
2519 hare = successor ( tortoise. clone ( ) ) ;
2620 while tortoise != hare {
2721 ( hare, lam) = ( successor ( hare) , lam + 1 ) ;
2822 }
29- ( lam, tortoise, mu )
23+ ( lam, tortoise, tortoise_steps )
3024}
3125
32- /// Identify a cycle in an infinite sequence using Brent's algorithm.
33- /// Return the cycle size, the first element, and the index of first element.
34- ///
35- /// # Warning
36- ///
37- /// If no cycle exist, this function loops forever.
38- pub fn brent < T , FS > ( start : T , successor : FS ) -> ( usize , T , usize )
26+ /// Find the cycle using Brent's algorithm.
27+ /// Returns (`lambda`, `meeting_element`, `hare_steps`).
28+ fn brent_find_cycle < T , FS > ( start : & T , successor : & FS ) -> ( usize , T , usize )
3929where
4030 T : Clone + PartialEq ,
4131 FS : Fn ( T ) -> T ,
@@ -44,14 +34,131 @@ where
4434 let mut lam = 1 ;
4535 let mut tortoise = start. clone ( ) ;
4636 let mut hare = successor ( start. clone ( ) ) ;
37+ let mut hare_steps = 1 ;
4738 while tortoise != hare {
4839 if power == lam {
4940 ( tortoise, power, lam) = ( hare. clone ( ) , power * 2 , 0 ) ;
5041 }
5142 ( hare, lam) = ( successor ( hare) , lam + 1 ) ;
43+ hare_steps += 1 ;
44+ }
45+ ( lam, hare, hare_steps)
46+ }
47+
48+ /// Identify a cycle in an infinite sequence using Floyd's algorithm (partial version).
49+ /// Return the cycle size, an element in the cycle, and an upper bound on the index of
50+ /// the first element.
51+ ///
52+ /// This function computes the cycle length λ and returns an element within the cycle,
53+ /// along with an upper bound μ̃ on the index of the first cycle element. The upper bound
54+ /// μ̃ satisfies μ ≤ μ̃ < μ + λ, where μ is the minimal index.
55+ ///
56+ /// This is faster than [`floyd`] as it skips the computation of the minimal μ.
57+ /// The upper bound μ̃ is sufficient for many applications, such as computing f^n(x) for
58+ /// large n, where knowing the exact starting point of the cycle is not necessary.
59+ ///
60+ /// # Example
61+ ///
62+ /// ```
63+ /// use pathfinding::prelude::floyd_partial;
64+ ///
65+ /// let (lam, _elem, mu_tilde) = floyd_partial(1, |x| (x * 2) % 7);
66+ /// assert_eq!(lam, 3); // Cycle length
67+ /// assert!(mu_tilde == 0); // Upper bound on mu (start is in cycle, so mu = 0)
68+ /// ```
69+ ///
70+ /// # Warning
71+ ///
72+ /// If no cycle exist, this function loops forever.
73+ #[ expect( clippy:: needless_pass_by_value) ]
74+ pub fn floyd_partial < T , FS > ( start : T , successor : FS ) -> ( usize , T , usize )
75+ where
76+ T : Clone + PartialEq ,
77+ FS : Fn ( T ) -> T ,
78+ {
79+ let ( lam, tortoise, tortoise_steps) = floyd_find_cycle ( & start, & successor) ;
80+ // Handle edge case where they meet at the start position (pure cycle, mu = 0)
81+ // In this case, tortoise_steps equals lam, and to satisfy mu_tilde < mu + lam,
82+ // we must return 0.
83+ let mu_tilde = if tortoise == start { 0 } else { tortoise_steps } ;
84+ ( lam, tortoise, mu_tilde)
85+ }
86+
87+ /// Identify a cycle in an infinite sequence using Floyd's algorithm.
88+ /// Return the cycle size, the first element, and the index of first element.
89+ ///
90+ /// # Warning
91+ ///
92+ /// If no cycle exist, this function loops forever.
93+ pub fn floyd < T , FS > ( start : T , successor : FS ) -> ( usize , T , usize )
94+ where
95+ T : Clone + PartialEq ,
96+ FS : Fn ( T ) -> T ,
97+ {
98+ let ( lam, mut hare, _tortoise_steps) = floyd_find_cycle ( & start, & successor) ;
99+ // Find the exact mu
100+ let ( mut mu, mut tortoise) = ( 0 , start) ;
101+ while tortoise != hare {
102+ ( tortoise, hare, mu) = ( successor ( tortoise) , successor ( hare) , mu + 1 ) ;
52103 }
104+ ( lam, tortoise, mu)
105+ }
106+
107+ /// Identify a cycle in an infinite sequence using Brent's algorithm (partial version).
108+ /// Return the cycle size, an element in the cycle, and an upper bound on the index of
109+ /// the first element.
110+ ///
111+ /// This function computes the cycle length λ and returns an element within the cycle,
112+ /// along with an upper bound μ̃ on the index of the first cycle element. The upper bound
113+ /// satisfies μ ≤ μ̃. Due to the nature of Brent's algorithm with its power-of-2 stepping,
114+ /// the bound may be looser than `μ + λ` in some cases, but is still reasonable for
115+ /// practical applications.
116+ ///
117+ /// This is faster than [`brent`] as it skips the computation of the minimal μ.
118+ /// The upper bound μ̃ is sufficient for many applications, such as computing f^n(x) for
119+ /// large n, where knowing the exact starting point of the cycle is not necessary.
120+ ///
121+ /// # Example
122+ ///
123+ /// ```
124+ /// use pathfinding::prelude::brent_partial;
125+ ///
126+ /// let (lam, _elem, mu_tilde) = brent_partial(1, |x| (x * 2) % 7);
127+ /// assert_eq!(lam, 3); // Cycle length
128+ /// assert!(mu_tilde >= 1); // Upper bound on mu
129+ /// ```
130+ ///
131+ /// # Warning
132+ ///
133+ /// If no cycle exist, this function loops forever.
134+ #[ expect( clippy:: needless_pass_by_value) ]
135+ pub fn brent_partial < T , FS > ( start : T , successor : FS ) -> ( usize , T , usize )
136+ where
137+ T : Clone + PartialEq ,
138+ FS : Fn ( T ) -> T ,
139+ {
140+ let ( lam, hare, hare_steps) = brent_find_cycle ( & start, & successor) ;
141+ // Use hare_steps as the upper bound, as it represents where we detected the cycle.
142+ let mu_tilde = hare_steps;
143+ ( lam, hare, mu_tilde)
144+ }
145+
146+ /// Identify a cycle in an infinite sequence using Brent's algorithm.
147+ /// Return the cycle size, the first element, and the index of first element.
148+ ///
149+ /// # Warning
150+ ///
151+ /// If no cycle exist, this function loops forever.
152+ pub fn brent < T , FS > ( start : T , successor : FS ) -> ( usize , T , usize )
153+ where
154+ T : Clone + PartialEq ,
155+ FS : Fn ( T ) -> T ,
156+ {
157+ let ( lam, _hare, _hare_steps) = brent_find_cycle ( & start, & successor) ;
158+ // Find the exact mu
53159 let mut mu = 0 ;
54- ( tortoise, hare) = ( start. clone ( ) , ( 0 ..lam) . fold ( start, |x, _| successor ( x) ) ) ;
160+ let mut tortoise = start. clone ( ) ;
161+ let mut hare = ( 0 ..lam) . fold ( start, |x, _| successor ( x) ) ;
55162 while tortoise != hare {
56163 ( tortoise, hare, mu) = ( successor ( tortoise) , successor ( hare) , mu + 1 ) ;
57164 }
0 commit comments