|
1 | 1 | //! Identify a cycle in an infinite sequence. |
2 | 2 |
|
| 3 | +/// Identify a cycle in an infinite sequence using Floyd's algorithm (partial version). |
| 4 | +/// Return the cycle size, an element in the cycle, and an upper bound on the index of |
| 5 | +/// the first element. |
| 6 | +/// |
| 7 | +/// This function computes the cycle length λ and returns an element within the cycle, |
| 8 | +/// along with an upper bound μ̃ on the index of the first cycle element. The upper bound |
| 9 | +/// μ̃ satisfies μ ≤ μ̃ < μ + λ, where μ is the minimal index. |
| 10 | +/// |
| 11 | +/// This is faster than [`floyd`] as it skips the computation of the minimal μ. |
| 12 | +/// |
| 13 | +/// # Warning |
| 14 | +/// |
| 15 | +/// If no cycle exist, this function loops forever. |
| 16 | +#[allow(clippy::needless_pass_by_value)] |
| 17 | +pub fn floyd_partial<T, FS>(start: T, successor: FS) -> (usize, T, usize) |
| 18 | +where |
| 19 | + T: Clone + PartialEq, |
| 20 | + FS: Fn(T) -> T, |
| 21 | +{ |
| 22 | + let mut tortoise = successor(start.clone()); |
| 23 | + let mut hare = successor(successor(start.clone())); |
| 24 | + while tortoise != hare { |
| 25 | + (tortoise, hare) = (successor(tortoise), successor(successor(hare))); |
| 26 | + } |
| 27 | + let mut lam = 1; |
| 28 | + hare = successor(tortoise.clone()); |
| 29 | + while tortoise != hare { |
| 30 | + (hare, lam) = (successor(hare), lam + 1); |
| 31 | + } |
| 32 | + // tortoise is now at the start of the cycle |
| 33 | + // mu_tilde is an upper bound: we know the cycle starts at or before tortoise's position |
| 34 | + // We can compute an upper bound by noting that tortoise must be within the first cycle |
| 35 | + let mu_tilde = lam; |
| 36 | + (lam, tortoise, mu_tilde) |
| 37 | +} |
| 38 | + |
3 | 39 | /// Identify a cycle in an infinite sequence using Floyd's algorithm. |
4 | 40 | /// Return the cycle size, the first element, and the index of first element. |
5 | 41 | /// |
|
29 | 65 | (lam, tortoise, mu) |
30 | 66 | } |
31 | 67 |
|
| 68 | +/// Identify a cycle in an infinite sequence using Brent's algorithm (partial version). |
| 69 | +/// Return the cycle size, an element in the cycle, and an upper bound on the index of |
| 70 | +/// the first element. |
| 71 | +/// |
| 72 | +/// This function computes the cycle length λ and returns an element within the cycle, |
| 73 | +/// along with an upper bound μ̃ on the index of the first cycle element. The upper bound |
| 74 | +/// μ̃ satisfies μ ≤ μ̃ < μ + λ, where μ is the minimal index. |
| 75 | +/// |
| 76 | +/// This is faster than [`brent`] as it skips the computation of the minimal μ. |
| 77 | +/// |
| 78 | +/// # Warning |
| 79 | +/// |
| 80 | +/// If no cycle exist, this function loops forever. |
| 81 | +#[allow(clippy::needless_pass_by_value)] |
| 82 | +pub fn brent_partial<T, FS>(start: T, successor: FS) -> (usize, T, usize) |
| 83 | +where |
| 84 | + T: Clone + PartialEq, |
| 85 | + FS: Fn(T) -> T, |
| 86 | +{ |
| 87 | + let mut power = 1; |
| 88 | + let mut lam = 1; |
| 89 | + let mut tortoise = start.clone(); |
| 90 | + let mut hare = successor(start.clone()); |
| 91 | + while tortoise != hare { |
| 92 | + if power == lam { |
| 93 | + (tortoise, power, lam) = (hare.clone(), power * 2, 0); |
| 94 | + } |
| 95 | + (hare, lam) = (successor(hare), lam + 1); |
| 96 | + } |
| 97 | + // hare is now at a position in the cycle, and we know the cycle length is lam |
| 98 | + // We return lam as an upper bound for mu since we know mu < mu + lam |
| 99 | + (lam, hare, lam) |
| 100 | +} |
| 101 | + |
32 | 102 | /// Identify a cycle in an infinite sequence using Brent's algorithm. |
33 | 103 | /// Return the cycle size, the first element, and the index of first element. |
34 | 104 | /// |
|
0 commit comments