(Prog) Расчет FIR-фильтра через ДПФ над конечным полем

Материал из Модулярная арифметики
Версия от 15:26, 10 июля 2013; Turbo (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Содержание

Описание

Расчет FIR-фильтра (КИХ-фильтра) с использованием теоретико-числового преобразования (ДПФ над конечным полем) и системы остаточных классов. Для склейки ответа используется модулярный аналог Overlap-Add.

Исходник на C

#define _USE_MATH_DEFINES

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#define MAX_MOD_NUM 10
#define NL 1000
#define TRUE 1
#define FALSE 0
#define MAXPRIME 1000000

int prime[MAXPRIME];
int plist[MAXPRIME];
int pnum;

int find_bit(int p) {
	int i, k;
	k = 1;
	for (i = 0; i < 32; i++) {
		if (k > p)
			return i;
		k *= 2;
	}
	return -1;
}

__int64 powmod(__int64 a, __int64 k, __int64 n)
{
	__int64 b = 1;
	while (k) {
		if (k%2 == 0) {
			k /= 2;
			a = (a*a)%n;
		} else {
			k--;
			b = (b*a)%n;
		}
	}
	return b;
}

// Первообразный (примитивный) корень для простого числа $p степени $period

int find_primitive_root(int p, int period) {
	int i, val;
	int found_count;
	int proot;
	int pow_mod;
	int *checkarr;
	if ((p-1)%period != 0) {
		return -1;
	}
	checkarr = (int *)calloc(p, sizeof(int));
	found_count = (p-1)/period;
	for (proot = 1; proot < p; proot++) {
		pow_mod = powmod(proot, period, p);
		if (pow_mod == 1) {
			checkarr[1] = 1;
			for (i = 2; i < p; i++)
				checkarr[i] = 0;
			
			// Заполняем
			val = proot;
			for (i = 1; i < (p-1); i++) {
				checkarr[val]++;
				val = (val*proot)%p;
			}						
			
			// Проверяем
			for (i = 1; i < p; i++) {
				if (checkarr[i] != 0 && checkarr[i] != found_count) {
					break;
				}
			}
			if (i == p) {
				free(checkarr);
				return proot;
			}
		}
	}

	free(checkarr);
	return -1;
}

/*
	Теоретико числовое дискретное преобразование Фурье
*/

int dft_discrete
	(int len,
	int *x,
	int gamma,
	int mod)
{
	int i, j, pm;
	int *tmp;
	tmp = (int *)calloc(len, sizeof(int));

	for (i = 0; i < len; i++) {
		tmp[i] = 0;
		for (j = 0; j < len; j++) {
			pm = powmod(gamma, i*j, mod);
			tmp[i] += (x[j]*pm)%mod;
		}
		tmp[i] = tmp[i]%mod;
	}
	for (i = 0; i < len; i++) {
		x[i] = tmp[i];
	}

	free(tmp);
	return 0;
}

/* calculates a * *x + b * *y = gcd(a, b) = *d */
void extended_euclid(int a, int b, int *x, int *y, int *d) {
  int q, r, x1, x2, y1, y2;
  if (b == 0) {
    *d = a, *x = 1, *y = 0;
    return;
  }
  x2 = 1, x1 = 0, y2 = 0, y1 = 1;
  while (b > 0) {
    q = a / b, r = a - q * b;
    *x = x2 - q * x1, *y = y2 - q * y1;
    a = b, b = r;
    x2 = x1, x1 = *x, y2 = y1, y1 = *y;
  }
  *d = a, *x = x2, *y = y2;
}

/* computes the inverse of a modulo n */
int inverse(int a, int n) {
  int d, x, y;
  extended_euclid(a, n, &x, &y, &d);
  if (d == 1) return x;
  return 0;
}

/*
	Инверсное теоретико числовое дискретное преобразование Фурье
*/

int inverse_dft_discrete
	(int len,
	int *x,
	int gamma,
	int mod)
{
	int i, j, pm, step;
	int lenInv;
	int *tmp;
	tmp = (int *)calloc(len, sizeof(int));

	lenInv = inverse(len, mod);
	while(lenInv < 0)
		lenInv += mod;
	if (lenInv == 0) {
		printf("Error!\n");
		return 0;
	}

	for (i = 0; i < len; i++) {
		tmp[i] = 0;
		for (j = 0; j < len; j++) {
			step = -i*j;
			while(step < 0)
				step += len;
			pm = powmod(gamma, step, mod);
			tmp[i] += (x[j]*pm)%mod;
		}
		tmp[i] = (lenInv*tmp[i])%mod;
	}
	for (i = 0; i < len; i++) {
		x[i] = tmp[i];
	}

	free(tmp);
	return 0;
}

/*
	Вычисляет линейную свертку двух последовательностей (примерно одинаковой длины) 
	используя теоретико числовое ДПФ
	Длина выхода N = (len1+len2-1)
*/

int calc_simple_conv_modular (
	int period,
	int mod,
	int *in1, 
	int len1, 
	int *in2, 
	int len2, 
	int *out)
{
	int i, N;
	int gamma;
	int *x;
	int *y;
	int *r;
	
	if (period < len1+len2-1) {
		printf("Incorrect modules set!\n");
		exit(0);
	}

	// N - длина результата линейной свертки
	N = period;
	gamma = find_primitive_root(mod, period);

	x = (int *)calloc(N, sizeof(int));
	y = (int *)calloc(N, sizeof(int));
	r = (int *)calloc(N, sizeof(int));

	for (i = 0; i < len1; i++) {
		x[i] = in1[i];
	}
	for (i = len1; i < N; i++) {
		x[i] = 0;
	}
	for (i = 0; i < len2; i++) {
		y[i] = in2[i];
	}
	for (i = len2; i < N; i++) {
		y[i] = 0;
	}

	dft_discrete(N, x, gamma, mod);
	dft_discrete(N, y, gamma, mod);

	for (i = 0; i < N; i++)
	{
		r[i] = (x[i]*y[i])%mod;
	}

	inverse_dft_discrete(N, r, gamma, mod);

	for (i = 0; i < len1+len2-1; i++) {
		out[i] = r[i];
	}
	for (i = len1+len2-1; i < N; i++) {
		if (r[i] != 0) {
			printf("Some error here!\n");
		}
	}

	free(x);
	free(y);
	free(r);
	return 0;
}

/*
	Простейший подход - действуем по определению свертки
*/

int calc_fir_filter_deafult(
	int *vec1,
	int len1,
	int *vec2,
	int len2,
	int *out) 
{
	int i, j;
	for (i = 0; i < len1+len2-1; i++) {
		out[i] = 0;
		for (j = (i-len2+1 > 0)?(i-len2+1):0; j <= i; j++) {
			if (j >= len1)
				break;
			out[i] += vec1[j]*vec2[i-j];
		}
	}
	return 0;
}

/*
	Обратное преобразование
*/

int backward_convert(int len, int *mod, int *vals) 
{
	int i;
	__int64 maxvalue = 1;
	__int64 backward[128];
	__int64 res = 0;
	__int64 a, b;

	for (i = 0; i < len; i++) {
		maxvalue *= mod[i];
	}

	for (i = 0; i < len; i++)
	{
		backward[i] = maxvalue/mod[i];
		backward[i] *= powmod(backward[i], mod[i]-2, mod[i]);
	}
	
	for (i = 0; i < len; i++)
	{
		a = (__int64)vals[i];
		b = backward[i];
		res += (a*b)%maxvalue;
	}
	return (int)(res%maxvalue);
}

int find_modular_basis(int period, int *mod, int &modLen, int maxVal) 
{
	int i, k;
	int max_basis_value = 1;
	int proot;
	int val;
		
	modLen = 0;
	for (k = 1; k < 100*period; k++)
	{
		val = k*period + 1;
		if (prime[val] == 1) {
			proot = find_primitive_root(val, period);
			if (proot == -1) {
				printf("Невозможно найти примитивный корень степени %d для простого числа %d!\n", period, val);
				exit(0);
			}
			max_basis_value *= val;
			mod[modLen] = val;
			modLen++;
			if (max_basis_value > maxVal)
				break;
		}
	}
	if (k == period)
		return 0;

	// Пробуем исключить самый маленький модуль
	if (max_basis_value/mod[0] > maxVal) {
		for (i = 0; i < modLen - 1; i++)
			mod[i] = mod[i+1];
		modLen--;
	}

	return 1;
}

/*
	Свертка через модулярные каналы
	Простой подход
*/

int calc_fir_filter_modular_simple(
	int dataBit,
	int *vec1,
	int len1,
	int *vec2,
	int len2,
	int *out) 
{
	int mod[MAX_MOD_NUM];
	int vals[MAX_MOD_NUM];
	int modLen;
	int i, j, k, N;
	int *x[MAX_MOD_NUM];
	int *y[MAX_MOD_NUM];
	int *r[MAX_MOD_NUM];
	int *rTmp[MAX_MOD_NUM];
	int maxValue;
	int maxBasisValue;
	int period;

	if (len1 > len2) {
		printf("Error: len1 > len2\n");
		exit(0);
	}

	// Заполняем набор простых чисел
	
	for (i = 1; i < MAXPRIME; i++)
		prime[i] = 1;

	for (i = 2; i < MAXPRIME; i++)
		for (j = 2; i*j < MAXPRIME; j++)
			prime[i*j] = 0;

	pnum = 0;
	for (i = 1; i < MAXPRIME; i++)
		if (prime[i] == 1) {
			plist[pnum] = i;
			pnum++;
		}

	printf("Filter: ");
	for (i = 0; i < len1; i++) {
		printf("%d ", vec1[i]);
	}
	printf("\n");

	// Максимальное значение для результата свертки
	maxValue = 0;
	for (i = 0; i < len1; i++) {
		maxValue += vec1[i];
	}
	maxValue *= (int)(pow(2.0, dataBit) - 0.5);
	printf("Maximum possible value for result: %d\n", maxValue);

	// Рассчитываем модульный базис
	i = find_bit(2*len1-1);
	period = 1 << i;
	while (!find_modular_basis(period, mod, modLen, maxValue)) {
		period *= 2;
	}

	printf("Required length of convolution: %d\n", period);
	printf("Modular basis:");
	maxBasisValue = 1;
	for (i = 0; i < modLen; i++) {
		printf(" %d", mod[i]);
		maxBasisValue *= mod[i];
	}
	printf("\n");

	printf("Dynamic range of basis: %d (~ %.2lf times larger than required)\n", maxBasisValue, (1.0*maxBasisValue/maxValue));
	
	N = len1+len2-1;
	for (i = 0; i < modLen; i++) {
		x[i] = (int *)calloc(len1, sizeof(double));
		y[i] = (int *)calloc(len1, sizeof(double));
		rTmp[i] = (int *)calloc(len1+len1-1, sizeof(double));
		r[i] = (int *)calloc(N, sizeof(double));
	}

	for (i = 0; i < len1; i++) {
		for (j = 0; j < modLen; j++)
			x[j][i] = vec1[i]%mod[j];
	}
	for (i = 0; i < N; i++) {
		for (j = 0; j < modLen; j++)
			r[j][i] = 0;
	}

	for (i = 0; i < len2; i += len1)
	{
		for (j = 0; j < len1; j++) {
			for (k = 0; k < modLen; k++)
				y[k][j] = vec2[i+j]%mod[k];
		}
		for (k = 0; k < modLen; k++) {
			calc_simple_conv_modular(period, mod[k], x[k], len1, y[k], len1, rTmp[k]);
		}
		for (j = 0; j < len1+len1-1; j++) {
			for (k = 0; k < modLen; k++) {
				r[k][i+j] = (r[k][i+j] + rTmp[k][j])%mod[k];
			}
		}
	}

	for (i = 0; i < N; i++) {
		for (j = 0; j < modLen; j++) {
			printf("r[%d][%d] = %d ", j, i, r[j][i]);
			vals[j] = r[j][i];
		}
		out[i] = backward_convert(modLen, mod, vals);
		for (j = 0; j < modLen; j++) {
			if (out[i]%mod[j] != r[j][i]) {
				printf("Error backward (%d%%%d != %d its equal to %d)!\n", out[i], mod[j], r[j][i], out[i]%mod[j]);
				exit(0);
			}
		}
		printf("FINALR = %d ", out[i]);
		printf("\n");
	}

	for (i = 0; i < modLen; i++) {
		free(x[i]);
		free(y[i]);
		free(rTmp[i]);
		free(r[i]);
	}
	return 0;
}


int main ()
{
	int i, z;
	double maxVal;

	int filter[NL];
	int filterLen;
	int testArray[100*NL];
	int arrayLen;
	int dataBit;
	int res_check[10*NL];
	int res_experimental[10*NL];

	freopen("input.txt", "r", stdin);
	
	scanf("%d", &z);
	while(z--) {
		scanf("%d %d %d", &dataBit, &filterLen, &arrayLen);
		for (i = 0; i < filterLen; i++) {
			scanf("%d", &(filter[i]));
		}
		for (i = 0; i < arrayLen; i++) {
			scanf("%d", &(testArray[i]));
		}
	}
	
	maxVal = pow(2.0, 2*dataBit)*(filterLen+arrayLen);
	printf("Max value: %.0lf\n", maxVal);

	calc_fir_filter_deafult(filter, filterLen, testArray, arrayLen, res_check);
	calc_fir_filter_modular_simple(dataBit, filter, filterLen, testArray, arrayLen, res_experimental);

	// Check correctness
	for (i = 0; i < filterLen+arrayLen-1; i++) {
		if (res_check[i] != res_experimental[i]) {
			printf("Some error at position %d\n", i);
			break;
		}
	}
	if (i == filterLen+arrayLen-1) {
		printf("Congratulations! No errors were found at calcualtions!\n");
	} else {
		printf("Some errors were found at calcualtions!\n");
	}

	return 0;
}

Входные/выходные данные

Формат входных/выходных данных для FIR-фильтра

Пример (входные данные)

1
4 4 8
3 0 1 2
4 3 5 1 5 6 3 2

Пример (выходные данные)

Max value: 3072
Filter: 3 0 1 2
Maximum possible value for result: 90
Required length of convolution: 8
Modular basis: 17 41
Dynamic range of basis: 697 (~ 7.74 times larger than required)
r[0][0] = 12 r[1][0] = 12 FINALR = 12
r[0][1] = 9 r[1][1] = 9 FINALR = 9
r[0][2] = 2 r[1][2] = 19 FINALR = 19
r[0][3] = 14 r[1][3] = 14 FINALR = 14
r[0][4] = 9 r[1][4] = 26 FINALR = 26
r[0][5] = 12 r[1][5] = 29 FINALR = 29
r[0][6] = 16 r[1][6] = 16 FINALR = 16
r[0][7] = 5 r[1][7] = 22 FINALR = 22
r[0][8] = 15 r[1][8] = 15 FINALR = 15
r[0][9] = 8 r[1][9] = 8 FINALR = 8
r[0][10] = 4 r[1][10] = 4 FINALR = 4
Congratulations! No errors were found at calcualtions!

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация