LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
exact_sturm.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <exception>
5
6namespace LMCAS::detail {
7namespace {
8
9using SturmSequence = std::vector<Polynomial<Rational>>;
10
11int sign_changes(const std::vector<int>& signs) {
12 int changes = 0;
13 int previous = 0;
14 for (const int current : signs) {
15 if (current == 0) continue;
16 if (previous != 0 && current != previous) ++changes;
17 previous = current;
18 }
19 return changes;
20}
21
23 const Polynomial<Rational>& polynomial,
24 const Rational& point) {
25 if (polynomial.is_zero()) return 0;
26 const int degree = polynomial.degree();
27 const BigInt numerator = point.get_numerator();
28 const BigInt denominator = point.get_denominator();
29 BigInt coefficient_denominator(1);
30 for (const auto& coefficient : polynomial.coeffs) {
31 coefficient_denominator *= coefficient.get_denominator();
32 }
33 std::vector<BigInt> numerator_powers(
34 static_cast<std::size_t>(degree) + 1, BigInt(1));
35 std::vector<BigInt> denominator_powers(
36 static_cast<std::size_t>(degree) + 1, BigInt(1));
37 for (int exponent = 1; exponent <= degree; ++exponent) {
38 numerator_powers[static_cast<std::size_t>(exponent)] =
39 numerator_powers[static_cast<std::size_t>(exponent - 1)] *
40 numerator;
41 denominator_powers[static_cast<std::size_t>(exponent)] =
42 denominator_powers[static_cast<std::size_t>(exponent - 1)] *
44 }
45 BigInt value(0);
46 for (int exponent = 0; exponent <= degree; ++exponent) {
47 const auto& coefficient =
48 polynomial.coeffs[static_cast<std::size_t>(exponent)];
49 if (coefficient == Rational(0)) continue;
50 value += coefficient.get_numerator() *
51 (coefficient_denominator / coefficient.get_denominator()) *
52 numerator_powers[static_cast<std::size_t>(exponent)] *
53 denominator_powers[
54 static_cast<std::size_t>(degree - exponent)];
55 }
56 if (value == BigInt(0)) return 0;
57 return value < BigInt(0) ? -1 : 1;
58}
59
61 Polynomial<Rational> polynomial,
63 const std::string& operation) {
64 auto step = context.consume_steps(1, operation);
65 if (!step) return Result<SturmSequence>::failure(step.error());
66
67 polynomial.trim();
68 if (polynomial.degree() <= 0) {
70 }
71 polynomial = polynomial.square_free_part().make_monic();
72
73 SturmSequence sequence;
74 sequence.push_back(polynomial);
75 sequence.push_back(polynomial.differentiate());
76 while (!sequence.back().is_zero()) {
77 step = context.consume_steps(1, operation);
78 if (!step) return Result<SturmSequence>::failure(step.error());
79 const auto& dividend = sequence[sequence.size() - 2];
80 const auto& divisor = sequence.back();
81 auto division = dividend.div_mod(divisor);
82 auto remainder = std::move(division.second);
83 if (remainder.is_zero()) break;
84 for (auto& coefficient : remainder.coeffs) coefficient = -coefficient;
85 remainder.trim();
86 sequence.push_back(std::move(remainder));
87 }
88 if (!sequence.empty() && sequence.back().is_zero()) sequence.pop_back();
89 return Result<SturmSequence>::success(std::move(sequence));
90}
91
93 const SturmSequence& sequence,
94 const Rational& point,
96 const std::string& operation) {
97 auto step = context.consume_steps(sequence.size() + 1, operation);
98 if (!step) return Result<int>::failure(step.error());
99 std::vector<int> signs;
100 signs.reserve(sequence.size());
101 for (const auto& member : sequence) {
102 signs.push_back(polynomial_sign_at(member, point));
103 }
104 return Result<int>::success(sign_changes(signs));
105}
106
108 const Polynomial<Rational>& square_free,
109 const SturmSequence& sequence,
110 const Rational& lower,
111 const Rational& upper,
113 const std::string& operation) {
114 if (!(lower < upper) || sequence.empty()) {
116 }
117 auto lower_variations = variations_at(sequence, lower, context, operation);
118 if (!lower_variations) {
119 return Result<std::size_t>::failure(lower_variations.error());
120 }
121 auto upper_variations = variations_at(sequence, upper, context, operation);
122 if (!upper_variations) {
123 return Result<std::size_t>::failure(upper_variations.error());
124 }
125 int count = lower_variations.value() - upper_variations.value();
126 if (polynomial_sign_at(square_free, upper) == 0) --count;
127 if (count < 0) {
130 "Sturm variation produced a negative open-interval count",
131 operation);
132 }
133 return Result<std::size_t>::success(static_cast<std::size_t>(count));
134}
135
136
138 Rational lower;
139 Rational upper;
140 std::size_t count;
141};
142
144 const Polynomial<Rational>& square_free, const SturmSequence& sequence,
146 const std::string& operation) {
147 while (polynomial_sign_at(square_free, interval.lower) == 0 ||
148 polynomial_sign_at(square_free, interval.upper) == 0) {
149 const Rational midpoint = (interval.lower + interval.upper) / Rational(2);
150 if (polynomial_sign_at(square_free, midpoint) == 0) {
151 interval.lower = midpoint;
152 interval.upper = midpoint;
153 break;
154 }
155 const bool lower_is_root = polynomial_sign_at(square_free, interval.lower) == 0;
156 auto half = lower_is_root
157 ? count_open_roots(square_free, sequence, midpoint, interval.upper,
159 : count_open_roots(square_free, sequence, interval.lower, midpoint,
161 if (!half) return Result<void>::failure(half.error());
162 if (lower_is_root == (half.value() == 1)) {
163 interval.lower = midpoint;
164 } else {
165 interval.upper = midpoint;
166 }
167 }
168 return Result<void>::success();
169}
170
172 const Polynomial<Rational>& square_free, const SturmSequence& sequence,
173 const PendingInterval& current, std::vector<PendingInterval>& pending,
174 std::vector<RationalInterval>& isolated, ComputationContext& context,
175 const std::string& operation) {
176 const Rational midpoint = (current.lower + current.upper) / Rational(2);
177 const bool midpoint_is_root = polynomial_sign_at(square_free, midpoint) == 0;
178 auto left = count_open_roots(
179 square_free, sequence, current.lower, midpoint, context, operation);
180 if (!left) return Result<void>::failure(left.error());
181 auto right = count_open_roots(
182 square_free, sequence, midpoint, current.upper, context, operation);
183 if (!right) return Result<void>::failure(right.error());
184 const std::size_t accounted = left.value() + right.value() +
185 (midpoint_is_root ? 1U : 0U);
186 if (accounted != current.count) {
189 "Sturm subdivision did not preserve the root count", operation);
190 }
191 if (right.value() != 0) {
192 pending.push_back({midpoint, current.upper, right.value()});
193 }
194 if (midpoint_is_root) isolated.emplace_back(midpoint, midpoint);
195 if (left.value() != 0) {
196 pending.push_back({current.lower, midpoint, left.value()});
197 }
198 return Result<void>::success();
199}
200
202 const Polynomial<Rational>& square_free, const SturmSequence& sequence,
203 ComputationContext& context, const std::string& operation) {
204 const Rational bound = strict_cauchy_root_bound(square_free);
205 auto total = count_open_roots(square_free, sequence, -bound, bound, context, operation);
206 if (!total) return Result<std::vector<RationalInterval>>::failure(total.error());
207 std::vector<PendingInterval> pending;
208 std::vector<RationalInterval> isolated;
209 if (total.value() != 0) pending.push_back({-bound, bound, total.value()});
210 isolated.reserve(total.value());
211 while (!pending.empty()) {
212 auto step = context.consume_steps(1, operation);
213 if (!step) return Result<std::vector<RationalInterval>>::failure(step.error());
214 PendingInterval current = std::move(pending.back());
215 pending.pop_back();
216 if (current.count == 1) {
217 auto cleared = clear_isolation_endpoints(
218 square_free, sequence, current, context, operation);
219 if (!cleared) return Result<std::vector<RationalInterval>>::failure(cleared.error());
220 isolated.emplace_back(current.lower, current.upper);
221 continue;
222 }
223 auto subdivided = subdivide_isolation(
224 square_free, sequence, current, pending, isolated, context, operation);
225 if (!subdivided) return Result<std::vector<RationalInterval>>::failure(subdivided.error());
226 }
227 std::sort(isolated.begin(), isolated.end(),
228 [](const RationalInterval& left, const RationalInterval& right) {
229 if (left.first != right.first) return left.first < right.first;
230 return left.second < right.second;
231 });
232 if (isolated.size() != total.value()) {
235 "Sturm isolation lost a distinct real root", operation);
236 }
237 return Result<std::vector<RationalInterval>>::success(std::move(isolated));
238}
239
240} // namespace
241
243 const Polynomial<Rational>& polynomial,
244 const Rational& lower,
245 const Rational& upper,
247 const std::string& operation) {
248 if (upper < lower) {
251 "real-root interval bounds must be ordered",
252 operation);
253 }
254 try {
255 auto step = context.consume_steps(1, operation);
256 if (!step) return Result<std::size_t>::failure(step.error());
257 if (polynomial.degree() <= 0) {
259 }
260 auto square_free = polynomial.square_free_part().make_monic();
261 if (lower == upper) {
263 polynomial_sign_at(square_free, lower) == 0 ? 1U : 0U);
264 }
265 auto sequence = make_sturm_sequence(square_free, context, operation);
266 if (!sequence) return Result<std::size_t>::failure(sequence.error());
267 auto interior = count_open_roots(
268 square_free, sequence.value(), lower, upper, context, operation);
269 if (!interior) return interior;
270 std::size_t count = interior.value();
271 if (polynomial_sign_at(square_free, lower) == 0) ++count;
272 if (polynomial_sign_at(square_free, upper) == 0) ++count;
273 return Result<std::size_t>::success(count);
274 } catch (const std::bad_alloc&) {
277 "Sturm root counting allocation failed",
278 operation);
279 } catch (const std::exception& error) {
282 }
283}
284
287 const std::string& operation) {
288 try {
289 auto step = context.consume_steps(1, operation);
290 if (!step) return Result<std::vector<RationalInterval>>::failure(step.error());
291 if (polynomial.degree() <= 0) {
292 return Result<std::vector<RationalInterval>>::success({});
293 }
294 if (polynomial.degree() == 1) {
295 const Rational root = -polynomial.coeffs[0] / polynomial.coeffs[1];
296 return Result<std::vector<RationalInterval>>::success({{root, root}});
297 }
298 auto square_free = polynomial.square_free_part().make_monic();
299 auto sequence = make_sturm_sequence(square_free, context, operation);
300 if (!sequence) {
301 return Result<std::vector<RationalInterval>>::failure(sequence.error());
302 }
303 return isolate_sturm_sequence(square_free, sequence.value(), context, operation);
304 } catch (const std::bad_alloc&) {
307 "Sturm root isolation allocation failed", operation);
308 } catch (const std::exception& error) {
311 }
312}
313
314} // namespace LMCAS::detail
一元多项式模板类
Polynomial differentiate() const
求导
bool is_zero() const
判断是否为零多项式
std::vector< CoeffType > coeffs
系数数组,coeffs[i] 为 x^i 的系数
Polynomial square_free_part() const
计算无平方因子部分
int degree() const
获取多项式的次数
Polynomial make_monic() const
首一化(使首项系数为 1)
void trim()
去除高次零系数
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
Result< void > clear_isolation_endpoints(const Polynomial< Rational > &square_free, const SturmSequence &sequence, PendingInterval &interval, ComputationContext &context, const std::string &operation)
Result< std::size_t > count_open_roots(const Polynomial< Rational > &square_free, const SturmSequence &sequence, const Rational &lower, const Rational &upper, ComputationContext &context, const std::string &operation)
Result< SturmSequence > make_sturm_sequence(Polynomial< Rational > polynomial, ComputationContext &context, const std::string &operation)
int sign_changes(const std::vector< int > &signs)
std::vector< Polynomial< Rational > > SturmSequence
Result< int > variations_at(const SturmSequence &sequence, const Rational &point, ComputationContext &context, const std::string &operation)
Result< std::vector< RationalInterval > > isolate_sturm_sequence(const Polynomial< Rational > &square_free, const SturmSequence &sequence, ComputationContext &context, const std::string &operation)
Result< void > subdivide_isolation(const Polynomial< Rational > &square_free, const SturmSequence &sequence, const PendingInterval &current, std::vector< PendingInterval > &pending, std::vector< RationalInterval > &isolated, ComputationContext &context, const std::string &operation)
int polynomial_sign_at(const Polynomial< Rational > &polynomial, const Rational &point)
多元 Hensel 提升(单变量步)
Result< std::vector< RationalInterval > > isolate_real_roots_exact(const Polynomial< Rational > &polynomial, ComputationContext &context, const std::string &operation)
Result< std::size_t > count_real_roots_exact(const Polynomial< Rational > &polynomial, const Rational &lower, const Rational &upper, ComputationContext &context, const std::string &operation)
ComputationContext const std::string & operation
std::pair< Rational, Rational > RationalInterval
std::size_t std::size_t ComputationContext & context
Rational strict_cauchy_root_bound(const Polynomial< Rational > &polynomial)
const std::string const LimitExprPtr & point
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
ExprResult interval(ExprPtr lower, ExprPtr upper, bool lower_closed, bool upper_closed)
const ExprPtr & lower
const ExprPtr & exponent
ExprResult member(const ExprPtr &element, const ExprPtr &set, ComputationContext &context)
const ExprPtr & right
const std::string const ExprPtr & value
const ExprPtr & denominator