Длинная арифметика: возведение в степень - C (СИ)
Формулировка задачи:
Вычислить с помощью алгоритмов длинной арифметики значение числа 3^5000 и представить его в шестнадцатеричной системе счисления. Помогите реализовать. Какой алгоритм лучше подходит?
Решение задачи: «Длинная арифметика: возведение в степень»
textual
Листинг программы
#ifndef KARATSUBA_H_INCLUDED
#define KARATSUBA_H_INCLUDED
#include <basetsd.h>
typedef struct _BigInt
{
UINT32 *digits;
int length;
} BigInt;
BigInt *add(BigInt *, BigInt *);
BigInt *subtract(BigInt *, BigInt *);
BigInt *multiply(BigInt *, BigInt *);
#endif // KARATSUBA_H_INCLUDED
#include <stdlib.h>
#include "karatsuba.h"
void normalize(BigInt *);
BigInt *add(BigInt *a, BigInt *b)
{
if (a->length < b->length)
{
BigInt *t = a; a = b; b = t;
}
BigInt *r = malloc(sizeof(BigInt));
r->length = a->length + 1;
r->digits = malloc(r->length * sizeof(UINT32));
UINT32 carry = 0, i;
for (i = 0; i < b->length; i++)
{
UINT64 d = (UINT64)a->digits[i] + b->digits[i] + carry;
r->digits[i] = (UINT32)(d & 0xFFFFFFFF);
carry = (UINT32)(d >> 32);
}
for (; i < a->length; i++)
{
UINT64 d = (UINT64)a->digits[i] + carry;
r->digits[i] = (UINT32)(d & 0xFFFFFFFF);
carry = (UINT32)(d >> 32);
}
r->digits[i] = carry;
normalize(r);
return r;
}
BigInt *multiply(BigInt *a, BigInt *b)
{
BigInt *r = malloc(sizeof(BigInt));
// 1. База индукции
if (a->length == 1 && b->length == 1)
{
r->length = 2;
UINT64 res = a->digits[0] * b->digits[0];
r->digits = malloc(2 * sizeof(UINT32));
r->digits[0] = (UINT32)(res & 0xFFFFFFFF);
r->digits[1] = (UINT32)(res >> 32);
}
else
{
int length = // первая степень двойки, не меньшая чем a-Length и b->length
// заполнить нулями недостающие цифры
BigInt *a0 = malloc(sizeof(BigInt));
a0->length = length >> 1;
a0->digits = malloc((length >> 1) * sizeof(UINT32));
memcpy(a0->digits, a->digits, (length >> 1) * sizeof(UINT32));
BigInt *a1 = // вторая половина a
BigInt *b0 = // первая половина b
BigInt *b1 = // вторая половина b
BigInt *z0 = multiply(a0, b0);
BigInt *z2 = multiply(a1, b1);
BigInt *z1 = subtract(subtract(multiply(add(a0, a1), add(b0, b1)), z0), z2);
// собрать результат
}
//
normalize(r);
return r;
}
void normalize(BigInt *a)
{
int i = a->length-1;
while (i > 0 && a->digits[i] == 0) i--;
a->digits = realloc(a->digits, a->length = i+1);
}
Объяснение кода листинга программы
Код представляет собой реализацию алгоритма длинной арифметики для работы с большими целыми числами. Алгоритм состоит из функций:
- add - сложение двух больших целых чисел
- subtract - вычитание двух больших целых чисел
- multiply - умножение двух больших целых чисел
- normalize - приведение большого целого числа к нормальному виду (сведение концов) Список действий:
- Если a->length < b->length, то меняем местами a и b
- Создаем новый большой целый число r, которое будет результатом сложения
- Заполняем r->digits и r->length
- Проходим по всем цифрам a и b, начиная с самых старших разрядов
- Вычисляем сумму текущих разрядов a и b, а также учитываем перенос (carry)
- Если текущий разряд a равен 0, то игнорируем его
- Если текущий разряд b равен 0, то игнорируем его
- Если текущий разряд a и b не равен 0, то вычисляем сумму и перенос
- Добавляем сумму в r и учитываем перенос
- Если перенос не равен 0, то добавляем его в r
- Рекурсивно вызываем multiply для младших половинок a и b
- Рекурсивно вызываем multiply для старших половинок a и b
- Рекурсивно вызываем subtract для z0 и z2
- Рекурсивно вызываем subtract для z1 и z3
- Рекурсивно вызываем add для z0 и z2
- Рекурсивно вызываем add для z1 и z3
- Рекурсивно вызываем multiply для z0 и z2
- Рекурсивно вызываем multiply для z1 и z3
- Рекурсивно вызываем subtract для z0 и z2
- Рекурсивно вызываем subtract для z1 и z3