C #, sem loops
OK, eu passei por alguns desses links, mas para ser sincero, eles eram um pouco chatos. Não estou interessado em otimizar o inferno com tabelas de hash e outros enfeites. Por que eu preciso? Você tem um maldito supercomputador!
Inferno, eu nem quero me preocupar com loops! Esta solução seguirá a regra de no-loops .
Observe que o código que estou prestes a escrever não é um código bom ou o tipo de código que eu escreveria na vida real (caso algum empregador em potencial leia isso). Esse código enfatiza a brevidade e a capacidade de trabalhar em uma narrativa e enfatiza as convenções, rituais e loops adequados e assim por diante.
Para demonstrar do que estou falando, começaremos com uma classe chocante com campos públicos para armazenar os operandos da equação:
class BealOperands
{
public BigInteger A, B, C, x, y, z;
}
OK, começaremos com o que provavelmente é o desafio mais difícil. Precisamos descobrir uma maneira de permutar todas as combinações desses operandos. Sem dúvida, existem maneiras de fazê-lo com mais eficiência do que verificar todas as permutações, mas não posso me incomodar em descobri-las. E por que eu deveria? Temos um maldito supercomputador!
Aqui está o algoritmo que eu criei. É incrivelmente ineficiente e repassa repetidamente os mesmos operandos, mas quem se importa? Supercomputador!
- Trate os seis operandos como um número de base 2 e permeie todas as combinações.
- Trate os seis operandos como um número base-3 e permeie todas as combinações.
- Trate os seis operandos como um número base-4 e permeie todas as combinações.
- (...)
Como fazer tudo isso sem loops? Fácil! Basta implementar um IEnumerable
e associado IEnumerator
para bombear as permutações. Mais tarde, usaremos o LINQ para consultá-lo.
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() { }
}
Agora estamos no negócio! Tudo o que precisamos fazer é enumerar uma instância BealOperandGenerator
e encontrar um contra-exemplo da conjectura de Beal.
Nosso próximo grande problema é que não parece haver uma maneira embutida de elevar BigInteger
a à potência de a BigInteger
. Existe BigInteger.Pow(BigInteger value, int exponent)
, e BigInteger.ModPow(BigInteger value, BigInteger exponent, BigInteger modulus)
, mas não existe, um método para elevar um BigInteger
, ao poder de outro BigInteger
, módulo infinito.
Que prego brilhante de problema! Parece que foi feito para ser resolvido com o nosso IEnumerable
/ IEnumerator
hammer!
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;
}
}
Agora temos um método de extensão Pow
, que pode ser chamado em a BigInteger
, e leva umBigInteger
expoente e nenhum módulo.
OK, vamos voltar. Como podemos saber se um particular BealOperands
é um contra-exemplo da conjectura de Beal? Bem, duas coisas precisam ser verdadeiras:
- Os operandos, quando conectados a essa fórmula na parte superior da página, devem formar uma equação verdadeira.
- A, B e C NÃO devem ter um fator primo comum (ou seja, seu MDC é 1).
Temos o que precisamos para verificar a primeira condição. E acontece que a segunda condição é muito mais fácil de verificar do que parece. BigInteger
fornece um GreatestCommonDivisor
método adorável , que permite evitar convenientemente todo o pesadelo de tentar implementá-lo sem loops.
Portanto, estamos prontos para escrever um método para verificar se a BealOperands
é um contra-exemplo. Aqui vai ...
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;
}
}
E, finalmente, podemos reunir tudo com esse Main
método bastante liso :
static class Program
{
static void Main()
{
var bealOperandGenerator = new BealOperandGenerator();
if (bealOperandGenerator.Any(o => o.IsBealsConjectureCounterExample()))
{
Console.WriteLine("IN YOUR FACE, BEAL!");
}
}
}