C #, sin bucles
OK, hojeé un par de esos enlaces, pero para ser honesto, fueron un poco aburridos. No estoy interesado en optimizarlo con tablas hash y demás. ¿Por qué debería necesitarlo? ¡Tienes una maldita supercomputadora!
Demonios, ¡ni siquiera quiero molestarme con los bucles! Esta solución seguirá la regla de no-loops .
Tenga en cuenta que el código que estoy a punto de escribir no es un buen código, o el tipo de código que escribiría en la vida real (en caso de que algún posible empleador lea esto). Este código enfatiza la brevedad y la capacidad de trabajar en una narrativa, y enfatiza las convenciones y rituales y bucles apropiados, etc.
Para demostrar de lo que estoy hablando, comenzaremos con una clase impactante con campos públicos para almacenar los operandos de la ecuación:
class BealOperands
{
public BigInteger A, B, C, x, y, z;
}
Bien, comenzaremos con lo que probablemente sea el desafío más difícil. Necesitamos encontrar una manera de permutar a través de cada combinación de esos operandos. Indudablemente, hay formas de hacerlo de manera más eficiente que verificar cada permutación, pero no puedo molestarme en descubrirlas. ¿Y por qué debería hacerlo? ¡Tenemos una maldita supercomputadora!
Aquí está el algoritmo que se me ocurrió. Es increíblemente ineficiente, y repasa los mismos operandos una y otra vez, pero ¿a quién le importa? ¡Supercomputadora!
- Trate los seis operandos como un número de base 2 y permute a través de cada combinación.
- Trate los seis operandos como un número de base 3 y permute a través de cada combinación.
- Trate los seis operandos como un número de base 4 y permute a través de cada combinación.
- (...)
¿Cómo hacer todo esto sin bucles? ¡Fácil! Simplemente implemente un IEnumerable
y asociado IEnumerator
para bombear las permutaciones. Más tarde, usaremos LINQ para consultarlo.
class BealOperandGenerator : IEnumerable<BealOperands>
{
// Implementation of IEnumerable<> and IEnumerable -- basically boilerplate to get to BealOperandGeneratorEnumerator.
public IEnumerator<BealOperands> GetEnumerator() { return new BealOperandGeneratorEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
class BealOperandGeneratorEnumerator : IEnumerator<BealOperands>
{
public BealOperandGeneratorEnumerator() { Reset(); }
private BealOperands operands;
private BigInteger @base;
public void Reset()
{
// A is set to 0, which is "before" its minimum value, because IEnumerators are supposed to
// point to their first element *after* the first call to MoveNext().
// All other operands are set to their minimum values.
operands = new BealOperands { A = 0, B = 1, C = 1, x = 3, y = 3, z = 3 };
@base = 2;
}
public BealOperands Current
{
get
{
// We need to return a copy, since we'll be manipulating our internal one.
return new BealOperands {
A = operands.A, B = operands.B, C = operands.C,
x = operands.x, y = operands.y, z = operands.z };
}
}
public bool MoveNext()
{
// Increment the lowest "digit" and "carry" as necessary.
operands.A++;
if (operands.A - 1 >= @base)
{
operands.A = 1; operands.B++;
if (operands.B - 1 >= @base)
{
operands.B = 1; operands.C++;
if (operands.C - 1 >= @base)
{
operands.C = 1; operands.x++;
if (operands.x - 3 >= @base)
{
operands.x = 3; operands.y++;
if (operands.y - 3 >= @base)
{
operands.y = 3; operands.z++;
if (operands.z - 3 >= @base)
{
operands.z = 3; @base++;
}
}
}
}
}
}
// There will always be more elements in this sequence.
return true;
}
// More boilerplate
object System.Collections.IEnumerator.Current { get { return Current; } }
public void Dispose() { }
}
Ahora estamos en el negocio! Todo lo que necesitamos hacer es enumerar una instancia de BealOperandGenerator
y encontrar un contraejemplo de la Conjetura de Beal.
Nuestro siguiente gran problema es que no parece haber una forma integrada de elevar BigInteger
a la potencia de a BigInteger
. Hay BigInteger.Pow(BigInteger value, int exponent)
, y BigInteger.ModPow(BigInteger value, BigInteger exponent, BigInteger modulus)
, pero ningún método para elevar a BigInteger
, al poder de otro BigInteger
, módulo infinito.
¡Qué brillante clavo de problema! ¡Parece que fue hecho para ser resuelto con nuestro IEnumerable
/ IEnumerator
martillo!
class BigIntegerPowerEnumerable : IEnumerable<Tuple<BigInteger, BigInteger>>
{
public BigIntegerPowerEnumerable(BigInteger @base, BigInteger exponent) { this.@base = @base; this.exponent = exponent; }
BigInteger @base, exponent;
public IEnumerator<Tuple<BigInteger, BigInteger>> GetEnumerator() { return new BigIntegerPowerEnumerator(@base, exponent); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
class BigIntegerPowerEnumerator : IEnumerator<Tuple<BigInteger, BigInteger>>
{
public BigIntegerPowerEnumerator(BigInteger @base, BigInteger exponent)
{
originalBase = @base;
originalExponent = exponent;
Reset();
}
BigInteger originalBase, currentBase, originalExponent, currentExponent;
bool finished;
public void Reset()
{
// IEnumerable.Reset() is a silly method. You're required to implement it when you implement IEnumerable,
// but it isn't used by foreach or LINQ or anything. If you want to re-enumerate the enumerable, just get
// a brand new enumerator.
// In this case it gets in the way. The only reason I'm storing the original values is so I can implement
// this useless method properly. I supposed I could just throw a NotImplementedException or something,
// but it's done now.
currentBase = originalBase;
currentExponent = originalExponent;
finished = false;
}
public bool MoveNext()
{
if (finished) return false;
if (currentExponent <= Int32.MaxValue)
{
currentBase = BigInteger.Pow(currentBase, (Int32)currentExponent);
currentExponent = 1;
finished = true;
}
else
{
currentBase = BigInteger.Pow(currentBase, Int32.MaxValue);
currentExponent -= Int32.MaxValue;
}
return true;
}
public Tuple<BigInteger, BigInteger> Current
{
get { return new Tuple<BigInteger, BigInteger>(currentBase, currentExponent); }
}
object System.Collections.IEnumerator.Current { get { return Current; } }
public void Dispose() { }
}
static class BigIntegerPowExtension
{
public static BigInteger Pow(this BigInteger @base, BigInteger exponent)
{
return new BigIntegerPowerEnumerable(@base, exponent).Last().Item1;
}
}
Ahora tenemos un método de extensión Pow
, que se puede llamar en a BigInteger
, y toma un BigInteger
exponente y ningún módulo.
OK, retrocedamos. ¿Cómo podemos saber si un particular BealOperands
es un contraejemplo de la Conjetura de Beal? Bueno, dos cosas deben ser ciertas:
- Los operandos, cuando se conectan a esa fórmula en la parte superior de la página, deben formar una ecuación verdadera.
- A, B y C NO deben tener un factor primo común (es decir, su MCD es 1).
Tenemos lo que necesitamos para verificar la primera condición. Y resulta que la segunda condición es mucho más fácil de verificar de lo que parece. BigInteger
proporciona un GreatestCommonDivisor
método encantador , que nos permite evitar convenientemente toda la pesadilla de tratar de implementar eso sin bucles.
Así que estamos listos para escribir un método para verificar si a BealOperands
es un contraejemplo. Aquí va...
static class BealOperandsExtensions
{
public static bool IsBealsConjectureCounterExample(this BealOperands o)
{
// If the equation isn't even true, we don't have a counter example unfortunately
if (o.A.Pow(o.x) + o.B.Pow(o.y) != o.C.Pow(o.z))
{
return false;
}
// We have a counterexample if A, B and C are coprime
return BigInteger.GreatestCommonDivisor(o.A, o.B) == 1 &&
BigInteger.GreatestCommonDivisor(o.A, o.C) == 1 &&
BigInteger.GreatestCommonDivisor(o.B, o.C) == 1;
}
}
Y finalmente podemos unirlo todo con este Main
método bastante hábil :
static class Program
{
static void Main()
{
var bealOperandGenerator = new BealOperandGenerator();
if (bealOperandGenerator.Any(o => o.IsBealsConjectureCounterExample()))
{
Console.WriteLine("IN YOUR FACE, BEAL!");
}
}
}