As a school assignment, I have to create a C # program that multiplies two polynomials using FFT over a finite field.
My final selection field will be Z p (all operations modulo p, elements: { 0, ..., p - 1 }). I realized that p must be large enough so that the factors in the resulting polynomial do not change modulo.
Finding the primitive root n th of unity is easy for small n (in the corresponding finite field). However, I needed to find it for n = 2 20 . I practically needed only this, calculating it for all lower degrees 2, making a square. I tried to write a simple program that would calculate it (using the fact that in the final field 2 r th is the root of unity in the final field Z c * 2 r + 1 ), I missed it for a rather long time and did not finish it. So I tried something on Google and found a table of primitive n th for n = 2 k for k = 0..30 in the field Z 70383776563201 and used it. Of course, when I used longint, this led to overflow and therefore incorrect answers. So I started using the BigInteger structure from the System.Numerics namespace. This is where I am, having the right algorithm, which is very slow:
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; } }
I know that creating all lists does not help speed, but the BigInteger structure, of course, is the main problem. (I tried basically the same code with the System.Numerics.Complex structure, and it was as fast as it should be) The modulo operation is very time consuming, so I know that I need to go back to longint. The problem is finding the primitive root n th of unity. I don't know if it is even possible to use a primitive root of 2 20 out of one with longint without worrying about overflow. If not, why can n be used with longint and therefore have a fast algorithm?
Is there a way to compute primitive roots for large n faster? Maybe someone knows a table of precomputed primitive roots in finite fields? Or should I use other trailing fields? This is the only species that I know of. I searched for quite a while and did not find anything useful. Teacher told me that this area is well documented, but it seems that this is not so.
Juraj source share