Поиск по сайту.



Математика. Пути и Графы. Комбинаторика и перебор

Сортировка

Защита и сокрытие информации. Атаки и взлом

Сжатие информации и кодирование. СRC

Графика и обработка изображений. Фракталы

Поиск в строках, массивах,
последовательностях


Разбор выражений.
Компиляторы и интерпретаторы


Cтруктуры данных.
Хранение информации


AI, ГА, Нейронные сети

Вейвлеты

Игры, и все с ними связанное

Разное


Софт: просмотр PS и PDF файлов

   Написать веб-мастеру
   Почитать историю сайта

Математика: Теория чисел: Длинная арифметика.

Представление длинных чисел

Основные операции

Быстрое умножение длинных чисел методом Шенхаге-Штрассена (через преобразование Фурье)

Исходники - реализация основных операций стандартным образом и (!) алгоритма Шенхаге-Штрассена вместе с БПФ.

fft.c
fft.h
bigint.h
bigint.c

1.   Представление длинных чисел.

Способ представления зависит от наших целей. Обычно, большое число N представляется в виде

X = x0 + x1 B + x2 B2 + ... + xn Bn.      , если оно целое.
X = x0 + x1
B
+ x2
B2
+ ... + xn
Bn
      или       x = y * Be    , если дробное.

B - основание системы счисления, в которой записывается число. Все xi - стандартные числа /long или double, например/ и 0 <= xi < B

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

Знак числа либо хранится отдельно, либо все xi < 0.

Основание B обычно зависит от максимального размера базового типа данных на компьютере, и выбирается, исходя из следующих соображений:

  • Основание B подходить под один из базовых типов данных

  • B должно быть как можно больше, чтобы уменьшить размер представления длинного числа и увеличить скорость операций с ними, но достаточно малого размера, чтобы все операции с коэффициентами использовали базовый тип данных.

  • Для удобства можно выбрать B как степень 10 ( вывод информации, отладка ). B - степень двойки позволяет проводить быстрые операции на низком уровне. Нужно иметь в виду, что быстрый переход от основания B = 2m к B = 10n для больших чисел реализовать весьма и весьма непросто.
    Некоторые задачи, например определение простоты, могут использовать реализацию при B = 2m, так как вывод - логическое ДА или НЕТ.

Для коэффициентов xi естественно выбрать тип long (в Cи). Однако, long обычно занимает 32 бита, а использование double (Си - обычно 53 бита) дает возможность получить большее основание.

Кроме того, некоторые процессоры ( напр. pentium ) специально оптимизированы для операций с плавающей точкой, поэтому лучше выбирать double.

typedef double Real;
typedef struct BigIntTag {
  Real * Coef;
  long Size, SizeMax;
} BigInt;

#define BASE 10000.
#define invBASE 0.0001   //   B-1
#define NBDEC_BASE 4     //  Число знаков в B

2.   Основные операции.

2.1   Преобразование к каноническому виду

Переход от неканонического представления

X = x0 + x1 B + x2 B2 + ...    , где xi не обязательно между 0 и B-1
к каноническому, с 0 <= xi < B.

Начиная с i = 0, делим с остатком xi на B и осуществляем перенос на xi+1.

void UpdateBigInt(BigInt * A)
{
  long i;
  Real carry=0., x;

  for (i=0; i<A->Size; i++) {
    x = A->Coef[i] + carry;
    carry = floor(x*invBASE);
    A->Coef[i] = x-carry*BASE;
  }
  if (carry!=0) {
    while (carry!=0.) {
      x = carry;
      carry = floor(x*invBASE);
      A->Coef[i] = x-carry*BASE;
      i++;
      A->Size=i;
    }
    if (A->Size > A->SizeMax) {
      printf("Error in UpdateBigInt, Size > SizeMax\n");
    }
  }
  else {
    while (i>0 && A->Coef[i-1]==0.) i--;
    A->Size=i;
  }
}

2.2   Сумма и разность.

Cкладываем коэффициенты и осуществляем преобразование к каноническому виду при сложении, аналогично - разность.

/*
 *  C = A + B
 */
void AddBigInt(BigInt * A, BigInt * B, BigInt * C)
{
  long i;

  if (A->Size < B->Size) {
    AddBigInt(B,A,C);
    return;
  }
  /* We now have B->Size < A->Size */
  for (i=0; i < B->Size; i++)
    C->Coef[i] = A->Coef[i] + B->Coef[i];
  for (; i < A->Size; i++) 
    C->Coef[i] = A->Coef[i];

  C->Size = A->Size;
  UpdateBigInt(C);
}

2.3   Умножение на малое целое.

Пусть q - такое целое, что qB подходит под тип данных коэффициентов. Тогда произведение x на q получаем, умножая каждое xi на q и осуществляя перенос с i = 0 и далее.

2.4   Деление на малое целое.

Начиная с i = n, делим каждое xi на q и осуществляем перенос, умноженный на B в xi-1 и так далее.

2.5   Обычное перемножение двух длинных чисел.

Пусть даны 2 длинных числа в канонической форме:

Длинное число Z = X*Y может быть вычислено по формуле:

с последующим преобразованием.

Быстрое умножение длинных чисел методом Шенхаге-Штрассена (через преобразование Фурье)


Архив статей.

  • Generalized Arithmetic in C++
  • Метод представления длинных чисел, удобный для программирования. Безразлично, что представлено: десятичные или обычные дроби, челые числа, комплексные или полиномы.




Вверх по странице, к оглавлению и навигации