Tôi đang làm một lớp BigInt như một bài tập lập trình. Nó sử dụng một vectơ của phần bổ sung 2 ký tự int trong cơ sở-65536 (để các phép nhân 32 bit không tràn. Tôi sẽ tăng cơ sở khi tôi làm cho nó hoạt động hoàn toàn).Sư đoàn Newton-Raphson với số nguyên lớn
Tất cả các hoạt động toán học cơ bản được mã hóa, với một vấn đề: phân chia là đau đớn chậm với thuật toán cơ bản mà tôi có thể tạo. (Nó hoạt động như phân chia nhị phân cho mỗi chữ số của thương ... Tôi sẽ không đăng nó trừ khi ai đó muốn xem nó ....)
Thay vì thuật toán chậm, tôi muốn sử dụng Newton-Raphson để tìm (biến đổi) đối ứng và sau đó nhân (và thay đổi). Tôi nghĩ rằng tôi có đầu của tôi xung quanh những điều cơ bản: bạn đưa ra công thức (x1 = x0 (2 - x0 * ước)) đoán ban đầu tốt, và sau đó sau một số lần lặp lại, x hội tụ với nghịch đảo. Phần này có vẻ dễ dàng đủ ... nhưng tôi đang chạy vào một số vấn đề khi cố gắng áp dụng công thức này để số nguyên lớn:
Vấn đề 1:
Bởi vì tôi đang làm việc với số nguyên ... cũng .. Tôi không thể sử dụng phân số. Điều này dường như gây ra x để luôn phân kỳ (x0 * ước số phải là < 2 có vẻ như?). Trực giác của tôi nói với tôi rằng nên có một số sửa đổi phương trình mà sẽ cho phép nó để làm việc số nguyên (một số độ chính xác) nhưng tôi thực sự đấu tranh để tìm hiểu nó là gì. (Tôi thiếu kỹ năng toán học đang đánh tôi ở đây ....) Tôi nghĩ rằng tôi cần phải tìm một số phương trình tương đương, thay vì d có d * [base^somePower]? Có thể có một số phương trình như (x1 = x0 (2 - x0 * d)) hoạt động với các số nguyên không?
Vấn đề 2:
Khi tôi sử dụng công thức Newton để tìm nghịch đảo của một số con số, kết quả kết thúc lên được chỉ là một phe nhỏ dưới đây những gì câu trả lời nên ... cũ. khi cố gắng tìm đối ứng của 4 (trong hệ thập phân):
x0 = 0.3
x1 = 0.24
x2 = 0.2496
x3 = 0.24999936
x4 = 0.2499999999983616
x5 = 0.24999999999999999999998926258176
Nếu tôi được đại diện cho số trong cơ số 10, tôi sẽ muốn có một kết quả của 25 (và nhớ đến sản phẩm dịch phải bằng 2). Với một số đối ứng như 1/3, bạn có thể chỉ cần cắt ngắn kết quả sau khi bạn biết mình có đủ độ chính xác. Nhưng làm thế nào tôi có thể rút ra các đối ứng chính xác từ kết quả trên?
Xin lỗi nếu điều này quá mơ hồ hoặc nếu tôi yêu cầu quá nhiều. Tôi đã xem qua Wikipedia và tất cả các tài liệu nghiên cứu tôi có thể tìm thấy trên Google, nhưng tôi cảm thấy như tôi đang đập đầu vào tường. Tôi đánh giá cao sự giúp đỡ của bất cứ ai có thể cho tôi!
...
Chỉnh sửa: Got làm việc thuật toán, mặc dù nó là chậm hơn nhiều so với mong đợi của tôi. Tôi thực sự mất rất nhiều tốc độ so với thuật toán cũ của tôi, thậm chí trên con số với hàng nghìn chữ số ... Tôi vẫn còn thiếu cái gì đó. Nó không phải là một vấn đề với phép nhân, mà là rất nhanh. (Tôi thực sự sử dụng thuật toán của Karatsuba).
Đối với bất cứ ai quan tâm, đây là lần lặp hiện tại của tôi của thuật toán Newton-Raphson:
bigint operator/(const bigint& lhs, const bigint& rhs) {
if (rhs == 0) throw overflow_error("Divide by zero exception");
bigint dividend = lhs;
bigint divisor = rhs;
bool negative = 0;
if (dividend < 0) {
negative = !negative;
dividend.invert();
}
if (divisor < 0) {
negative = !negative;
divisor.invert();
}
int k = dividend.numBits() + divisor.numBits();
bigint pow2 = 1;
pow2 <<= k + 1;
bigint x = dividend - divisor;
bigint lastx = 0;
bigint lastlastx = 0;
while (1) {
x = (x * (pow2 - x * divisor)) >> k;
if (x == lastx || x == lastlastx) break;
lastlastx = lastx;
lastx = x;
}
bigint quotient = dividend * x >> k;
if (dividend - (quotient * divisor) >= divisor) quotient++;
if (negative)quotient.invert();
return quotient;
}
Và đây là (thực sự xấu xí) thuật toán cũ của tôi đó là nhanh hơn:
bigint operator/(const bigint& lhs, const bigint & rhs) {
if (rhs == 0) throw overflow_error("Divide by zero exception");
bigint dividend = lhs;
bigint divisor = rhs;
bool negative = 0;
if (dividend < 0) {
negative = !negative;
dividend.invert();
}
if (divisor < 0) {
negative = !negative;
divisor.invert();
}
bigint remainder = 0;
bigint quotient = 0;
while (dividend.value.size() > 0) {
remainder.value.insert(remainder.value.begin(), dividend.value.at(dividend.value.size() - 1));
remainder.value.push_back(0);
remainder.unPad();
dividend.value.pop_back();
if (divisor > remainder) {
quotient.value.push_back(0);
} else {
int count = 0;
int i = MSB;
bigint value = 0;
while (i > 0) {
bigint increase = divisor * i;
bigint next = value + increase;
if (next <= remainder) {
value = next;
count += i;
}
i >>= 1;
}
quotient.value.push_back(count);
remainder -= value;
}
}
for (int i = 0; i < quotient.value.size()/2; i++) {
int swap = quotient.value.at(i);
quotient.value.at(i) = quotient.value.at((quotient.value.size() - 1) - i);
quotient.value.at(quotient.value.size() - 1 - i) = swap;
}
if (negative)quotient.invert();
quotient.unPad();
return quotient;
}
[giải pháp của bạn trả về '1' thay vì' 2' cho '2/1'] (https://ideone.com/cGNsdl) ¶ Bạn nghĩ rằng bạn đã tìm thấy một giải pháp, bạn có thể [đăng nó làm câu trả lời của riêng bạn ] (https://stackoverflow.com/help/self-answer) (câu trả lời sẽ được đăng dưới dạng câu trả lời chứ không phải là các cập nhật câu hỏi). – jfs
Đây là một [làm việc (trong bài kiểm tra của tôi) 'unsigned_div_newton()' thực hiện bằng Python (văn bản bằng tiếng Nga)] (https://ru.stackoverflow.com/a/788422/23044). Việc triển khai dựa trên phân chia dài ('unsigned_div_long()') nhanh hơn nhiều so với các trường hợp tôi đã thử. – jfs