Согласно Википедии (http://en.wikipedia.org/wiki/Binary_GCD_algorithm) Я пытался написать двоичный GCD для больших чисел (до 5000 цифр).
Сам мой GCD выглядит так:
bitset<N> gcd(bitset<N> u, bitset<N> v) {
bitset<N> one (string("1"));
bitset<N> zero (string("0"));
int shift;
if (u == 0) return v;
if (v == 0) return u;
for (shift = 0; ((u | v) & one) == zero; ++shift) {
u >>= 1;
v >>= 1;
}
while ((u & one) == zero) u >>= 1;
do {
while ((v & one) == zero) v >>= 1;
if (u.to_string() > v.to_string()) {
bitset<N> t = v;
v = u;
u = t;
}
bitsetSubtract(v,u);
} while (v != 0);
return u << shift;
}
Я также использую собственную функцию вычитания битов:
void bitsetSubtract(bitset<N> &x, const bitset<N> &y) {
bool borrow = false;
for (int i = 0; i < N; i++) {
if (borrow) {
if (x[i]) {
x[i] = y[i];
borrow = y[i];
} else {
x[i] = !y[i];
borrow = true;
}
} else {
if (x[i]) {
x[i] = !y[i];
borrow = false;
} else {
x[i] = y[i];
borrow = y[i];
}
}
}
}
Я не вижу места для улучшения скорости этого алгоритма (сам двоичный GCD работает быстро), но я получаю обратную связь, что моя программа работает слишком медленно.
Вы представляли bignum как массив цифр base-2 (двоичных).
Реальные библиотеки bignum не используют основание 2. Они используют намного большую базу, потому что процессоры имеют инструкции, которые работают более чем на один бит за раз. Обычно вы используете базу 256 (28) 65536 (2)16) 4294967296 (232) или 18446744073709551616 (264) если ваша цель — максимальная скорость и минимальный размер, или база 100 (с одним байтом на цифру), 10000 (с двумя байтами на цифру), 1000000000 (с четырьмя байтами на цифру) или 10000000000000000000 (с восемью байтами на цифру) ) если вы должны хранить точные десятичные дроби.
Вам нужно использовать что-то вроде vector<uint32_t>
или же vector<uint64_t>
как ваш bignum, и работать с 32 или 64 битами за раз, а не только 1 бит за раз.
Других решений пока нет …