|
| 1 | +# 41143250 |
| 2 | +# 問題一 |
| 3 | + |
| 4 | +## 解題說明 |
| 5 | + |
| 6 | +本題要求實作一個多項式(Polynomial)類別,該類別可以建立多項式物件,並支援以下操作: |
| 7 | +加法、減法、乘法與指定值的代入計算,多項式由若干非零項所組成,每項以 <coef, exp> 表示,其中 $coef$ 為係數(整數),$exp$ 為非負整數次方。 |
| 8 | + |
| 9 | +### 解題策略 |
| 10 | + |
| 11 | +1. **資料結構設計** |
| 12 | +使用 Term 結構儲存每一項的係數與指數。 |
| 13 | +使用 Chain<Term> 節點類別與循環鏈結串列來儲存多項式項目,串列首節點為頭節點,其 next 指向自身表示空多項式。 |
| 14 | +透過 Available<Term> 類別管理節點回收與重複利用,減少動態配置的成本。 |
| 15 | + |
| 16 | +2. **建構與清除** |
| 17 | +建構子建立空的循環鏈結串列。 |
| 18 | +clear() 函式負責釋放所有節點並回收至 Available 物件中,以便重複使用。 |
| 19 | + |
| 20 | +3. **多項式輸入與輸出** |
| 21 | +輸入運算子 >>:讀入多項式項數與每項的 (coef, exp),依序插入鏈結串列。 |
| 22 | +輸出運算子 <<:依次輸出多項式項,支援正負號與次方格式化。 |
| 23 | + |
| 24 | +4. **多項式運算** |
| 25 | +加法 (operator+):同時走訪兩個鏈結串列,按指數大小合併;若指數相同則加總係數,係數為 0 則略過該項。 |
| 26 | +減法 (operator-):將另一多項式係數全部取負後呼叫加法運算。 |
| 27 | +乘法 (operator*):雙重迴圈將每一項相乘後插入結果中,若存在相同次方則合併。 |
| 28 | + |
| 29 | +5. **代入運算 (Evaluate)** |
| 30 | +依序將每項計算 $coef \times x^{exp}$ 後加總得到多項式值。 |
| 31 | + |
| 32 | + |
| 33 | +### 解題範例 |
| 34 | + |
| 35 | +假設 |
| 36 | +多項式 $ pA(x) = 2x^2 + x + 3 $ |
| 37 | +多項式 $ pA(x) = 2x^2 + x + 3 $ |
| 38 | + |
| 39 | +加法結果:$ 2x^2 + 2x + 7 $ |
| 40 | +減法結果:$ 2x^2 + 0x - 1 $ |
| 41 | +乘法結果:$ 2x^3 + 9x^2 + 7x + 12 $ |
| 42 | +代入 $x = 2$ 至 $p_B(x)$:$ 1\times 2 + 4 = 6 $ |
| 43 | + |
| 44 | +## 程式實作 |
| 45 | + |
| 46 | +以下為主要程式碼: |
| 47 | + |
| 48 | +```cpp |
| 49 | +#include <iostream> |
| 50 | +#include <cmath> |
| 51 | +using namespace std; |
| 52 | + |
| 53 | +// ------------------ 資料結構 ------------------ |
| 54 | +struct Term { |
| 55 | + int coef; |
| 56 | + int exp; |
| 57 | + Term(int c = 0, int e = 0) : coef(c), exp(e) {} |
| 58 | +}; |
| 59 | + |
| 60 | +template <class T> |
| 61 | +class Chain { |
| 62 | + friend class Polynomial; |
| 63 | + template <class U> friend class Available; |
| 64 | + |
| 65 | +public: |
| 66 | + T term; |
| 67 | + Chain<T>* next; |
| 68 | + Chain() : term(T()), next(nullptr) {} |
| 69 | + Chain(const T& t) : term(t), next(nullptr) {} |
| 70 | + Chain(const T& t, Chain<T>* n) : term(t), next(n) {} |
| 71 | + Chain(int coef, int exp) : term(coef, exp), next(nullptr) {} |
| 72 | +}; |
| 73 | + |
| 74 | +template <class T> |
| 75 | +class Available { |
| 76 | +private: |
| 77 | + Chain<T>* avaList; |
| 78 | +public: |
| 79 | + Available() : avaList(nullptr) {} |
| 80 | + ~Available() { |
| 81 | + while (avaList) { |
| 82 | + Chain<T>* temp = avaList; |
| 83 | + avaList = avaList->next; |
| 84 | + delete temp; |
| 85 | + } |
| 86 | + } |
| 87 | + void putBack(Chain<T>* node) { |
| 88 | + Chain<T>* current = node; |
| 89 | + while (current->next) current = current->next; |
| 90 | + current->next = avaList; |
| 91 | + avaList = node; |
| 92 | + } |
| 93 | + Chain<T>* getOneNode() { |
| 94 | + if (!avaList) return new Chain<T>(); |
| 95 | + Chain<T>* node = avaList; |
| 96 | + avaList = avaList->next; |
| 97 | + node->next = nullptr; |
| 98 | + return node; |
| 99 | + } |
| 100 | +}; |
| 101 | + |
| 102 | +class Polynomial { |
| 103 | +private: |
| 104 | + Chain<Term>* head; |
| 105 | + Available<Term> recycler; |
| 106 | + |
| 107 | + Chain<Term>* newTerm(int c, int e) { |
| 108 | + Chain<Term>* node = recycler.getOneNode(); |
| 109 | + node->term.coef = c; |
| 110 | + node->term.exp = e; |
| 111 | + node->next = nullptr; |
| 112 | + return node; |
| 113 | + } |
| 114 | + void clear() { |
| 115 | + if (!head) return; |
| 116 | + Chain<Term>* cur = head->next; |
| 117 | + while (cur != head) { |
| 118 | + Chain<Term>* temp = cur; |
| 119 | + cur = cur->next; |
| 120 | + recycler.putBack(temp); |
| 121 | + } |
| 122 | + head->next = head; |
| 123 | + } |
| 124 | +public: |
| 125 | + Polynomial() { |
| 126 | + head = new Chain<Term>(); |
| 127 | + head->next = head; |
| 128 | + } |
| 129 | + ~Polynomial() { |
| 130 | + clear(); |
| 131 | + delete head; |
| 132 | + } |
| 133 | + |
| 134 | + // 輸入 |
| 135 | + friend istream& operator>>(istream& is, Polynomial& p) { |
| 136 | + p.clear(); |
| 137 | + int n; |
| 138 | + if (!(is >> n)) return is; |
| 139 | + Chain<Term>* tail = p.head; |
| 140 | + for (int i = 0; i < n; i++) { |
| 141 | + int c, e; |
| 142 | + is >> c >> e; |
| 143 | + Chain<Term>* node = p.newTerm(c, e); |
| 144 | + tail->next = node; |
| 145 | + tail = node; |
| 146 | + } |
| 147 | + tail->next = p.head; |
| 148 | + return is; |
| 149 | + } |
| 150 | + |
| 151 | + // 輸出 |
| 152 | + friend ostream& operator<<(ostream& os, const Polynomial& p) { |
| 153 | + Chain<Term>* cur = p.head->next; |
| 154 | + bool first = true; |
| 155 | + if (cur == p.head) { |
| 156 | + os << 0; |
| 157 | + return os; |
| 158 | + } |
| 159 | + while (cur != p.head) { |
| 160 | + int c = cur->term.coef; |
| 161 | + int e = cur->term.exp; |
| 162 | + if (!first && c > 0) os << " + "; |
| 163 | + if (c < 0) os << " - "; |
| 164 | + int absC = abs(c); |
| 165 | + if (absC != 1 || e == 0) os << absC; |
| 166 | + if (e > 0) os << "x"; |
| 167 | + if (e > 1) os << "^" << e; |
| 168 | + first = false; |
| 169 | + cur = cur->next; |
| 170 | + } |
| 171 | + return os; |
| 172 | + } |
| 173 | + |
| 174 | + // 複製建構 |
| 175 | + Polynomial(const Polynomial& other) { |
| 176 | + head = new Chain<Term>(); |
| 177 | + head->next = head; |
| 178 | + Chain<Term>* tail = head; |
| 179 | + Chain<Term>* cur = other.head->next; |
| 180 | + while (cur != other.head) { |
| 181 | + Chain<Term>* node = newTerm(cur->term.coef, cur->term.exp); |
| 182 | + tail->next = node; |
| 183 | + tail = node; |
| 184 | + cur = cur->next; |
| 185 | + } |
| 186 | + tail->next = head; |
| 187 | + } |
| 188 | + |
| 189 | + // 指派運算子 |
| 190 | + Polynomial& operator=(const Polynomial& other) { |
| 191 | + if (this != &other) { |
| 192 | + clear(); |
| 193 | + Chain<Term>* tail = head; |
| 194 | + Chain<Term>* cur = other.head->next; |
| 195 | + while (cur != other.head) { |
| 196 | + Chain<Term>* node = newTerm(cur->term.coef, cur->term.exp); |
| 197 | + tail->next = node; |
| 198 | + tail = node; |
| 199 | + cur = cur->next; |
| 200 | + } |
| 201 | + tail->next = head; |
| 202 | + } |
| 203 | + return *this; |
| 204 | + } |
| 205 | + |
| 206 | + // 加法 |
| 207 | + Polynomial operator+(const Polynomial& b) const { |
| 208 | + Polynomial result; |
| 209 | + Chain<Term>* aPtr = head->next; |
| 210 | + Chain<Term>* bPtr = b.head->next; |
| 211 | + Chain<Term>* tail = result.head; |
| 212 | + while (aPtr != head || bPtr != b.head) { |
| 213 | + if (aPtr == head) { |
| 214 | + tail->next = result.newTerm(bPtr->term.coef, bPtr->term.exp); |
| 215 | + bPtr = bPtr->next; |
| 216 | + } |
| 217 | + else if (bPtr == b.head) { |
| 218 | + tail->next = result.newTerm(aPtr->term.coef, aPtr->term.exp); |
| 219 | + aPtr = aPtr->next; |
| 220 | + } |
| 221 | + else if (aPtr->term.exp > bPtr->term.exp) { |
| 222 | + tail->next = result.newTerm(aPtr->term.coef, aPtr->term.exp); |
| 223 | + aPtr = aPtr->next; |
| 224 | + } |
| 225 | + else if (aPtr->term.exp < bPtr->term.exp) { |
| 226 | + tail->next = result.newTerm(bPtr->term.coef, bPtr->term.exp); |
| 227 | + bPtr = bPtr->next; |
| 228 | + } |
| 229 | + else { |
| 230 | + int sum = aPtr->term.coef + bPtr->term.coef; |
| 231 | + if (sum != 0) { |
| 232 | + tail->next = result.newTerm(sum, aPtr->term.exp); |
| 233 | + } |
| 234 | + else { |
| 235 | + tail->next = result.head; |
| 236 | + break; |
| 237 | + } |
| 238 | + aPtr = aPtr->next; |
| 239 | + bPtr = bPtr->next; |
| 240 | + } |
| 241 | + tail = tail->next; |
| 242 | + } |
| 243 | + tail->next = result.head; |
| 244 | + return result; |
| 245 | + } |
| 246 | + |
| 247 | + // 減法 |
| 248 | + Polynomial operator-(const Polynomial& b) const { |
| 249 | + Polynomial negB = b; |
| 250 | + Chain<Term>* cur = negB.head->next; |
| 251 | + while (cur != negB.head) { |
| 252 | + cur->term.coef = -cur->term.coef; |
| 253 | + cur = cur->next; |
| 254 | + } |
| 255 | + return (*this) + negB; |
| 256 | + } |
| 257 | + |
| 258 | + // 乘法 |
| 259 | + Polynomial operator*(const Polynomial& b) const { |
| 260 | + Polynomial result; |
| 261 | + for (Chain<Term>* aPtr = head->next; aPtr != head; aPtr = aPtr->next) { |
| 262 | + for (Chain<Term>* bPtr = b.head->next; bPtr != b.head; bPtr = bPtr->next) { |
| 263 | + int c = aPtr->term.coef * bPtr->term.coef; |
| 264 | + int e = aPtr->term.exp + bPtr->term.exp; |
| 265 | + // 合併同類項 |
| 266 | + Chain<Term>* cur = result.head->next; |
| 267 | + Chain<Term>* prev = result.head; |
| 268 | + bool found = false; |
| 269 | + while (cur != result.head) { |
| 270 | + if (cur->term.exp == e) { |
| 271 | + cur->term.coef += c; |
| 272 | + found = true; |
| 273 | + break; |
| 274 | + } |
| 275 | + prev = cur; |
| 276 | + cur = cur->next; |
| 277 | + } |
| 278 | + if (!found && c != 0) { |
| 279 | + Chain<Term>* node = result.newTerm(c, e); |
| 280 | + prev->next = node; |
| 281 | + node->next = result.head; |
| 282 | + } |
| 283 | + } |
| 284 | + } |
| 285 | + return result; |
| 286 | + } |
| 287 | + |
| 288 | + // 代入計算 |
| 289 | + float Evaluate(float x) const { |
| 290 | + float resultVal = 0; |
| 291 | + Chain<Term>* cur = head->next; |
| 292 | + while (cur != head) { |
| 293 | + resultVal += cur->term.coef * pow(x, cur->term.exp); |
| 294 | + cur = cur->next; |
| 295 | + } |
| 296 | + return resultVal; |
| 297 | + } |
| 298 | +}; |
| 299 | + |
| 300 | + |
| 301 | +// ------------------ 主程式 ------------------ |
| 302 | +int main() { |
| 303 | + Polynomial pA, pB; |
| 304 | + cout << "請輸入polyA" << endl; |
| 305 | + cin >> pA; |
| 306 | + cout << "請輸入polyB" << endl; |
| 307 | + cin >> pB; |
| 308 | + |
| 309 | + cout << "polyA + polyB = " << pA + pB << endl; |
| 310 | + cout << "polyA - polyB = " << pA - pB << endl; |
| 311 | + cout << "polyA * polyB = " << pA * pB << endl; |
| 312 | + cout << "polyB(2.0) = " << pB.Evaluate(2.0) << endl; |
| 313 | + return 0; |
| 314 | +} |
| 315 | +``` |
| 316 | + |
| 317 | +## 效能分析 |
| 318 | + |
| 319 | +1. 時間複雜度: |
| 320 | +Evaluate: $O(n)$ |
| 321 | +加法 / 減法:$O(n + m)$ |
| 322 | +乘法:最差 $O(n \times m)$(需合併同類項) |
| 323 | + |
| 324 | +2. 空間複雜度 |
| 325 | +主要為儲存多項式節點的鏈結串列:$O(n)$ |
| 326 | +乘法結果最多 $O(n \times m)$ 項,取決於次方分布。 |
| 327 | + |
| 328 | +## 測試與驗證 |
| 329 | + |
| 330 | +### 測試案例 |
| 331 | + |
| 332 | +| 測試案例 | polyA | polyB | polyA+polyB | polyA-polyB | polyA×polyB | polyB(2.0) | |
| 333 | +|----------|------------|--------|-------------|-------------|-------------------|------------| |
| 334 | +| 測試一 | $2x^2+x+3$ | $$x+4$ | $2x^2+2x+7$ | $2x^2-1$ | $2x^3+9x^2+7x+12$ | 6 | |
| 335 | + |
| 336 | +## 申論及開發報告 |
| 337 | + |
| 338 | +### 類別設計的思路 |
| 339 | + |
| 340 | +在本程式中,使用遞迴來實現阿克曼函數的主要原因如下: |
| 341 | + |
| 342 | +1. **封裝性高** |
| 343 | + 使用 Polynomial 封裝多項式操作,Term 封裝單項,Available 封裝節點管理,結構清晰且易於維護。 |
| 344 | +2. **功能模組化** |
| 345 | + 輸入、輸出、加減乘、代入計算均獨立成員函式,便於測試與擴展。 |
| 346 | +3. **鏈結串列實作** |
| 347 | + 採循環鏈結串列,可快速判斷空多項式 (head->next == head),插入與清除效率高,不必預先分配容量。 |
| 348 | +4. **可擴展設計** |
| 349 | + 可進一步加入排序保證、簡化顯示、同次項自動合併等優化功能。 |
0 commit comments