Рекурсивный алгоритм Карацубы, дающий неточные ответы

Я пытался реализовать алгоритм умножения Карацубы на С++ для двух больших чисел одинаковой длины. Мой код работает правильно для меньших чисел, таких как 3456*1492, но не работает с большими числами, такими как 64-значные. Обычно он получает первые несколько цифр правильно, но после этого идет наперекосяк. Я использую библиотеку мультиточности Boost для обработки больших целых чисел.

Мой код выглядит следующим образом:

cpp_int karatsuba(cpp_int n1, cpp_int n2) {
    if (n1 < 10 || n2 < 10) {
        return n1 * n2;
    }

    int len = countDigits(n1);

    cpp_int a = n1 / cpp_int(pow(10, len/2));
    cpp_int b = n1 % cpp_int(pow(10, len/2));
    cpp_int c = n2 / cpp_int(pow(10, len/2));
    cpp_int d = n2 % cpp_int(pow(10, len/2));

    cpp_int sub1 = karatsuba(a, c);
    cpp_int sub2 = karatsuba(b, d);
    cpp_int sub3 = karatsuba(a+b, c+d) - sub1 - sub2;

    return sub1 * cpp_int(pow(10, len)) + sub3 * cpp_int(pow(10, len/2)) + sub2;
}

Я сравнил свой код с несколькими реализациями в Интернете и не смог найти существенных различий, которые могли бы повлиять на ответ. Возможно, есть проблема с точностью типа cpp_int или проблема в моем коде?

Большое вам спасибо за вашу помощь!


person Arnav Garg    schedule 25.05.2020    source источник
comment
Какова реализация countDigits?   -  person Jan Schultke    schedule 26.05.2020
comment
Числа, с которыми он терпит неудачу, слишком велики, чтобы поместиться в double, но я получаю те же проблемы с uint1024_t.   -  person Arnav Garg    schedule 26.05.2020
comment
countDigits многократно делится на 10, пока число не станет равным 0, и подсчитывает количество делений. Я протестировал countDigits, поэтому уверен, что проблема не в нем.   -  person Arnav Garg    schedule 26.05.2020
comment
Вы смотрели на Карацубу в Boost.Multiprecision? github.com/boostorg/multiprecision/blob/ разработать/включить/повысить/   -  person user14717    schedule 26.05.2020
comment
зачем использовать pow внутри умножения? это безумие и сводит на нет преимущества сложности Карацубы, поскольку pow - это более высокая операция (которая также использует умножение внутри, которое привело бы к переполнению стека, если бы ваш Карацуба использовался кстати.) и намного медленнее ... используйте мощность 2 базы для разделения и битовых операций (бит сдвиги, И маскировка...) вместо этого. Также см. Быстрое вычисление квадрата большого числа.   -  person Spektre    schedule 26.05.2020


Ответы (1)


Я подозреваю, что одна проблема заключается в том, что pow возвращает представление с плавающей запятой, которое неточно преобразуется в cpp_int.

Может помочь использование мультиточной версии pow.

Кроме того, len/2 округляется в меньшую сторону, поэтому при вычислении pow(10,len) вместо этого должно быть pow(10,(len/2)*2).

person Peter de Rivaz    schedule 25.05.2020
comment
Переключение на многоточную реализацию pow устранило проблему, с которой я столкнулся. Большое спасибо! - person Arnav Garg; 26.05.2020