Есть ли алгоритм для точного умножения двух целых чисел произвольной длины? Язык, с которым я работаю, ограничен длиной 64-битного целого числа без знака (максимальный размер целого числа 18446744073709551615). На самом деле, я хотел бы иметь возможность сделать это, разбивая каждое число, обрабатывая их каким-либо образом, используя беззнаковые 64-битные целые числа, а затем получая возможность снова объединить их в строку (что решило бы проблему умножения результата. место хранения).
Есть идеи?





Да, вы делаете это, используя тип данных, который фактически представляет собой строку цифр (точно так же, как обычная «строка» - это строка символов). Как вы это делаете, сильно зависит от языка. Например, Java использует BigDecimal. На каком языке ты говоришь?
Freebasic, который, как мне кажется, не имеет встроенной функции. Я готов написать его, если бы я мог получить представление об используемом алгоритме.
Это часто дают в качестве домашнего задания. Алгоритм, который вы изучили в начальной школе, будет работать. Используйте библиотеку (некоторые из них упоминаются в других сообщениях), если вам это нужно для реального приложения.
В большинстве языков есть функции или библиотеки, которые делают это, обычно называемые библиотекой Bignum (GMP - хороший вариант).
Если вы хотите сделать это сами, я бы сделал это так же, как люди делают длинное умножение на бумаге. Для этого вы можете либо работать со строками, содержащими число, либо делать это в двоичном формате, используя побитовые операции.
Пример:
45
x67
---
315
+270
----
585
Или в двоичном формате:
101
x101
----
101
000
+101
------
11001
Редактировать: Сделав это в двоичном формате, я понял, что было бы намного проще (и, конечно, быстрее) кодировать, используя побитовые операции вместо строк, содержащих числа с основанием 10. Я отредактировал свой пример двоичного умножения, чтобы показать шаблон: для каждого 1 бита в нижнем числе добавьте к переменной верхнее число, сдвинутое влево положение 1-битного раз. В конце эта переменная будет содержать продукт.
Чтобы сохранить продукт, вам нужно иметь два 64-битных числа и представить, что одно из них является первыми 64 битами, а другое - вторыми 64 битами продукта. Вам нужно будет написать код, который переносит добавление от 63 бита второго числа к биту 0 первого числа.
Поздравляю, вы только что заново изобрели «крестьянское умножение». ;) en.wikipedia.org/wiki/…
Есть гораздо более быстрые алгоритмы умножения, чем «крестьянское умножение».
Самый простой способ - использовать механизм учебника, разбивая числа произвольного размера на блоки по 32 бита каждый.
Дано A B C D * E F G H (каждый фрагмент 32-битный, всего 128 бит)
Вам нужен выходной массив шириной 9 слов.
Установите [0..8] на 0
Для начала вы выполните: H * D + out [8] => 64-битный результат.
Сохраните младшие 32 бита в out [8] и возьмите старшие 32 бита как переносимые
Далее: (H * C) + out [7] + carry
Опять же, сохраните младшие 32 бита в out [7], используйте старшие 32 бита в качестве переноса
.
после выполнения H * A + out [4] + Carry вам нужно продолжать цикл до тех пор, пока у вас не закончится перенос.
Затем повторите с G, F, E.
.
Для G вы должны начать с out [7] вместо out [8] и так далее.
Наконец, перейдите и преобразуйте большое целое число в цифры (для этого потребуется процедура «разделить большое число на одно слово»)
Если вы не можете использовать существующую библиотеку bignum, такую как GMP, проверьте Статья в Википедии о двоичном умножении на компьютерах. Для этого существует ряд хороших эффективных алгоритмов.
Вот мой фрагмент кода на C. Старый добрый метод умножения
char *multiply(char s1[], char s2[]) {
int l1 = strlen(s1);
int l2 = strlen(s2);
int i, j, k = 0, c = 0;
char *r = (char *) malloc (l1+l2+1); // add one byte for the zero terminating string
int temp;
strrev(s1);
strrev(s2);
for (i = 0;i <l1+l2; i++) {
r[i] = 0 + '0';
}
for (i = 0; i <l1; i ++) {
c = 0; k = i;
for (j = 0; j < l2; j++) {
temp = get_int(s1[i]) * get_int(s2[j]);
temp = temp + c + get_int(r[k]);
c = temp /10;
r[k] = temp%10 + '0';
k++;
}
if (c!=0) {
r[k] = c + '0';
k++;
}
}
r[k] = '\0';
strrev(r);
return r;
}
//Here is a JavaScript version of an Karatsuba Algorithm running with less time than the usual multiplication method
function range(start, stop, step) {
if (typeof stop == 'undefined') {
// one param defined
stop = start;
start = 0;
}
if (typeof step == 'undefined') {
step = 1;
}
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
return [];
}
var result = [];
for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
result.push(i);
}
return result;
};
function zeroPad(numberString, zeros, left = true) {
//Return the string with zeros added to the left or right.
for (var i in range(zeros)) {
if (left)
numberString = '0' + numberString
else
numberString = numberString + '0'
}
return numberString
}
function largeMultiplication(x, y) {
x = x.toString();
y = y.toString();
if (x.length == 1 && y.length == 1)
return parseInt(x) * parseInt(y)
if (x.length < y.length)
x = zeroPad(x, y.length - x.length);
else
y = zeroPad(y, x.length - y.length);
n = x.length
j = Math.floor(n/2);
//for odd digit integers
if ( n % 2 != 0)
j += 1
var BZeroPadding = n - j
var AZeroPadding = BZeroPadding * 2
a = parseInt(x.substring(0,j));
b = parseInt(x.substring(j));
c = parseInt(y.substring(0,j));
d = parseInt(y.substring(j));
//recursively calculate
ac = largeMultiplication(a, c)
bd = largeMultiplication(b, d)
k = largeMultiplication(a + b, c + d)
A = parseInt(zeroPad(ac.toString(), AZeroPadding, false))
B = parseInt(zeroPad((k - ac - bd).toString(), BZeroPadding, false))
return A + B + bd
}
//testing the function here
example = largeMultiplication(12, 34)
console.info(example)в вашем коде много синтаксических ошибок, которые приводят к сбою компиляции. Я исправил некоторые из них, но код по-прежнему не работает
ты уверен, что это работает? почему вы удаляете тег js, чтобы люди могли проверить?
это было не умышленно, я просто заново заменил код на рабочий. Вы можете создать plunkr для этого или jsfiddle
Отметьте это> алгоритм умножения больших чисел