有限域FFT的多项式乘法 - 统一的第n个原始根的选择

时间:2012-01-14 15:54:40

标签: c# fft

作为学校作业,我应该在C#中创建一个程序,在有限域上使用FFT将两个多项式相乘。
我选择的有限域是Z p (所有操作模p,元素为{ 0,..., p - 1 })。我意识到 p 必须足够大,以便模数运算不会改变结果多项式中的因子。
对于小n(在相应的有限域中),很容易找到n th 的统一根。但是,我需要找到n = 2 20 。我几乎只需要这个,因为通过平方来计算所有较低2的幂。我试着编写一个计算它的简单程序(使用有限域Z c * 2 r 中有2个 r 统一根的事实+ 1 ),运行了很长时间但没完成。所以我尝试谷歌的东西,只找到一个原始n th 根的表格,对于n = 2 k ,对于k = 0..30,在字段Z 70383776563201 并使用它。当然,当我使用longint时,它会导致溢出,因此错误的答案。所以我开始使用 System.Numerics 命名空间中的 BigInteger 结构。我就是这样,拥有一个非常慢的正确算法:

private static List<BigInteger> FFT(List<BigInteger> input, BigInteger Omega)
            {
                if (input.Count == 1)
                {
                    return new List<BigInteger>() { input[0] };
                }
                else
                {
                    List<BigInteger> evenInput = new List<BigInteger>();
                    for (int i = 0; i < input.Count; i += 2)
                    {
                        evenInput.Add(input[i]);
                    }
                    List<BigInteger> oddInput = new List<BigInteger>();
                    for (int i = 1; i < input.Count; i += 2)
                    {
                        oddInput.Add(input[i]);
                    }
                    List<BigInteger> even = FFT(evenInput, (Omega * Omega));
                    List<BigInteger> odd = FFT(oddInput, (Omega * Omega));
                    BigInteger[] outputArr = new BigInteger[input.Count];
                    int count = 0;
                    for (int i = 0; i < input.Count / 2; i++)
                    {
                        outputArr[i] = (even[i] + BigInteger.Pow(Omega, i) * odd[i]);
                        outputArr[i + input.Count / 2] = (even[i] - BigInteger.Pow(Omega, i) * odd[i]);
                        count += 2;
                    }
                    List<BigInteger> output = new List<BigInteger>();
                    for (int i = 0; i < count; i++)
                    {
                        output.Add(outputArr[i] % finiteField);
                    }
                    return output;
                }
            }

我知道创建所有列表并没有帮助提高速度,但主要问题当然是BigInteger结构。(我尝试使用System.Numerics.Complex结构基本相同的代码,它应该尽可能快模数运算需要很长时间,所以我知道我必须回到longint。问题是找到原始的n th 统一的根。我不知道是否甚至可以使用longint的2 20 原始根,而不必担心溢出。如果不是,我可以使用longint,因此具有快速算法吗? 有没有一种方法可以更快地计算大n的原始根?也许有人知道有限域中预先计算的原始根的表格?或者我应该考虑使用其他有限域?这是我所知道的唯一一种。我一直在寻找相当长的时间,并没有遇到任何有用的东西。老师告诉我,这个区域有很好的记录,但似乎并非如此。

2 个答案:

答案 0 :(得分:3)

我还没有完全考虑过,但是当你切换到BigIntegers时,你似乎不应该看到那么大的差异 - 可能是10倍?你做数学模型2 ^ 20,所以你的数字应该保持很小。在我看来,这些是您的问题:Omega * Omega,尤其是even[i] + BigInteger.Pow(Omega, i) * odd[i]。这将使你的数字增长得比他们需要的大得多。

Biginteger.Pow在指数长度内具有指数运行时间。您正在进行模块化数学运算,因此您应该能够更频繁地减少模数finiteField:Omega * Omega % finiteFieldeven[i] + BigInteger.ModPow(Omega, i, finiteField) * odd[i]

答案 1 :(得分:2)

你可以在模2 n +1的环中工作,即0 ... 2 n 的数字。由于2 n == -1(mod 2 n +1)且-1的平方为1,因此2是基元(2 * n) th 这个环中团结的根源。

更重要的是,modulo operations mod 2 n +1很容易用shift,adds和subs实现。即使用bignums,这也很便宜。同样,乘以2的幂乘以自然只是移位(加上mod操作)。

这是Schönhage-Strassen长整数乘法算法的一部分。维基百科的文章是一个良好的开端。