Một thử thách thú vị. :)
Tôi giả sử rằng bạn muốn các số nguyên có độ dài tùy ý. Tôi đề xuất cách tiếp cận sau:
Xem xét bản chất nhị phân của kiểu dữ liệu "int". Hãy nghĩ đến việc sử dụng các phép toán nhị phân đơn giản để mô phỏng những gì các mạch trong CPU của bạn thực hiện khi chúng thêm mọi thứ. Trong trường hợp bạn quan tâm sâu hơn, hãy xem xét đọc bài viết wikipedia này về bộ cộng nửa và bộ cộng đầy đủ . Bạn sẽ làm điều gì đó tương tự như vậy, nhưng bạn có thể đi xuống mức thấp như vậy - nhưng do lười biếng, tôi nghĩ rằng tôi sẽ bỏ qua và tìm một giải pháp đơn giản hơn.
Nhưng trước khi đi vào bất kỳ chi tiết thuật toán nào về cộng, trừ, nhân, chúng ta hãy tìm một số cấu trúc dữ liệu. Tất nhiên, một cách đơn giản là lưu trữ mọi thứ trong một vector std ::.
template< class BaseType >
class BigInt
{
typedef typename BaseType BT;
protected: std::vector< BaseType > value_;
};
Bạn có thể muốn cân nhắc xem bạn có muốn tạo vectơ có kích thước cố định hay không và có định phân bổ trước nó hay không. Lý do là đối với các phép toán đa dạng, bạn sẽ phải đi qua từng phần tử của vectơ - O (n). Bạn có thể muốn biết rõ một phép toán sẽ phức tạp như thế nào và số n cố định thực hiện điều đó.
Nhưng bây giờ đến một số thuật toán về hoạt động trên các con số. Bạn có thể làm điều đó ở cấp độ logic, nhưng chúng tôi sẽ sử dụng sức mạnh kỳ diệu của CPU để tính toán kết quả. Nhưng những gì chúng ta sẽ tiếp thu từ minh họa logic của Half- và FullAdders là cách nó xử lý. Ví dụ, hãy xem xét cách bạn triển khai toán tử + = . Đối với mỗi số trong BigInt <> :: value_, bạn sẽ thêm các số đó và xem kết quả có tạo ra một số hình thức thực hiện hay không. Chúng tôi sẽ không làm điều đó một cách khôn ngoan, nhưng dựa vào bản chất của BaseType của chúng tôi (có thể là dài hoặc int hoặc ngắn hoặc bất cứ điều gì): nó tràn.
Chắc chắn nếu bạn cộng hai số thì kết quả phải lớn hơn một số lớn hơn đúng không? Nếu không, thì kết quả bị tràn.
template< class BaseType >
BigInt< BaseType >& BigInt< BaseType >::operator += (BigInt< BaseType > const& operand)
{
BT count, carry = 0;
for (count = 0; count < std::max(value_.size(), operand.value_.size(); count++)
{
BT op0 = count < value_.size() ? value_.at(count) : 0,
op1 = count < operand.value_.size() ? operand.value_.at(count) : 0;
BT digits_result = op0 + op1 + carry;
if (digits_result-carry < std::max(op0, op1)
{
BT carry_old = carry;
carry = digits_result;
digits_result = (op0 + op1 + carry) >> sizeof(BT)*8;
}
else carry = 0;
}
return *this;
}
Phép toán số học khác tương tự. Heck, bạn thậm chí có thể sử dụng stl-functors std :: plus và std :: hidden, std :: times và std :: divides, ..., nhưng hãy nhớ mang theo. :) Bạn cũng có thể thực hiện phép nhân và chia bằng cách sử dụng các toán tử cộng và trừ, nhưng điều đó rất chậm, bởi vì điều đó sẽ tính toán lại kết quả bạn đã tính trong các lần gọi trước đến cộng và trừ trong mỗi lần lặp. Có rất nhiều thuật toán tốt cho công việc đơn giản này, hãy sử dụng wikipedia hoặc web.
Và tất nhiên, bạn nên triển khai các toán tử tiêu chuẩn như operator<<
(chỉ cần chuyển từng giá trị trong value_ sang trái cho n bit, bắt đầu từ value_.size()-1
... oh và nhớ mang :), operator<
- bạn thậm chí có thể tối ưu hóa một chút ở đây, kiểm tra số thô của chữ số với size()
đầu tiên. Và như thế. Sau đó, hãy biến lớp học của bạn trở nên hữu ích, bằng cách befriendig std :: ostream operator<<
.
Hy vọng cách tiếp cận này là hữu ích!