使用有限域FFT的多项式乘法-单位的第n个原始根的选择

本文关键字:单位 选择 原始 FFT 多项式 | 更新日期: 2023-09-27 18:20:22

作为一项学校作业,我应该用C#创建一个程序,在有限域上使用FFT将两个多项式相乘
我选择的有限域是Zp(所有运算都模p,元素是{0,…,p-1})。我意识到p必须足够大,这样生成的多项式中的因子就不会因模运算而改变
对于小n(在相应的有限域中),找到单位的第n原始根是容易的。然而,我需要为n=220找到它。我实际上只需要这个,因为计算2的所有较低幂是通过平方来完成的。我试着写一个简单的程序来计算它(利用有限域Zc*2r+1中存在第2r个单位根的事实),运行了很长时间,但没有完成。所以我试着用谷歌搜索一些东西,只在Z70383776563201字段中找到了一个n=2k的基元nth根的表,并使用了它。当然,当我使用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。问题是找到单位的原始nth根。我不知道是否有可能使用longint的第220个单位的原始根,而不必担心溢出。如果不是,对于哪个n,我可以使用longint,从而有一个快速算法
有没有一种方法可以更快地计算大n的原始根?也许有人知道有限域中预先计算的原始根的表?或者我应该考虑使用其他有限域吗?这是我所知道的唯一一种。我搜索了很长时间,没有发现任何有用的东西。老师告诉我,这个领域有很好的记录,但事实似乎并非如此。

使用有限域FFT的多项式乘法-单位的第n个原始根的选择

我还没有完全考虑清楚,但当切换到BigIntegers-10x时,你似乎不应该看到那么大的区别?你的数学模型2^20,所以你的数字应该保持很小。在我看来,这些都是你的问题:Omega * Omega,尤其是even[i] + BigInteger.Pow(Omega, i) * odd[i]。这会让你的数字增长得比需要的要大得多。

大整数。Pow在指数长度上有一个指数运行时间。您正在进行模数学,因此应该能够更频繁地减少模有限字段:Omega * Omega % finiteFieldeven[i] + BigInteger.ModPow(Omega, i, finiteField) * odd[i]

您可以在模2n+1的环中工作,即0。。。包括2n。由于2n=-1(mod2n+1)并且-1的平方是1,因此2是该环中单位的基元(2*n)根。

更重要的是,模运算mod2n+1很容易用移位、加法和子来实现。即使是大数字也很便宜。同样,乘以2的幂自然只是移位(加上mod运算)。

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