Код Java-рекурсии умножения Карацубы не работает?

Я пытаюсь умножить два числа, используя умножение Карацубы. Мой java-код не работает. Я использовал строку в качестве параметров и аргументов, чтобы мы могли умножать два n-значных числа (n четно). Кроме того, я не хочу использовать long или BigInteger. Пожалуйста, помогите мне понять мою ошибку кода.

class karat{

public static String karatsuba(String first, String second){

    if(first.length() <= 1 || second.length() <= 1)
        return String.valueOf(Long.parseLong(first)*Long.parseLong(second));

    String a = karatsuba(first.substring(0, first.length()/2), second.substring(0, second.length()/2));

    String b = karatsuba(first.substring(first.length() - first.length()/2, first.length()), second.substring(second.length() - second.length()/2, second.length()));

    String c = karatsuba(String.valueOf(Long.parseLong(first.substring(0, first.length()/2)) + Long.parseLong(first.substring(first.length() - first.length()/2, first.length()))), String.valueOf(Long.parseLong(second.substring(0, second.length()/2)) + Long.parseLong(second.substring(second.length() - second.length()/2, second.length()))));

    String d = String.valueOf(Long.parseLong(c) - Long.parseLong(b) - Long.parseLong(a));

    return String.valueOf(((int)Math.pow(10, first.length()))*(Long.parseLong(a)) + (((int)Math.pow(10, first.length()/2))*Long.parseLong(d)) + (Long.parseLong(c)));
}

public static void main(String[] args){
        String result = karatsuba("1234", "5678");
        System.out.println(result); }
}

Не могли бы вы также уточнить мой код.

Переданные на умножение числа - 1234 и 5678

Вывод - 6655870 (неверно)

Вывод должен быть - 7006652 (правильно)

Спасибо


person Sid    schedule 20.08.2017    source источник
comment
Попробуйте записать, что означает умножение на Math.pow(10, length/2)Math.pow(10, 2*(length/2))). Сделайте несколько примеров вручную (особенно с разной длиной/не степенью двойки).   -  person greybeard    schedule 20.08.2017
comment
На бумаге все нормально. Например, я прошел 22 и 22. Вывод 496, должно быть 484. Не знаю, что не так.   -  person Sid    schedule 20.08.2017
comment
Вы (без нужды) используете String.valueOf(s.substring()), неоднократно определяете идентичные части входных данных (например, first.substring(0, first.length()/2)) и не даете имена этим частям: это затрудняет понимание вашего кода и споры о нем. Выберите легкодоступное представление алгоритма Карацубы для справки и назовите переменные для частей соответственно. Большинство поставляется с (крошечным) примером; используйте отладчик для отслеживания шагов вашего кода. Адаптируйте представленный здесь код и определите ссылку. Если вы все еще не можете обнаружить ошибку (их больше), опишите, где именно вы застряли.   -  person greybeard    schedule 20.08.2017
comment
Ваша проблема очень похожа на эту: stackoverflow.com/questions /28233748/   -  person biziclop    schedule 20.08.2017


Ответы (2)


Прежде всего, я попытался взглянуть на ваш код, он заставляет программиста теряться, кое-что, прежде чем мы перейдем к решению.

Общий совет. Не рекомендуется преобразовывать строку в значение и обратно и вперед, как вы, это не работает так. Я также пытался отладить ваш код, это просто дьявольский круг.

Поэтому я бы начал с проверки длины значения и максимального значения.

Чем, если одно из значений меньше 2 длины, означает, что все, что меньше 10, выполняет умножение, в противном случае - алгоритм рекурсии Карацубы.

Вот решение:

public static long karatsuba(long num1, long num2) {

    int m = Math.max(
            String.valueOf(num1).length(),
            String.valueOf(num2).length()
    );

    if (m < 2)
        return num1 * num2;

    m = (m / 2) + (m % 2);

    long b = num1 >> m;
    long a = num1 - (b << m);
    long d = num2 >> m;
    long c = num2 - (d << m);

    long ac = karatsuba(a, c);
    long bd = karatsuba(b, d);
    long abcd = karatsuba(a + b, c + d);

    return ac + (abcd - ac - bd << m) + (bd << 2 * m);
}

Некоторый тест;

public static void main(String[] args) {
    System.out.println(karatsuba(1, 9));
    System.out.println(karatsuba(1234, 5678));
    System.out.println(karatsuba(12345, 6789));
}

Результат будет

9
7006652
83810205

Это меньше боли, чем ваш код Stringish. Кстати, решение взято из pesudo в вики и этого класс.

person maytham-ɯɐɥʇʎɐɯ    schedule 20.08.2017
comment
Спасибо. В следующий раз я всегда буду заботиться о написании кода, который будет легко читать и другим. Если я не буду использовать строки, как я буду умножать числа, большие, чем числа в дальнем диапазоне? Я преобразовываю строку в длинную и наоборот для вычислений в строке, потому что я не могу умножить две отходящие строки. Моя главная цель - умножить n-значные числа, которые могут быть больше, чем числа, присутствующие в дальнем диапазоне. - person Sid; 21.08.2017
comment
Это называется «Большие числа», посмотрите на класс, на который я дал ссылку в своем ответе, это именно то, что вы ищете, если вы считаете, что ответ был полезен, пожалуйста, проверьте как ответ - person maytham-ɯɐɥʇʎɐɯ; 21.08.2017
comment
Спасибо майтам. Разве я не могу использовать строки? - person Sid; 21.08.2017
comment
Это возможно, как и раньше, тогда вам нужно преобразовать назад и вперед, и помните, что вы не можете использовать строку для принудительного максимального значения любого типа, например, максимальное целое значение равно 2147483647, вы не можете использовать строку больше этого числа и конвертировать это для int одинаково справедливо для любого типа, что вам нужно проверить, с какой спецификацией размера числа вы хотите работать. - person maytham-ɯɐɥʇʎɐɯ; 21.08.2017

Интересный алгоритм. Одна ошибка в

return String.valueOf(((int)Math.pow(10, first.length()))*(Long.parseLong(a)) + (((int)Math.pow(10, first.length()/2))*Long.parseLong(d)) + (Long.parseLong(c)));

В конце должно быть Long.parseLong(b) вместо Long.parseLong(c).

А при промежуточных вычислениях может случиться так, что две строки будут разной длины. Это тоже работает не корректно.

Пожалуйста, разрешите некоторые комментарии для улучшения реализации. Идея использовать строки, кажется, позволяет использовать большие числа, но затем вы вводите такие вещи, как Long.parseLong() или (int)Math.pow(10, first.length()), ограничивая вас диапазоном long или int.

Если вы действительно хотите делать большие числа, напишите свое собственное сложение на основе строк и умножение в десятой степени (это тривиально, добавляя несколько нулей).

И старайтесь избегать таких имен, как a, b, c или d — слишком легко забыть, что они означают, как и ваша первоначальная ошибка. Например. имена из Википедии немного лучше (с использованием z0, z1 и z2), но все же не идеально...

person Ralf Kleberhoff    schedule 20.08.2017
comment
Спасибо за помощь. После внесения изменений в соответствии с вашими предложениями программа работает, но вывод по-прежнему неверен на 1234 * 5678. Что не так ? - person Sid; 20.08.2017
comment
Как я уже сказал, И в промежуточных вычислениях может случиться так, что две строки будут разной длины. Это тоже работает не корректно. Следуйте за потоком вашей программы (либо с помощью отладчика, либо путем вставки вызовов System.out.println()), и тогда вы увидите случай, когда ваш метод karatsuba возвращает неправильный результат. Тщательно проанализируйте этот случай шаг за шагом, и вы увидите, что происходит не так. Еще один совет: не делайте так много в одной строке кода — вводите промежуточные переменные, это упрощает отладку. - person Ralf Kleberhoff; 20.08.2017