How to resolve the algorithm Cyclotomic polynomial step by step in the C# programming language
How to resolve the algorithm Cyclotomic polynomial step by step in the C# programming language
Table of Contents
Problem Statement
The nth Cyclotomic polynomial, for any positive integer n, is the unique irreducible polynomial of largest degree with integer coefficients that is a divisor of x^n − 1, and is not a divisor of x^k − 1 for any k < n.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Cyclotomic polynomial step by step in the C# programming language
The CyclotomicPolynomial
class in C# provides functionality to work with cyclotomic polynomials, which are polynomials that have factors of the form x^n - 1
, where n
is a positive integer.
The main methods in this class are:
GetCyclotomicPolynomial(int n)
: This method takes an integern
as input and returns the cyclotomic polynomial for that integer. The cyclotomic polynomial forn
is denoted asCP[n]
.Main2()
: This method is the entry point of the class and serves as a test driver for theGetCyclotomicPolynomial
method. It prints out the cyclotomic polynomials forn <= 30
and the smallest cyclotomic polynomial withn
or-n
as a coefficient forn <= 10
.
The GetCyclotomicPolynomial
method uses a combination of algorithms to calculate the cyclotomic polynomial. It first checks if the result is already cached in the polyCache
dictionary. If it is, the cached result is returned. Otherwise, it proceeds to calculate the polynomial based on the following algorithm:
- If
n
is prime, it returnsx^n - 1
. - If
n
is of the form2p
, it returns a polynomial with terms(-1)^i * x^i
fori
ranging from0
ton/2 - 1
. - If
n
is of the form2^h
, it returnsx^(1<<(h-1)) + 1
. - If
n
is of the formp^k
, it returns a polynomial with termsx^(i * p^(k-1))
fori
ranging from0
top - 1
. - If
n
is of the form2^h * p^k
, it returns a polynomial with terms(-1)^i * x^(i * 2^(h-1) * p^(k-1))
fori
ranging from0
top - 1
. - If
n
is odd and greater than1
, it returnsCP[-n][x]
.
If none of these conditions are met, the method falls back to one of three different algorithms to calculate the cyclotomic polynomial. These algorithms are not specified in detail in the code, but they are referred to as Algorithm 0
, Algorithm 1
, and Algorithm 2
.
The GetFactors
method takes an integer n
and returns a dictionary that contains the prime factors of n
. The GetDivisors
method takes an integer n
and returns an enumerable collection of all the divisors of n
.
The Polynomial
class represents a polynomial as a collection of terms. Each term consists of a coefficient and an exponent. The class provides methods for adding, subtracting, multiplying, and dividing polynomials. It also provides a method for simplifying the polynomial by combining like terms.
The Term
struct represents a single term in a polynomial. It consists of a coefficient and an exponent. The Term
struct provides methods for adding, subtracting, and multiplying terms. It also provides a method for checking if a term is zero.
Here's a breakdown of the code:
- Main2 Function:
- Prints the cyclotomic polynomials for
n <= 30
and the smallest cyclotomic polynomial withn
or-n
as a coefficient forn <= 10
.
- Prints the cyclotomic polynomials for
- GetCyclotomicPolynomial Function:
- Checks if the cyclotomic polynomial for
n
is already cached inpolyCache
. - If not cached, calculates the cyclotomic polynomial using a combination of algorithms depending on the factorization of
n
.
- Checks if the cyclotomic polynomial for
- GetFactors Function:
- Finds the prime factors of
n
and returns them in a dictionary.
- Finds the prime factors of
- GetDivisors Function:
- Returns an enumerable collection of all the divisors of
n
.
- Returns an enumerable collection of all the divisors of
- Polynomial Class:
- Represents a polynomial as a collection of
Term
s. - Provides methods for adding, subtracting, multiplying, and dividing polynomials.
- Represents a polynomial as a collection of
- Term Struct:
- Represents a single term in a polynomial with a coefficient and an exponent.
- Provides methods for adding, subtracting, and multiplying terms.
Source code in the csharp programming language
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using IntMap = System.Collections.Generic.Dictionary<int, int>;
public static class CyclotomicPolynomial
{
public static void Main2() {
Console.WriteLine("Task 1: Cyclotomic polynomials for n <= 30:");
for (int i = 1; i <= 30; i++) {
var p = GetCyclotomicPolynomial(i);
Console.WriteLine($"CP[{i}] = {p.ToString()}");
}
Console.WriteLine();
Console.WriteLine("Task 2: Smallest cyclotomic polynomial with n or -n as a coefficient:");
for (int i = 1, n = 0; i <= 10; i++) {
while (true) {
n++;
var p = GetCyclotomicPolynomial(n);
if (p.Any(t => Math.Abs(t.Coefficient) == i)) {
Console.WriteLine($"CP[{n}] has coefficient with magnitude = {i}");
n--;
break;
}
}
}
}
private const int MaxFactors = 100_000;
private const int Algorithm = 2;
private static readonly Term x = new Term(1, 1);
private static readonly Dictionary<int, Polynomial> polyCache =
new Dictionary<int, Polynomial> { [1] = x - 1 };
private static readonly Dictionary<int, IntMap> factorCache =
new Dictionary<int, IntMap> { [2] = new IntMap { [2] = 1 } };
private static Polynomial GetCyclotomicPolynomial(in int n) {
if (polyCache.TryGetValue(n, out var result)) return result;
var factors = GetFactors(n);
if (factors.ContainsKey(n)) { //n is prime
result = new Polynomial(from exp in ..n select x[exp]);
} else if (factors.Count == 2 && factors.Contains(2, 1) && factors.Contains(n/2, 1)) { //n = 2p
result = new Polynomial(from i in ..(n/2) select (IsOdd(i) ? -x : x)[i]);
} else if (factors.Count == 1 && factors.TryGetValue(2, out int h)) { //n = 2^h
result = x[1<<(h-1)] + 1;
} else if (factors.Count == 1 && !factors.ContainsKey(n)) { // n = p^k
(int p, int k) = factors.First();
result = new Polynomial(from i in ..p select x[i * (int)Math.Pow(p, k-1)]);
} else if (factors.Count == 2 && factors.ContainsKey(2)) { //n = 2^h * p^k
(int p, int k) = factors.First(entry => entry.Key != 2);
int twoExp = 1 << (factors[2] - 1);
result = new Polynomial(from i in ..p select (IsOdd(i) ? -x : x)[i * twoExp * (int)Math.Pow(p, k-1)]);
} else if (factors.ContainsKey(2) && IsOdd(n/2) && n/2 > 1) { // CP(2m)[x] = CP[-m][x], n is odd > 1
Polynomial cycloDiv2 = GetCyclotomicPolynomial(n/2);
result = new Polynomial(from term in cycloDiv2 select IsOdd(term.Exponent) ? -term : term);
#pragma warning disable CS0162
} else if (Algorithm == 0) {
var divisors = GetDivisors(n);
result = x[n] - 1;
foreach (int d in divisors) result /= GetCyclotomicPolynomial(d);
} else if (Algorithm == 1) {
var divisors = GetDivisors(n).ToList();
int maxDivisor = divisors.Max();
result = (x[n] - 1) / (x[maxDivisor] - 1);
foreach (int d in divisors.Where(div => maxDivisor % div == 0)) {
result /= GetCyclotomicPolynomial(d);
}
} else if (Algorithm == 2) {
int m = 1;
result = GetCyclotomicPolynomial(m);
var primes = factors.Keys.ToList();
primes.Sort();
foreach (int prime in primes) {
var cycloM = result;
result = new Polynomial(from term in cycloM select term.Coefficient * x[term.Exponent * prime]);
result /= cycloM;
m *= prime;
}
int s = n / m;
result = new Polynomial(from term in result select term.Coefficient * x[term.Exponent * s]);
#pragma warning restore CS0162
} else {
throw new InvalidOperationException("Invalid algorithm");
}
polyCache[n] = result;
return result;
}
private static bool IsOdd(int i) => (i & 1) != 0;
private static bool Contains(this IntMap map, int key, int value) => map.TryGetValue(key, out int v) && v == value;
private static int GetOrZero(this IntMap map, int key) => map.TryGetValue(key, out int v) ? v : 0;
private static IEnumerable<T> Select<T>(this Range r, Func<int, T> f) => Enumerable.Range(r.Start.Value, r.End.Value - r.Start.Value).Select(f);
private static IntMap GetFactors(in int n) {
if (factorCache.TryGetValue(n, out var factors)) return factors;
factors = new IntMap();
if (!IsOdd(n)) {
foreach (var entry in GetFactors(n/2)) factors.Add(entry.Key, entry.Value);
factors[2] = factors.GetOrZero(2) + 1;
return Cache(n, factors);
}
for (int i = 3; i * i <= n; i+=2) {
if (n % i == 0) {
foreach (var entry in GetFactors(n/i)) factors.Add(entry.Key, entry.Value);
factors[i] = factors.GetOrZero(i) + 1;
return Cache(n, factors);
}
}
factors[n] = 1;
return Cache(n, factors);
}
private static IntMap Cache(int n, IntMap factors) {
if (n < MaxFactors) factorCache[n] = factors;
return factors;
}
private static IEnumerable<int> GetDivisors(int n) {
for (int i = 1; i * i <= n; i++) {
if (n % i == 0) {
yield return i;
int div = n / i;
if (div != i && div != n) yield return div;
}
}
}
public sealed class Polynomial : IEnumerable<Term>
{
public Polynomial() { }
public Polynomial(params Term[] terms) : this(terms.AsEnumerable()) { }
public Polynomial(IEnumerable<Term> terms) {
Terms.AddRange(terms);
Simplify();
}
private List<Term>? terms;
private List<Term> Terms => terms ??= new List<Term>();
public int Count => terms?.Count ?? 0;
public int Degree => Count == 0 ? -1 : Terms[0].Exponent;
public int LeadingCoefficient => Count == 0 ? 0 : Terms[0].Coefficient;
public IEnumerator<Term> GetEnumerator() => Terms.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public override string ToString() => Count == 0 ? "0" : string.Join(" + ", Terms).Replace("+ -", "- ");
public static Polynomial operator *(Polynomial p, Term t) => new Polynomial(from s in p select s * t);
public static Polynomial operator +(Polynomial p, Polynomial q) => new Polynomial(p.Terms.Concat(q.Terms));
public static Polynomial operator -(Polynomial p, Polynomial q) => new Polynomial(p.Terms.Concat(q.Terms.Select(t => -t)));
public static Polynomial operator *(Polynomial p, Polynomial q) => new Polynomial(from s in p from t in q select s * t);
public static Polynomial operator /(Polynomial p, Polynomial q) => p.Divide(q).quotient;
public (Polynomial quotient, Polynomial remainder) Divide(Polynomial divisor) {
if (Degree < 0) return (new Polynomial(), this);
Polynomial quotient = new Polynomial();
Polynomial remainder = this;
int lcv = divisor.LeadingCoefficient;
int dv = divisor.Degree;
while (remainder.Degree >= divisor.Degree) {
int lcr = remainder.LeadingCoefficient;
Term div = new Term(lcr / lcv, remainder.Degree - dv);
quotient.Terms.Add(div);
remainder += divisor * -div;
}
quotient.Simplify();
remainder.Simplify();
return (quotient, remainder);
}
private void Simplify() {
if (Count < 2) return;
Terms.Sort((a, b) => -a.CompareTo(b));
for (int i = Terms.Count - 1; i > 0; i--) {
Term s = Terms[i-1];
Term t = Terms[i];
if (t.Exponent == s.Exponent) {
Terms[i-1] = new Term(s.Coefficient + t.Coefficient, s.Exponent);
Terms.RemoveAt(i);
}
}
Terms.RemoveAll(t => t.IsZero);
}
}
public readonly struct Term : IEquatable<Term>, IComparable<Term>
{
public Term(int coefficient, int exponent = 0) => (Coefficient, Exponent) = (coefficient, exponent);
public Term this[int exponent] => new Term(Coefficient, exponent); //Using x[exp] because x^exp has low precedence
public int Coefficient { get; }
public int Exponent { get; }
public bool IsZero => Coefficient == 0;
public static Polynomial operator +(Term left, Term right) => new Polynomial(left, right);
public static Polynomial operator -(Term left, Term right) => new Polynomial(left, -right);
public static implicit operator Term(int coefficient) => new Term(coefficient);
public static Term operator -(Term t) => new Term(-t.Coefficient, t.Exponent);
public static Term operator *(Term left, Term right) => new Term(left.Coefficient * right.Coefficient, left.Exponent + right.Exponent);
public static bool operator ==(Term left, Term right) => left.Equals(right);
public static bool operator !=(Term left, Term right) => !left.Equals(right);
public static bool operator <(Term left, Term right) => left.CompareTo(right) < 0;
public static bool operator >(Term left, Term right) => left.CompareTo(right) > 0;
public static bool operator <=(Term left, Term right) => left.CompareTo(right) <= 0;
public static bool operator >=(Term left, Term right) => left.CompareTo(right) >= 0;
public bool Equals(Term other) => Exponent == other.Exponent && Coefficient == other.Coefficient;
public override bool Equals(object? obj) => obj is Term t && Equals(t);
public override int GetHashCode() => Coefficient.GetHashCode() * 31 + Exponent.GetHashCode();
public int CompareTo(Term other) {
int c = Exponent.CompareTo(other.Exponent);
if (c != 0) return c;
return Coefficient.CompareTo(other.Coefficient);
}
public override string ToString() => (Coefficient, Exponent) switch {
(0, _) => "0",
(_, 0) => $"{Coefficient}",
(1, 1) => "x",
(-1, 1) => "-x",
(_, 1) => $"{Coefficient}x",
(1, _) => $"x^{Exponent}",
(-1, _) => $"-x^{Exponent}",
_ => $"{Coefficient}x^{Exponent}"
};
}
}
You may also check:How to resolve the algorithm Yahoo! search interface step by step in the R programming language
You may also check:How to resolve the algorithm Generate lower case ASCII alphabet step by step in the Z80 Assembly programming language
You may also check:How to resolve the algorithm Rosetta Code/Find unimplemented tasks step by step in the Racket programming language
You may also check:How to resolve the algorithm Active Directory/Search for a user step by step in the D programming language
You may also check:How to resolve the algorithm Extend your language step by step in the E programming language