/// <summary> /// 畳み込みを mod <typeparamref name="TMod"/> で計算します。 /// </summary> /// <remarks> /// <para><paramref name="a"/>, <paramref name="b"/> の少なくとも一方が空の場合は空配列を返します。</para> /// <para>制約:</para> /// <para>- 2≤<typeparamref name="TMod"/>≤2×10^9</para> /// <para>- <typeparamref name="TMod"/> は素数</para> /// <para>- 2^c | (<typeparamref name="TMod"/> - 1) かつ |<paramref name="a"/>| + |<paramref name="b"/>| - 1 ≤ 2^c なる c が存在する</para> /// <para>計算量: O((|<paramref name="a"/>|+|<paramref name="b"/>|)log(|<paramref name="a"/>|+|<paramref name="b"/>|) + log<typeparamref name="TMod"/>)</para> /// </remarks> public static Int32[] Convolution <TMod>(ReadOnlySpan <Int32> a, ReadOnlySpan <Int32> b) where TMod : struct, IStaticMod { var n = a.Length; var m = b.Length; if (n == 0 || m == 0) { return(Array.Empty <Int32>()); } var a2 = new StaticModInt <TMod> [n]; var b2 = new StaticModInt <TMod> [m]; for (int i = 0; i < a2.Length; i++) { a2[i] = new StaticModInt <TMod>(a[i]); } for (int i = 0; i < b2.Length; i++) { b2[i] = new StaticModInt <TMod>(b[i]); } var c2 = Convolution(a2, b2); var c = new Int32[n + m - 1]; for (int i = 0; i < c.Length; i++) { c[i] = (Int32)c2[i].Value; } return(c); }
/// <summary> /// 畳み込みを mod <typeparamref name="TMod"/> で計算します。 /// </summary> /// <remarks> /// <para><paramref name="a"/>, <paramref name="b"/> の少なくとも一方が空の場合は空配列を返します。</para> /// <para>制約:</para> /// <para>- 2≤<typeparamref name="TMod"/>≤2×10^9</para> /// <para>- <typeparamref name="TMod"/> は素数</para> /// <para>- 2^c | (<typeparamref name="TMod"/> - 1) かつ |<paramref name="a"/>| + |<paramref name="b"/>| - 1 ≤ 2^c なる c が存在する</para> /// <para>計算量: O((|<paramref name="a"/>|+|<paramref name="b"/>|)log(|<paramref name="a"/>|+|<paramref name="b"/>|) + log<typeparamref name="TMod"/>)</para> /// </remarks> public static Span <StaticModInt <TMod> > Convolution <TMod>(ReadOnlySpan <StaticModInt <TMod> > a, ReadOnlySpan <StaticModInt <TMod> > b) where TMod : struct, IStaticMod { var n = a.Length; var m = b.Length; if (n == 0 || m == 0) { return(Array.Empty <StaticModInt <TMod> >()); } if (System.Math.Min(n, m) <= 60) { return(ConvolutionNaive(a, b)); } int z = 1 << Internal.InternalMath.CeilPow2(n + m - 1); var aTemp = new StaticModInt <TMod> [z]; a.CopyTo(aTemp); var bTemp = new StaticModInt <TMod> [z]; b.CopyTo(bTemp); return(Convolution(aTemp.AsSpan(), bTemp.AsSpan(), n, m, z)); }
static StaticModInt <T>[] ConvNative <T>(StaticModInt <T>[] a, StaticModInt <T>[] b) where T : struct, IStaticMod { int n = a.Length, m = b.Length; var c = new StaticModInt <T> [n + m - 1]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { c[i + j] += a[i] * b[j]; } } return(c); }
public void Mid() { var mt = MTRandom.Create(); int n = 1234, m = 2345; var a = new StaticModInt <Mod998244353> [n]; var b = new StaticModInt <Mod998244353> [m]; for (int i = 0; i < n; i++) { a[i] = mt.NextUInt(); } for (int i = 0; i < m; i++) { b[i] = mt.NextUInt(); } MathLib.Convolution(a, b).Should().Equal(ConvNative(a, b)); }
public void MemoryStatic() { var mt = MTRandom.Create(); for (int n = 0; n < 100; n++) { var arr = new StaticModInt <MemoryID> [n]; var expected = new uint[n]; for (int i = 0; i < n; i++) { var v = mt.NextUInt(); arr[i] = v; expected[i] = v % 101; } MemoryMarshal.Cast <StaticModInt <MemoryID>, uint>(arr).ToArray() .Should().Equal(expected); } }
/// <summary> /// 畳み込みを mod <typeparamref name="TMod"/> で計算します。 /// </summary> /// <remarks> /// <para><paramref name="a"/>, <paramref name="b"/> の少なくとも一方が空の場合は空配列を返します。</para> /// <para>制約:</para> /// <para>- 2≤<typeparamref name="TMod"/>≤2×10^9</para> /// <para>- <typeparamref name="TMod"/> は素数</para> /// <para>- 2^c | (<typeparamref name="TMod"/> - 1) かつ |<paramref name="a"/>| + |<paramref name="b"/>| - 1 ≤ 2^c なる c が存在する</para> /// <para>計算量: O((|<paramref name="a"/>|+|<paramref name="b"/>|)log(|<paramref name="a"/>|+|<paramref name="b"/>|) + log<typeparamref name="TMod"/>)</para> /// </remarks> public static ulong[] Convolution <TMod>(ulong[] a, ulong[] b) where TMod : struct, IStaticMod { var n = a.Length; var m = b.Length; if (n == 0 || m == 0) { return(Array.Empty <ulong>()); } if (System.Math.Min(n, m) <= 60) { var c = ConvolutionNaive <TMod>(a.Select(TakeMod).ToArray(), b.Select(TakeMod).ToArray()); return(c.Select(ci => (ulong)ci.Value).ToArray()); } else { int z = 1 << Internal.InternalMath.CeilPow2(n + m - 1); var aTemp = new StaticModInt <TMod> [z]; for (int i = 0; i < a.Length; i++) { aTemp[i] = TakeMod(a[i]); } var bTemp = new StaticModInt <TMod> [z]; for (int i = 0; i < b.Length; i++) { bTemp[i] = TakeMod(b[i]); } var c = Convolution <TMod>(aTemp, bTemp, n, m, z)[0..(n + m - 1)]; var result = new ulong[c.Length]; for (int i = 0; i < result.Length; i++) { result[i] = (ulong)c[i].Value; } return(result); } StaticModInt <TMod> TakeMod(ulong x) => StaticModInt <TMod> .Raw((int)(x % default(TMod).Mod)); }
public void SimpleSMod() { var mt = MTRandom.Create(); for (int n = 1; n < 20; n++) { for (int m = 1; m < 20; m++) { var a = new StaticModInt <Mod998244353> [n]; var b = new StaticModInt <Mod998244353> [m]; for (int i = 0; i < n; i++) { a[i] = mt.NextUInt(); } for (int i = 0; i < m; i++) { b[i] = mt.NextUInt(); } MathLib.Convolution(a, b).Should().Equal(ConvNative(a, b)); } } for (int n = 1; n < 20; n++) { for (int m = 1; m < 20; m++) { var a = new StaticModInt <Mod924844033> [n]; var b = new StaticModInt <Mod924844033> [m]; for (int i = 0; i < n; i++) { a[i] = mt.NextUInt(); } for (int i = 0; i < m; i++) { b[i] = mt.NextUInt(); } MathLib.Convolution(a, b).Should().Equal(ConvNative(a, b)); } } }
/// <summary> /// 畳み込みを mod <typeparamref name="TMod"/> で計算します。 /// </summary> /// <remarks> /// <para><paramref name="a"/>, <paramref name="b"/> の少なくとも一方が空の場合は空配列を返します。</para> /// <para>制約:</para> /// <para>- 2≤<typeparamref name="TMod"/>≤2×10^9</para> /// <para>- <typeparamref name="TMod"/> は素数</para> /// <para>- 2^c | (<typeparamref name="TMod"/> - 1) かつ |<paramref name="a"/>| + |<paramref name="b"/>| - 1 ≤ 2^c なる c が存在する</para> /// <para>計算量: O((|<paramref name="a"/>|+|<paramref name="b"/>|)log(|<paramref name="a"/>|+|<paramref name="b"/>|) + log<typeparamref name="TMod"/>)</para> /// </remarks> public static int[] Convolution <TMod>(int[] a, int[] b) where TMod : struct, IStaticMod { var n = a.Length; var m = b.Length; if (n == 0 || m == 0) { return(Array.Empty <int>()); } if (System.Math.Min(n, m) <= 60) { var c = ConvolutionNaive <TMod>(a.Select(ai => new StaticModInt <TMod>(ai)).ToArray(), b.Select(bi => new StaticModInt <TMod>(bi)).ToArray()); return(c.Select(ci => ci.Value).ToArray()); } else { int z = 1 << Internal.InternalMath.CeilPow2(n + m - 1); var aTemp = new StaticModInt <TMod> [z]; for (int i = 0; i < a.Length; i++) { aTemp[i] = new StaticModInt <TMod>(a[i]); } var bTemp = new StaticModInt <TMod> [z]; for (int i = 0; i < b.Length; i++) { bTemp[i] = new StaticModInt <TMod>(b[i]); } var c = Convolution <TMod>(aTemp, bTemp, n, m, z)[0..(n + m - 1)]; var result = new int[c.Length]; for (int i = 0; i < result.Length; i++) { result[i] = c[i].Value; } return(result); } }
private static StaticModInt <TMod>[] ConvolutionNaive <TMod>(ReadOnlySpan <StaticModInt <TMod> > a, ReadOnlySpan <StaticModInt <TMod> > b) where TMod : struct, IStaticMod { if (a.Length < b.Length) { #pragma warning disable IDE0180 // ref 構造体のため型引数として使えない var temp = a; a = b; b = temp; #pragma warning restore IDE0180 } var ans = new StaticModInt <TMod> [a.Length + b.Length - 1]; for (int i = 0; i < a.Length; i++) { for (int j = 0; j < b.Length; j++) { ans[i + j] += a[i] * b[j]; } } return(ans); }
private static StaticModInt <TMod>[] ConvolutionFFT <TMod>(ReadOnlySpan <StaticModInt <TMod> > a, ReadOnlySpan <StaticModInt <TMod> > b) where TMod : struct, IStaticMod { int n = a.Length, m = b.Length; int z = 1 << InternalBit.CeilPow2(n + m - 1); var a2 = new StaticModInt <TMod> [z]; var b2 = new StaticModInt <TMod> [z]; a.CopyTo(a2); b.CopyTo(b2); var result = ConvolutionFFTInner(a2, b2); Array.Resize(ref result, n + m - 1); var iz = new StaticModInt <TMod>(z).Inv(); for (int i = 0; i < result.Length; i++) { result[i] *= iz; } return(result); }
public StaticModInt <T> Divide(StaticModInt <T> x, StaticModInt <T> y) => x / y;
public void Inv() { for (int i = 1; i < 10; i++) { int x = new StaticModInt <InvID11>(i).Inv().Value; ((long)x * i % 11).Should().Be(1); } for (int i = 1; i < 12; i++) { if (Gcd(i, 12) != 1) { continue; } int x = new StaticModInt <InvID12>(i).Inv().Value; ((long)x * i % 12).Should().Be(1); } for (int i = 1; i < 100000; i++) { int x = new StaticModInt <InvID1000000007>(i).Inv().Value; ((long)x * i % 1000000007).Should().Be(1); } for (int i = 1; i < 100000; i++) { if (Gcd(i, 1000000008) != 1) { continue; } int x = new StaticModInt <InvID1000000008>(i).Inv().Value; ((long)x * i % 1000000008).Should().Be(1); } for (int i = 1; i < 100000; i++) { int x = new StaticModInt <InvID998244353>(i).Inv().Value; ((long)x * i % 998244353).Should().Be(1); } DynamicModInt <InvID998244353> .Mod = 998244353; for (int i = 1; i < 100000; i++) { int x = new DynamicModInt <InvID998244353>(i).Inv().Value; x.Should().BeGreaterOrEqualTo(0); x.Should().BeLessOrEqualTo(998244353 - 1); ((long)x * i % 998244353).Should().Be(1); } DynamicModInt <InvID1000000008> .Mod = 1000000008; for (int i = 1; i < 100000; i++) { if (Gcd(i, 1000000008) != 1) { continue; } int x = new DynamicModInt <InvID1000000008>(i).Inv().Value; ((long)x * i % 1000000008).Should().Be(1); } }
public StaticModInt <T> Subtract(StaticModInt <T> x, StaticModInt <T> y) => x - y;
public StaticModInt <T> Add(StaticModInt <T> x, StaticModInt <T> y) => x + y;
public StaticModInt <T> Decrement(StaticModInt <T> x) => -- x;
public StaticModInt <T> Increment(StaticModInt <T> x) => ++ x;
public StaticModInt <T> Minus(StaticModInt <T> x) => - x;
StaticModInt <T> IDivisionOperator <StaticModInt <T> > .Modulo(StaticModInt <T> x, StaticModInt <T> y) => throw new NotSupportedException();
public StaticModInt <T> Multiply(StaticModInt <T> x, StaticModInt <T> y) => x * y;