Раздел «Алгоритмы».FastFourierTransformCPP:

Быстрое преобразование Фурье (код на C++)

Файл test.cpp содержит простую программу, которая считывает из потока ввода два неотрицательных целых числа произвольной длины и печатает их произведение. Произведение вычисляется за время O(N*log N), где N - длина результата. Это достигается за счет использования дискретного преобразования Фурье. Здесь реализовано дискретное преобразование Фурье в той форме, в которой оно описано в книге Кормена. Преобразование Фурье реализовано отдельно в файле dft.cpp

Файл dft.h

/*
 * Fast Fourier Transform (with complex numbers)
 *
 * Daniel Shved, MIPT, 2010.
 * danshved [at] gmail.com
 */

#ifndef __DFT_H__
#define __DFT_H__

#include <complex>
using namespace std;

typedef complex<double> comp;

// Gets the complex conjugate of every element 
void conjugate(comp *array, int size);

// Multiplies two vectors element by element
void multiply(comp *arr1, comp *arr2, comp *result, int size);

// Finds the convolution of two vectors
void convolution(comp *arr1, comp *arr2, comp *result, int size);

// Discrete fourier transform. size must be a power of 2
void fourier_transform(comp *array, int size);

// Inverse fourier transform. size must be a power of 2
void inverse_fourier_transform(comp *array, int size);

#endif

Файл dft.cpp

/*
 * Fast Fourier Transform (implementation)
 *
 * Daniel Shved, MIPT, 2010.
 * danshved [at] gmail.com
 */

#include "dft.h"
#include <math.h>
#include <complex>
#include <algorithm>
#include <stack>
using namespace std;

/*
 * "Butterfly" transform.
 */
inline void butterfly(comp &x, comp &y, comp w)
{
   comp p = x, q = y*w;
   x = p + q;
   y = p - q;
}

/*
 * Series of butterfly transforms required by the FFT algorithm.
 */
inline void mass_butterfly(comp *array, int size, comp w)
{
   comp power(1.0, 0.0);
   int n = size/2;
   
   for(int i = 0; i < n; i++) {
      butterfly(array[i], array[i+n], power);
      power *= w;
   }
}

/*
 * Given a number ``x'' returns the number which has the same bits as ``x'',
 * but in the reverse order
 */
inline unsigned int backwards(unsigned int x, int length)
{
   unsigned int result = 0;
   unsigned int bit = 1u;
   unsigned int reverse = 1u<<(length-1);
   for(int i = 0; i < length && x != 0; i++) {
      if(x & bit) {
         result |= reverse;
         x &= ~bit;
      }
      bit <<= 1;
      reverse >>= 1;
   }
   return result;
}

/*
 * Moves elements of the array as required by the iterative FFT implementation.
 * ``size'' must be a power of 2.
 */
static void reposition(comp *array, int size)
{
   // Determine the bit length
   int length = 0;
   while(1u << length < (unsigned int)size)
      length++;

   // Swap elements at positions k and reverse(k)
   for(int i = 0; i < size; i++) {
      int j = backwards(i, length);
      if(i <= j)
         swap(array[i], array[j]);
   }
}

/*
 * Does the Discrete Fourier Transform.  Takes time O(size * log(size)).
 * ``size'' must be a power of 2.
 */
void fourier_transform(comp *array, int size)
{
   // Arrange numbers in a convenient order
   reposition(array, size);

   // Prepare roots of unity for every step
   int step;
   comp root = exp(comp(0.0, 2.0*M_PI/size));
   stack<comp> roots;
   for(step=size; step != 1; step /= 2) {
      roots.push(root);
      root *= root;
   }

   // Do lots of butterfly transforms
   for(step = 2; step <= size; step *= 2) {
      root = roots.top();
      roots.pop();
      for(int i = 0; i < size; i += step)   
         mass_butterfly(array + i, step, root);
   }
}

/*
 * The inverse DFT.
 */
void inverse_fourier_transform(comp *array, int size)
{
   conjugate(array, size);
   fourier_transform(array, size);
   conjugate(array, size);
   for(int i = 0; i < size; i++)
      array[i] = array[i] / (double)size;
}

/*
 * Replaces every element of the vector by its complex conjugate.
 */
void conjugate(comp *array, int size)
{
   for(int i = 0; i < size; i++)
      array[i] = conj(array[i]);
}

/*
 * Multiplies two vectors element by element.
 */
void multiply(comp *arr1, comp *arr2, comp *result, int size)
{
   for(int i = 0; i < size; i++)
      result[i] = arr1[i] * arr2[i];
}

/*
 * Finds the convolution of two vectors (the product of two polynomials, given
 * that the result has power less than ``size'').  ``size'' must be a power of
 * 2.
 */
void convolution(comp *arr1, comp *arr2, comp *result, int size)
{
   fourier_transform(arr1, size);
   fourier_transform(arr2, size);
   multiply(arr1, arr2, result, size);
   inverse_fourier_transform(result, size);
}

Файл test.cpp

/*
 * Long multiplication with the FFT.
 *
 * Daniel Shved, MIPT, 2010.
 * danshved [at] gmail.com
 */
#include "dft.h"
#include <string>
#include <iostream>
using namespace std;

const int base = 10;

/*
 * Turns a string with a number into an array of complex numbers (digits)
 */
comp *make_polynomial(const string &str, int length)
{
   comp *result = new comp[length];
   for(int i = 0; i < str.length(); i++)
      result[i] = comp((double)(str[str.length() - 1 - i] - '0'), 0.0);
   for(int i = str.length(); i < length; i++)
      result[i] = comp(0.0, 0.0);
   return result;
}

/*
 * This is used to turn complex numbers which are known to be almost real
 * integers into integers.
 */
int round(comp x)
{
   return (int)(x.real()+0.5);
}

/*
 * Given a polynomial ``a'', returns the string with number a(10).
 */
string make_number(comp *a, int n)
{
   // Turn coefficients into digits (carry)
   for(int i = 0; i < n - 1; i++) {
      a[i+1] += round(a[i]) / base;
      a[i] = round(a[i]) % base;
   }

   // Determine length
   int realLength = 0;
   for(int i = 0; i < n; i++)
      if(round(a[i]) != 0)
         realLength = i+1;
   if(realLength == 0)
      return string("0");
   
   // Make a string
   string result(realLength, '0');
   for(int i = 0; i < realLength; i++)
      result[realLength - 1 - i] = '0' + (char)round(a[i]);
   return result;
}

/*
 * Long multiplication.
 */
string multiply(string number1, string number2)
{
   // Determine the size of polynomials
   int deg = number1.length() + number2.length();
   int n = 1;
   while(n<=deg) n*= 2;

   // Turn numbers into complex polynomials
   comp *a = make_polynomial(number1, n);
   comp *b = make_polynomial(number2, n);

   // Multiply polynomials with the FFT
   comp *product = new comp[n];
   convolution(a, b, product, n);

   // Turn the polynomial back into a number
   string result = make_number(product, n);

   // Clean up and exit
   delete a;
   delete b;
   delete product;
   return result;
}

/*
 * A test. Reads two non-negative integers from the input and prints their
 * product.
 */
int main()
{
   string a, b, res;
   cin >> a >> b;
   res = multiply(a, b);
   cout << res << endl;
   return 0;
}

-- DanielShved - 24 Apr 2010

AlgorithmClasifyForm
Type: Код
Scope: Математика
Strategy:  
Language: C++
Complexity: Medium