The Berlekamp–Massey algorithm finds the shortest recurrence of a sequence. Given a sequence of length whose shortest recurrence has order , Berlekamp–Massey finds the shortest recurrence for every prefix of the sequence in time. In the worst case, , so the worst-case complexity is .
Definition
For a sequence , define a recurrence as a sequence satisfying
where . The value is called the order of the recurrence.
The shortest recurrence of the sequence is the recurrence with the smallest order.
Method
For convenience, we use a slightly different set of recurrence coefficients, , satisfying
It is easy to see that , and that the order is the same under the two definitions.
We can construct the recurrence incrementally. Consider the terms of in order, and adjust the coefficients whenever the current recurrence gives an incorrect result. For convenience, let the shortest recurrence for the first terms be .
Initially, . Suppose that is valid for the first terms of . For the -th term, there are two cases:
- The recurrence also gives the correct value for . No adjustment is needed; set .
- The recurrence does not give the correct value for . Adjust to obtain a new .
Let be the difference between and the value predicted by .
If this is the first adjustment of the recurrence coefficients, then is the first nonzero term of the sequence. In this case, set to zeroes, that is, to coefficients. This is clearly a valid shortest recurrence.
Otherwise, let be the number of terms of considered when the recurrence coefficients were last adjusted. Suppose there is a sequence satisfying
and . Then adding and term by term produces a valid recurrence coefficient sequence .
To construct , one possible choice is
There are leading coefficients. The final term, , means that every term of is multiplied by and appended to the sequence.
Indeed, , so this construction gives a valid . Set to the termwise sum of and .
If the recurrence is required in the original form , negate every and insert at the beginning.
The algorithm above has complexity when the shortest recurrence has order . Since in the worst case, the worst-case complexity is .
When implementing the algorithm, each adjustment only needs the recurrence coefficients from the previous adjustment. Therefore, if only the shortest recurrence of the whole sequence is required, it is enough to store the current coefficients and the coefficients from the previous adjustment, using space.
Reference implementation
vector<int> berlekamp_massey(const vector<int> &a) {
vector<int> v, last; // v is the answer, 0-based, p is the module
int k = -1, delta = 0;
for (int i = 0; i < (int)a.size(); i++) {
int tmp = 0;
for (int j = 0; j < (int)v.size(); j++)
tmp = (tmp + (long long)a[i - j - 1] * v[j]) % p;
if (a[i] == tmp) continue;
if (k < 0) {
k = i;
delta = (a[i] - tmp + p) % p;
v = vector<int>(i + 1);
continue;
}
vector<int> u = v;
int val = (long long)(a[i] - tmp + p) * power(delta, p - 2) % p;
if (v.size() < last.size() + i - k) v.resize(last.size() + i - k);
(v[i - k - 1] += val) %= p;
for (int j = 0; j < (int)last.size(); j++) {
v[i - k + j] = (v[i - k + j] - (long long)val * last[j]) % p;
if (v[i - k + j] < 0) v[i - k + j] += p;
}
if ((int)u.size() - i < (int)last.size() - k) {
last = u;
k = i;
delta = a[i] - tmp;
if (delta < 0) delta += p;
}
}
for (auto &x : v) x = (p - x) % p;
v.insert(v.begin(), 1);
return v; // $\forall i, \sum_{j = 0} ^ m a_{i - j} v_j = 0$
}
The naive Berlekamp–Massey algorithm finds the shortest recurrence of a finite sequence. If the sequence is infinite and an upper bound on the order of its shortest recurrence is known, taking the first terms is enough to determine the shortest recurrence of the entire sequence. (Proof omitted.)
Applications
Because the Berlekamp–Massey algorithm has relatively poor numerical stability, it is rarely used for problems over the real numbers. For convenience, all computations below are assumed to take place in the residue field of a prime .
Finding the shortest recurrence of a vector or matrix sequence
To find the shortest recurrence of a vector sequence , let the vector dimension be . Choose a random -dimensional row vector , and find the shortest recurrence of the scalar sequence . By the Schwartz–Zippel lemma, the two shortest recurrences are identical with probability at least .
The matrix case is similar. For a matrix sequence of size , choose a random row vector and a random column vector , then find the shortest recurrence of the scalar sequence . Again by the Schwartz–Zippel lemma, the probability that the two recurrences agree is at least .
Optimizing matrix exponentiation
Let be an -dimensional column vector satisfying . Then is a vector sequence satisfying a linear recurrence of order at most . (Proof omitted.)
We can directly compute , find the shortest recurrence of using the method above, and then invoke Homogeneous linear recurrence with constant coefficients.
If is required, the complexity is . If is sparse with only nonzero entries, this decreases to . However, since the algorithm already needs time for preprocessing, an linear-recurrence algorithm is also acceptable when the constraints are not tight.
Finding the minimal polynomial of a matrix
The minimal polynomial of a square matrix is the polynomial of smallest degree satisfying .
The minimal polynomial is exactly the shortest recurrence of , so it can be found directly with the Berlekamp–Massey algorithm. If is an -th order square matrix, the degree of its minimal polynomial is clearly at most .
The bottleneck is obtaining , since multiplying matrices directly at every step would have complexity . But when finding the shortest recurrence of a matrix sequence, we are actually finding the shortest recurrence of . Thus it is enough to compute .
If has nonzero entries, the complexity is .
Finding the determinant of a sparse matrix
If the characteristic polynomial of a square matrix is known, its constant term multiplied by is the determinant. However, the minimal polynomial is not necessarily the characteristic polynomial.
If is multiplied by a random diagonal matrix , the minimal polynomial of is the characteristic polynomial with probability at least . It is then enough to divide by .
For an -th order square matrix with nonzero entries, the complexity is .
Finding the rank of a sparse matrix
For an matrix , first choose a random diagonal matrix and a random diagonal matrix , then find the minimal polynomial of .
There is no need to perform matrix multiplication explicitly. When finding the minimal polynomial, we only need to multiply by a vector, so we can apply these matrices to the vector one after another. The answer is the degree of the minimal polynomial after removing all factors of .
If has nonzero entries and , the complexity is .
Solving a sparse system of equations
Problem: Given , where is a full-rank sparse matrix and and are column vectors, solve for in less than time. are known.
Method: Clearly, . If we can find the shortest recurrence of for , where , then
(Proof omitted.)
Since is sparse, compute directly from the definition of the recurrence.
If has nonzero entries, the complexity is likewise .
Reference implementation
vector<int> solve_sparse_equations(const vector<tuple<int, int, int>> &A,
const vector<int> &b) {
int n = (int)b.size(); // 0-based
vector<vector<int>> f({b});
for (int i = 1; i < 2 * n; i++) {
vector<int> v(n);
auto &u = f.back();
for (auto [x, y, z] : A) // [x, y, value]
v[x] = (v[x] + (long long)u[y] * z) % p;
f.push_back(v);
}
vector<int> w(n);
mt19937 gen;
for (auto &x : w) x = uniform_int_distribution<int>(1, p - 1)(gen);
vector<int> a(2 * n);
for (int i = 0; i < 2 * n; i++)
for (int j = 0; j < n; j++) a[i] = (a[i] + (long long)f[i][j] * w[j]) % p;
auto c = berlekamp_massey(a);
int m = (int)c.size();
vector<int> ans(n);
for (int i = 0; i < m - 1; i++)
for (int j = 0; j < n; j++)
ans[j] = (ans[j] + (long long)c[m - 2 - i] * f[i][j]) % p;
int inv = power(p - c[m - 1], p - 2);
for (int i = 0; i < n; i++) ans[i] = (long long)ans[i] * inv % p;
return ans;
}