LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
multivariate_factor_patterns.cpp
浏览该文件的文档.
1/**
2 * @file multivariate_factor.cpp
3 * @brief 多元多项式因式分解器实现.
4 */
6#include <algorithm>
7#include <climits>
8#include <cstdint>
9#include <cmath>
10#include <map>
11#include <set>
12#include <stdexcept>
14namespace LMCAS {
15namespace detail {
16static std::optional<Rational> rational_square_root(const Rational& coefficient) {
17 const BigInt& numerator = coefficient.get_numerator();
18 const BigInt& denominator = coefficient.get_denominator();
19 if (numerator.is_negative()) { return std::nullopt; }
20 BigInt numerator_root = numerator.sqrt();
21 if (numerator_root * numerator_root != numerator) { return std::nullopt; }
22 BigInt denominator_root = denominator.sqrt();
23 if (denominator_root * denominator_root != denominator) { return std::nullopt; }
24 return Rational(numerator_root, denominator_root);
25}
26
27static MultiPoly homogenize_factor(const Polynomial<Rational>& uf,
28 const std::vector<std::string>& vars) {
29 int d = uf.degree();
30 // c*x^k becomes c*x^k*y^(d-k).
31 std::vector<MultiPoly::Term> homo_terms;
32 for (int k = 0; k <= d; ++k) {
33 Rational coeff = (k < static_cast<int>(uf.coeffs.size())) ? uf.coeffs[k] : Rational(0);
34 if (coeff.is_zero()) { continue; }
35 Monomial mono(2, 0);
36 mono[0] = k;
37 mono[1] = d - k;
38 homo_terms.emplace_back(std::move(mono), coeff);
39 }
40 MultiPoly homo_factor(std::move(homo_terms), vars);
41 homo_factor = homo_factor.make_primitive();
42 return homo_factor;
43}
44
45static void adjust_homogeneous_constant(const MultiPoly& poly,
46 MultiFactorResult& result) {
47 const auto& vars = poly.variables();
48 MultiPoly product(Rational(1), vars);
49 for (const auto& f : result.factors) {
50 product = product * f;
51 }
52 if (!product.is_zero() && !poly.is_zero()) {
53 try {
54 MultiPoly ratio = poly.exact_div(product);
55 if (ratio.is_constant()) {
56 result.constant = ratio.numeric_content();
57 if (!ratio.terms().empty() && ratio.terms()[0].second < Rational(0)) {
58 result.constant = -result.constant;
59 }
60 }
61 } catch (const std::runtime_error&) {
62 if (!product.terms().empty() && !poly.terms().empty()) {
63 result.constant = poly.terms()[0].second / product.terms()[0].second;
64 }
65 }
66 }
67}
68
69/**
70 * @brief 检测是否为线性多项式(主变量次数 <= 1)
71 */
72bool is_linear(const MultiPoly& poly, const std::string& main_var)
73{
74 return poly.degree(main_var) <= 1;
75}
76
77/**
78 * @brief 检测差平方模式 a^2 - b^2
79 *
80 * 判断多项式是否恰好有两项,一正一负,且两项的单项式各分量指数均为偶数.
81 * 若匹配,返回 (a, b),其中 a^2 为正项,b^2 为负项.
82 *
83 * @param[in] poly 输入多项式
84 * @return 若匹配则返回 (a, b),否则返回 nullopt
85 */
86std::optional<std::pair<MultiPoly, MultiPoly>>
87detect_difference_of_squares(const MultiPoly& poly)
88{
89 if (poly.num_terms() != 2) { return std::nullopt; }
90
91 const auto& terms = poly.terms();
92 const auto& vars = poly.variables();
93
94 /// 确定哪项为正,哪项为负
95 int pos_idx = -1, neg_idx = -1;
96 if (terms[0].second > Rational(0) && terms[1].second < Rational(0)) {
97 pos_idx = 0; neg_idx = 1;
98 } else if (terms[1].second > Rational(0) && terms[0].second < Rational(0)) {
99 pos_idx = 1; neg_idx = 0;
100 } else {
101 return std::nullopt;
102 }
103
104 /// 检查系数绝对值相等(都应为完全平方的系数)
105 /// 对于差平方 a^2 - b^2,正项系数和负项系数的绝对值不必相等
106 /// 但两项的系数本身必须是完全平方数
107 Rational pos_coeff = terms[pos_idx].second;
108 Rational neg_coeff = -terms[neg_idx].second; // 取绝对值
109
110 /// 检查系数是否为完全平方有理数
111 /// 分子和分母都必须是完全平方数
112 auto positive_root = rational_square_root(pos_coeff);
113 if (!positive_root) { return std::nullopt; }
114 auto negative_root = rational_square_root(neg_coeff);
115 if (!negative_root) { return std::nullopt; }
116
117 /// 检查两项的单项式各分量指数均为偶数
118 const Monomial& pos_mono = terms[pos_idx].first;
119 const Monomial& neg_mono = terms[neg_idx].first;
120
121 for (size_t i = 0; i < pos_mono.size(); ++i) {
122 if (pos_mono[i] % 2 != 0) { return std::nullopt; }
123 }
124 for (size_t i = 0; i < neg_mono.size(); ++i) {
125 if (neg_mono[i] % 2 != 0) { return std::nullopt; }
126 }
127
128 /// 构造 a:正项单项式指数减半,系数为 sqrt(pos_coeff)
129 Monomial a_mono(vars.size(), 0);
130 for (size_t i = 0; i < pos_mono.size(); ++i) {
131 a_mono[i] = pos_mono[i] / 2;
132 }
133 const Rational& a_coeff = *positive_root;
134 std::vector<MultiPoly::Term> a_terms = {{a_mono, a_coeff}};
135 MultiPoly a(std::move(a_terms), vars);
136
137 /// 构造 b:负项单项式指数减半,系数为 sqrt(neg_coeff)
138 Monomial b_mono(vars.size(), 0);
139 for (size_t i = 0; i < neg_mono.size(); ++i) {
140 b_mono[i] = neg_mono[i] / 2;
141 }
142 const Rational& b_coeff = *negative_root;
143 std::vector<MultiPoly::Term> b_terms = {{b_mono, b_coeff}};
144 MultiPoly b(std::move(b_terms), vars);
145
146 return std::make_pair(a, b);
147}
148
149/**
150 * @brief 检测二项式幂模式 xⁿ - yⁿ
151 *
152 * 判断多项式是否为两个不同变量的纯幂之差,系数分别为 +1 和 -1,
153 * 且幂次相同.若匹配,返回 (变量1, 变量2, 幂次).
154 *
155 * @param[in] poly 输入多项式
156 * @return 若匹配则返回 (var1, var2, n),否则返回 nullopt
157 */
158std::optional<std::tuple<std::string, std::string, int>>
159detect_binomial_power(const MultiPoly& poly)
160{
161 if (poly.num_terms() != 2) { return std::nullopt; }
162
163 const auto& terms = poly.terms();
164 const auto& vars = poly.variables();
165
166 /// 确定正项和负项
167 int pos_idx = -1, neg_idx = -1;
168 if (terms[0].second == Rational(1) && terms[1].second == Rational(-1)) {
169 pos_idx = 0; neg_idx = 1;
170 } else if (terms[1].second == Rational(1) && terms[0].second == Rational(-1)) {
171 pos_idx = 1; neg_idx = 0;
172 } else {
173 return std::nullopt;
174 }
175
176 /// 检查每项的单项式是否为单变量的纯幂(恰好一个非零指数)
177 auto get_single_var_power = [&](const Monomial& mono)
178 -> std::optional<std::pair<int, int>> {
179 int nonzero_count = 0;
180 int var_index = -1;
181 int exponent = 0;
182 for (size_t i = 0; i < mono.size(); ++i) {
183 if (mono[i] != 0) {
184 nonzero_count++;
185 var_index = static_cast<int>(i);
186 exponent = mono[i];
187 }
188 }
189 if (nonzero_count != 1 || exponent <= 1) { return std::nullopt; }
190 return std::make_pair(var_index, exponent);
191 };
192
193 auto pos_info = get_single_var_power(terms[pos_idx].first);
194 if (!pos_info) { return std::nullopt; }
195 auto neg_info = get_single_var_power(terms[neg_idx].first);
196 if (!neg_info) { return std::nullopt; }
197
198 auto [pos_var_idx, pos_exp] = *pos_info;
199 auto [neg_var_idx, neg_exp] = *neg_info;
200
201 /// 幂次必须相同,变量必须不同
202 if (pos_exp != neg_exp) { return std::nullopt; }
203 if (pos_var_idx == neg_var_idx) { return std::nullopt; }
204
205 return std::make_tuple(vars[pos_var_idx], vars[neg_var_idx], pos_exp);
206}
207
208/**
209 * @brief 提取公因子单项式
210 *
211 * 计算所有项单项式的逐分量最小值(GCD 单项式),将其从每项中减去.
212 *
213 * @param[in] poly 输入多项式
214 * @return pair(公因子单项式, 提取后的多项式)
215 */
216std::pair<Monomial, MultiPoly>
217extract_common_monomial(const MultiPoly& poly)
218{
219 const auto& vars = poly.variables();
220 Monomial identity(vars.size(), 0);
221
222 if (poly.is_zero() || poly.num_terms() == 0) {
223 return {identity, poly};
224 }
225
226 const auto& terms = poly.terms();
227
228 /// 计算逐分量最小值
229 Monomial gcd_mono = terms[0].first;
230 for (size_t t = 1; t < terms.size(); ++t) {
231 const Monomial& mono = terms[t].first;
232 for (size_t i = 0; i < gcd_mono.size(); ++i) {
233 int val = (i < mono.size()) ? mono[i] : 0;
234 if (val < gcd_mono[i]) { gcd_mono[i] = val; }
235 }
236 }
237
238 /// 检查是否为平凡(全零)
239 bool is_trivial = true;
240 for (size_t i = 0; i < gcd_mono.size(); ++i) {
241 if (gcd_mono[i] != 0) { is_trivial = false; break; }
242 }
243 if (is_trivial) { return {identity, poly}; }
244
245 /// 从每项中减去 GCD 单项式
246 std::vector<MultiPoly::Term> quotient_terms;
247 quotient_terms.reserve(terms.size());
248 for (const auto& term : terms) {
249 Monomial new_mono(vars.size(), 0);
250 for (size_t i = 0; i < vars.size(); ++i) {
251 int orig = (i < term.first.size()) ? term.first[i] : 0;
252 new_mono[i] = orig - gcd_mono[i];
253 }
254 quotient_terms.emplace_back(std::move(new_mono), term.second);
255 }
256
257 MultiPoly quotient(std::move(quotient_terms), vars);
258 return {gcd_mono, quotient};
259}
260
261/**
262 * @brief 齐次二元多项式分解
263 *
264 * 对齐次二元多项式 f(x, y),令 y=1 得到一元多项式 f(x, 1),
265 * 对其进行一元因式分解,然后将每个因子重新齐次化.
266 *
267 * @param[in] poly 输入多项式(须为齐次二元)
268 * @param[in,out] context 调用方的计算预算和取消状态
269 * @return 不适用时成功返回 nullopt;分解错误原样传播
270 */
273 const MultiPoly& poly,
275{
276 if (!poly.is_homogeneous() || poly.num_vars() != 2) {
277 return std::optional<MultiFactorResult>{};
278 }
279
280 const auto& vars = poly.variables();
281 std::string var_x = vars[0];
282 std::string var_y = vars[1];
283
284 /// 去齐次化:令 y = 1,得到关于 x 的一元多项式
285 MultiPoly dehomogenized = poly.eval(var_y, Rational(1));
286
287 /// 转换为一元多项式
288 Polynomial<Rational> uni_poly;
289 try {
290 uni_poly = dehomogenized.to_univariate();
291 } catch (const std::invalid_argument&) {
292 return std::optional<MultiFactorResult>{};
293 }
294
295 /// 一元因式分解
296 auto checked_factors =
298 if (!checked_factors) {
300 checked_factors.error());
301 }
302 if (checked_factors.value().completeness != Completeness::Complete) {
303 return std::optional<MultiFactorResult>{};
304 }
305 std::vector<Polynomial<Rational>> uni_factors =
306 std::move(checked_factors.value().value);
307 if (uni_factors.size() <= 1) {
308 return std::optional<MultiFactorResult>{};
309 }
310
311 /// 重新齐次化每个因子
312 MultiFactorResult result;
313 result.constant = poly.numeric_content();
314
315 for (const auto& uf : uni_factors) {
316 auto homo_factor = homogenize_factor(uf, vars);
317 result.factors.push_back(std::move(homo_factor));
318 result.multiplicities.push_back(1);
319 }
320
321 adjust_homogeneous_constant(poly, result);
322
323 return std::optional<MultiFactorResult>(std::move(result));
324}
325
326} // namespace detail
327} // namespace LMCAS
一元多项式模板类
std::vector< CoeffType > coeffs
系数数组,coeffs[i] 为 x^i 的系数
int degree() const
获取多项式的次数
多元多项式因式分解器公共 API。
多元 Hensel 提升(单变量步)
std::pair< Monomial, MultiPoly > extract_common_monomial(const MultiPoly &poly)
提取公因子单项式
bool is_linear(const MultiPoly &poly, const std::string &main_var)
检测是否为线性多项式(主变量次数 <= 1)
static void adjust_homogeneous_constant(const MultiPoly &poly, MultiFactorResult &result)
Result< std::optional< MultiFactorResult > > factor_homogeneous_bivariate(const MultiPoly &poly, ComputationContext &context)
齐次二元多项式分解
static std::optional< Rational > rational_square_root(const Rational &coefficient)
const std::string & main_var
static MultiPoly homogenize_factor(const Polynomial< Rational > &uf, const std::vector< std::string > &vars)
std::optional< std::tuple< std::string, std::string, int > > detect_binomial_power(const MultiPoly &poly)
检测二项式幂模式 xⁿ - yⁿ
std::size_t std::size_t ComputationContext & context
std::optional< std::pair< MultiPoly, MultiPoly > > detect_difference_of_squares(const MultiPoly &poly)
检测差平方模式 a^2 - b^2
std::vector< int > Monomial
单项式类型,以整数向量表示各变量的指数
__attribute__((visibility("default"))) VectorCalculusFieldResult gradient_checked(const std __attribute__((visibility("default"))) VectorCalculusFieldResult gradient_checked(const std __attribute__((visibility("default"))) VectorField gradient(const std const std::vector< std::string > & vars
std::vector< MultiPoly > factors
精确因子列表(各因子本原)
const MultiPoly & b
UnivariateFactorResult factor_univariate_bridge_checked(const Polynomial< Rational > &polynomial, ComputationContext &context)
const ExprPtr & exponent
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
const ExprPtr & denominator
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string & t
Rational constant
数值常数因子
std::vector< int > multiplicities
各因子的重数