LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
multivariate_poly_arithmetic.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <optional>
4#include <stdexcept>
5
6namespace LMCAS {
7
8void MultiPoly::require_compatible_variables(const MultiPoly& other) const
9{
10 if (vars_ != other.vars_ && !is_zero() && !other.is_zero()) {
11 throw std::invalid_argument("MultiPoly: variable list mismatch");
12 }
13}
14
15/**
16 * @brief 多项式相加并合并同类项、去零、排序。
17 * 非零操作数须共享变量表;零元素采用非零侧的变量表和单项式序。
18 * @param[in] other 加数。
19 * @return 和多项式。
20 */
21MultiPoly MultiPoly::operator+(const MultiPoly& other) const
22{
23 if (other.is_zero()) return *this;
24 if (is_zero()) return other;
25 require_compatible_variables(other);
26
27 std::vector<Term> result_terms;
28 result_terms.reserve(terms_.size() + other.terms_.size());
29 result_terms.insert(result_terms.end(), terms_.begin(), terms_.end());
30 result_terms.insert(result_terms.end(), other.terms_.begin(), other.terms_.end());
31
32 MultiPoly result;
33 result.terms_ = std::move(result_terms);
34 result.vars_ = vars_;
35 result.order_ = order_;
36 result.normalize();
37 return result;
38}
39
40/**
41 * @brief 将 other 的各项系数取负后与自身相加。
42 * @param[in] other 减数。
43 * @return 差多项式。
44 */
45MultiPoly MultiPoly::operator-(const MultiPoly& other) const
46{
47 if (other.is_zero()) return *this;
48 if (is_zero()) return -other;
49 require_compatible_variables(other);
50
51 std::vector<Term> result_terms;
52 result_terms.reserve(terms_.size() + other.terms_.size());
53 result_terms.insert(result_terms.end(), terms_.begin(), terms_.end());
54
55 for (const auto& term : other.terms_) {
56 result_terms.emplace_back(term.first, -term.second);
57 }
58
59 MultiPoly result;
60 result.terms_ = std::move(result_terms);
61 result.vars_ = vars_;
62 result.order_ = order_;
63 result.normalize();
64 return result;
65}
66
67/**
68 * @brief 多项式相乘,指数逐分量相加、系数相乘,再合并同类项。
69 * @param[in] other 乘数。
70 * @return 积多项式。
71 */
72MultiPoly MultiPoly::operator*(const MultiPoly& other) const
73{
74 if (terms_.empty() || other.terms_.empty()) {
75 MultiPoly zero;
76 const MultiPoly& ring = is_zero() && !other.is_zero() ? other : *this;
77 zero.vars_ = ring.vars_;
78 zero.order_ = ring.order_;
79 return zero;
80 }
81 require_compatible_variables(other);
82
83 std::vector<Term> result_terms;
84 result_terms.reserve(terms_.size() * other.terms_.size());
85
86 size_t n_vars = vars_.size();
87
88 for (const auto& term_a : terms_) {
89 for (const auto& term_b : other.terms_) {
90 Monomial product_mono(n_vars, 0);
91 for (size_t i = 0; i < n_vars; ++i) {
92 int exp_a = (i < term_a.first.size()) ? term_a.first[i] : 0;
93 int exp_b = (i < term_b.first.size()) ? term_b.first[i] : 0;
94 product_mono[i] = exp_a + exp_b;
95 }
96 Rational product_coeff = term_a.second * term_b.second;
97 result_terms.emplace_back(std::move(product_mono), std::move(product_coeff));
98 }
99 }
100
101 MultiPoly result;
102 result.terms_ = std::move(result_terms);
103 result.vars_ = vars_;
104 result.order_ = order_;
105 result.normalize();
106 return result;
107}
108
109/**
110 * @brief 将多项式各项系数取负。
111 * @return 取负后的多项式。
112 */
113MultiPoly MultiPoly::operator-() const
114{
115 std::vector<Term> negated_terms;
116 negated_terms.reserve(terms_.size());
117
118 for (const auto& term : terms_) {
119 negated_terms.emplace_back(term.first, -term.second);
120 }
121
122 MultiPoly result;
123 result.terms_ = std::move(negated_terms);
124 result.vars_ = vars_;
125 result.order_ = order_;
126 return result;
127}
128
129/**
130 * @brief 比较多项式是否相等。
131 * 零多项式彼此相等;非零多项式须共享变量表且各单项式系数相同,排序方式不限。
132 * @param[in] other 比较对象。
133 * @return 相等时返回 true。
134 */
135bool MultiPoly::operator==(const MultiPoly& other) const
136{
137 if (terms_.size() != other.terms_.size()) return false;
138 if (is_zero()) return true;
139 if (vars_ != other.vars_) return false;
140 if (order_ != other.order_) {
141 const MonomialOrder order(other.order_);
142 for (const auto& term : terms_) {
143 auto found = std::lower_bound(
144 other.terms_.begin(), other.terms_.end(), term.first,
145 [&order](const Term& candidate, const Monomial& monomial) {
146 return order(candidate.first, monomial);
147 });
148 if (found == other.terms_.end() ||
149 found->first != term.first || found->second != term.second) {
150 return false;
151 }
152 }
153 return true;
154 }
155 for (size_t i = 0; i < terms_.size(); ++i) {
156 if (terms_[i].first != other.terms_[i].first) return false;
157 if (terms_[i].second != other.terms_[i].second) return false;
158 }
159 return true;
160}
161
162/**
163 * @brief 比较多项式是否不等。
164 * @param[in] other 比较对象。
165 * @return 不等时返回 true。
166 */
167bool MultiPoly::operator!=(const MultiPoly& other) const
168{
169 return !(*this == other);
170}
171
172/**
173 * @brief 各项系数乘以标量后规范化;零标量生成零多项式。
174 * @param[in] scalar 有理数标量。
175 * @return 标量乘积。
176 */
177MultiPoly MultiPoly::operator*(const Rational& scalar) const
178{
179 if (scalar.is_zero()) {
180 MultiPoly zero;
181 zero.vars_ = vars_;
182 zero.order_ = order_;
183 return zero;
184 }
185
186 std::vector<Term> scaled_terms;
187 scaled_terms.reserve(terms_.size());
188
189 for (const auto& term : terms_) {
190 scaled_terms.emplace_back(term.first, term.second * scalar);
191 }
192
193 MultiPoly result;
194 result.terms_ = std::move(scaled_terms);
195 result.vars_ = vars_;
196 result.order_ = order_;
197 result.normalize();
198 return result;
199}
200
201
202/**
203 * @brief 通过首项消去计算多元多项式的精确商。
204 * 每步以余式首项除以除数首项得到商项,再减去商项与除数的乘积;
205 * 除数首项单项式无法整除余式首项单项式时,判定为非精确除法。
206 * @param[in] divisor 除数多项式。
207 * @return 商多项式。
208 * @throw std::runtime_error 除数为零或除法不精确。
209 * @throw std::invalid_argument 两个非零操作数的变量表或排列不同。
210 */
211MultiPoly MultiPoly::exact_div(const MultiPoly& divisor) const
212{
213 if (divisor.is_zero()) {
214 throw std::runtime_error("exact division by zero polynomial");
215 }
216
217 if (is_zero()) {
218 MultiPoly zero;
219 zero.vars_ = divisor.vars_;
220 zero.order_ = divisor.order_;
221 return zero;
222 }
223 require_compatible_variables(divisor);
224
225 std::optional<MultiPoly> reordered_divisor;
226 if (order_ != divisor.order_) {
227 reordered_divisor.emplace(divisor);
228 reordered_divisor->order_ = order_;
229 reordered_divisor->normalize();
230 }
231 const MultiPoly& ordered_divisor = reordered_divisor ? *reordered_divisor : divisor;
232 const Monomial& divisor_lt_mono = ordered_divisor.terms_[0].first;
233 const Rational& divisor_lt_coeff = ordered_divisor.terms_[0].second;
234
235 size_t n_vars = vars_.size();
236
237 std::vector<Term> quotient_terms;
238 MultiPoly remainder = *this;
239
240 while (!remainder.is_zero()) {
241 const Monomial& rem_lt_mono = remainder.terms_[0].first;
242 const Rational& rem_lt_coeff = remainder.terms_[0].second;
243
244 if (!divides_monomial(divisor_lt_mono, rem_lt_mono)) {
245 throw std::runtime_error("exact division failed");
246 }
247
248 Monomial q_mono(n_vars, 0);
249 for (size_t i = 0; i < n_vars; ++i) {
250 int rem_exp = (i < rem_lt_mono.size()) ? rem_lt_mono[i] : 0;
251 int div_exp = (i < divisor_lt_mono.size()) ? divisor_lt_mono[i] : 0;
252 q_mono[i] = rem_exp - div_exp;
253 }
254
255 Rational q_coeff = rem_lt_coeff / divisor_lt_coeff;
256
257 quotient_terms.emplace_back(q_mono, q_coeff);
258
259 MultiPoly q_term_poly(std::vector<Term>{{q_mono, q_coeff}}, vars_, order_);
260 remainder = remainder - (q_term_poly * ordered_divisor);
261 }
262
263 return MultiPoly(std::move(quotient_terms), vars_, order_);
264}
265
266/**
267 * @brief 提取所有系数的有理数 GCD 作为数值内容。
268 * 有理数 GCD 满足 gcd(a/b, c/d) = gcd(a, c) / lcm(b, d)。
269 * @return 非零多项式返回正有理数;零多项式返回 Rational(0)。
270 */
271Rational MultiPoly::numeric_content() const
272{
273 if (terms_.empty()) {
274 return Rational(0);
275 }
276
277 BigInt num_gcd = terms_[0].second.get_numerator().abs();
278 BigInt den_lcm = terms_[0].second.get_denominator();
279
280 for (size_t i = 1; i < terms_.size(); ++i) {
281 BigInt num_i = terms_[i].second.get_numerator().abs();
282 BigInt den_i = terms_[i].second.get_denominator();
283
284 num_gcd = BigInt::gcd(num_gcd, num_i);
285 BigInt den_gcd = BigInt::gcd(den_lcm, den_i);
286 den_lcm = den_lcm * den_i / den_gcd;
287 }
288
289 if (!num_gcd) {
290 return Rational(0);
291 }
292
293 return Rational(num_gcd, den_lcm);
294}
295
296/**
297 * @brief 除以数值内容并调整符号,使首项系数为正。
298 * 零多项式原样返回;内容为 1 时仅调整首项符号。
299 * @return 零多项式或数值内容为 1、首项系数为正的本原多项式。
300 */
301MultiPoly MultiPoly::make_primitive() const
302{
303 if (terms_.empty()) {
304 return *this;
305 }
306
307 Rational content = numeric_content();
308
309 if (content.is_zero() || content == Rational(1)) {
310 if (!terms_.empty() && terms_[0].second < Rational(0)) {
311 return (*this) * Rational(-1);
312 }
313 return *this;
314 }
315
316 std::vector<Term> prim_terms;
317 prim_terms.reserve(terms_.size());
318
319 for (const auto& term : terms_) {
320 prim_terms.emplace_back(term.first, term.second / content);
321 }
322
323 if (!prim_terms.empty() && prim_terms[0].second < Rational(0)) {
324 for (auto& term : prim_terms) {
325 term.second = -term.second;
326 }
327 }
328
329 MultiPoly result;
330 result.terms_ = std::move(prim_terms);
331 result.vars_ = vars_;
332 result.order_ = order_;
333 return result;
334}
335
336}
单项式序比较器,支持 Lex、GrevLex、DegLex、DegRevLex
多元多项式类 MultiPoly,稀疏表示,支持任意数量变量上的环运算。
bool is_zero(const SymbolicExpr &expression)
std::vector< int > Monomial
单项式类型,以整数向量表示各变量的指数
const std::vector< FGLMPoly > const MonomialOrder & order
@ Rational
Rational numbers
bool divides_monomial(const Monomial &divisor, const Monomial &target)
判断 divisor 是否整除 target