1 2 3 4 5 6 7 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
//! 周期検出。
/// 周期検出を行う。
///
/// 与えられた $x\_0$ と $f$ を用いて $x\_i = f(x\_{i-1})$ ($i > 0$) として
/// 定義される列 $\\{x\_i\\}\_{i=0}\^\\infty$ の周期検出を行う。
/// $x\_{\\mu} = x\_{\\mu+\\lambda}$ なる最小の $(\\mu, \\lambda)$ を返す。
///
/// # Requirements
/// $f$ は参照透過である。
///
/// # Notes
/// 共通の $f$ に対して、異なる複数の $x\_0$ から周期検出を行いたい場合は、
/// この関数を複数回呼ぶよりも高速なアルゴリズムがあると思われる。
/// ある $x\_0$ での出力が $(\\mu, \\lambda)$ であれば、
/// $x\_i$ ($1\\le i\< \\mu$) での出力は $(\\mu-i, \\lambda)$、
/// $x\_i$ ($\\mu\\le i<\\mu+\\lambda$) での出力は $(0, \\lambda)$ とわかる。
/// さらに、これら $\\mu+\\lambda$ 個以外の $x\'$ についても、
/// $f\^i(x\')$ がこれらのいずれかと等しくなる $i$ が存在すれば、
/// $\\Theta(\\mu)$ 回の $f$ の呼び出しで $x\', f(x\'), \\dots, f\^{i-1}(x\')$ の出力もわかるはず。
///
/// # Complexity
/// $\\Theta(\\mu+\\lambda)$ 回の $f$ の呼び出しを行う。
///
/// # Examples
/// ```
/// use nekolib::algo::cycle_mu_lambda;
///
/// // 3, 9, 11, 9, 11, ...
/// assert_eq!(cycle_mu_lambda(3, |x| x * x % 14), (1, 2));
/// // 2, 6, 4, 5, 1, 3, 2, ...
/// assert_eq!(cycle_mu_lambda(2, |x| x * 3 % 7), (0, 6));
/// ```
///
/// ```
/// use nekolib::algo::cycle_mu_lambda;
///
/// assert_eq!(cycle_mu_lambda(0, |x| x), (0, 1));
/// ```
pub fn cycle_mu_lambda<T, F>(x0: T, f: F) -> (usize, usize)
where
T: Eq + Clone,
F: Fn(T) -> T,
{
let mut tor = f(x0.clone());
let mut har = f(tor.clone());
while tor != har {
tor = f(tor);
har = f(f(har));
}
let mut tor = x0;
let mut mu = 0;
while tor != har {
tor = f(tor);
har = f(har);
mu += 1;
}
let mut lambda = 1;
har = f(tor.clone());
while tor != har {
har = f(har);
lambda += 1;
}
(mu, lambda)
}
/// $n$ 項目を求める。
///
/// 与えられた $x\_0$ と $f$ を用いて $x\_i = f(x\_{i-1})$ ($i > 0$) として
/// 定義される列 $\\{x\_i\\}\_{i=0}\^\\infty$ の $n$ 項目を求める。
///
/// # Requirements
/// $f$ は参照透過である。
///
/// # Complexuty
/// $x\_{\\mu} = x\_{\\mu+\\lambda}$ なる最小の $(\\mu, \\lambda)$
/// に対し、$O(\\min\\{n, \\mu+\\lambda\\})$ time.
///
/// # Examples
/// ```
/// use nekolib::algo::cycle_nth;
///
/// let x0 = 0;
/// let f = |x| (x + 1) % 100; // (mu, lambda) = (0, 100)
/// assert_eq!(cycle_nth(x0, f, 0), 0);
/// assert_eq!(cycle_nth(x0, f, 99), 99);
/// assert_eq!(cycle_nth(x0, f, 100), 0);
/// assert_eq!(cycle_nth(x0, f, 1000), 0);
/// assert_eq!(cycle_nth(x0, f, 10_usize.pow(9)), 0);
///
/// let x0 = -10;
/// let f = |x| (x + 1) % 100; // (mu, lambda) = (10, 100)
/// assert_eq!(cycle_nth(x0, f, 0), -10);
/// assert_eq!(cycle_nth(x0, f, 99), 89);
/// assert_eq!(cycle_nth(x0, f, 100), 90);
/// assert_eq!(cycle_nth(x0, f, 1000), 90);
/// assert_eq!(cycle_nth(x0, f, 10_usize.pow(9)), 90);
/// ```
pub fn cycle_nth<T, F>(x0: T, f: F, n: usize) -> T
where
T: Eq + Clone,
F: Fn(T) -> T,
{
if n == 0 {
return x0;
}
if n == 1 {
return f(x0);
}
let mut tor = f(x0.clone());
let mut har = f(tor.clone());
let mut i = 2;
while i + 2 <= n && tor != har {
tor = f(tor);
har = f(f(har));
i += 2;
}
if i == n {
return har;
}
if i + 1 == n {
return f(har);
}
let mut tor = x0.clone();
let mut mu = 0;
while tor != har {
tor = f(tor);
har = f(har);
mu += 1;
}
let mut lambda = 1;
har = f(tor.clone());
while tor != har {
har = f(har);
lambda += 1;
}
let n = if n <= mu { n } else { mu + (n - mu) % lambda };
let mut x = x0;
for _ in 0..n {
x = f(x);
}
x
}