C #, nessun loop
OK, ho scremato un paio di quei collegamenti, ma ad essere sinceri erano un po 'noiosi. Non mi interessa ottimizzare l'inferno con le tabelle hash e quant'altro. Perché dovrei aver bisogno? Hai un dannato supercomputer!
Diavolo, non voglio nemmeno preoccuparmi dei loop! Questa soluzione seguirà la regola no-loop .
Si noti che il codice che sto per scrivere non è un buon codice, o il tipo di codice che scriverei nella vita reale (nel caso in cui qualche potenziale datore di lavoro capisca di leggerlo). Questo codice enfatizza la brevità e la capacità di lavorare in una narrazione, e sottolinea le convenzioni e i rituali, i cicli e così via appropriati.
Per dimostrare di cosa sto parlando, inizieremo con una classe scioccante con campi pubblici per memorizzare gli operandi dell'equazione:
class BealOperands
{
public BigInteger A, B, C, x, y, z;
}
OK, inizieremo con quella che è probabilmente la sfida più difficile. Dobbiamo trovare un modo per permutare attraverso ogni combinazione di quegli operandi. Ci sono indubbiamente modi per farlo in modo più efficiente che controllare ogni permutazione, ma non riesco a disturbarmi a capirle. E perché dovrei? Abbiamo un dannato supercomputer!
Ecco l'algoritmo che ho ideato. È incredibilmente inefficiente e ripete continuamente gli stessi operandi, ma a chi importa? Supercomputer!
- Tratta i sei operandi come un numero di base 2 e permuta attraverso ogni combinazione.
- Tratta i sei operandi come un numero di base 3 e permuta attraverso ogni combinazione.
- Tratta i sei operandi come un numero di base 4 e permuta attraverso ogni combinazione.
- (...)
Come fare tutto questo senza loop? Facile! Basta implementare un IEnumerable
e associato IEnumerator
per pompare le permutazioni. Più tardi, useremo LINQ per interrogarlo.
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() { }
}
Ora siamo in affari! Tutto ciò che dobbiamo fare è elencare un'istanza di BealOperandGenerator
e trovare un controesempio della congettura di Beal.
Il nostro prossimo grosso problema è che non sembra esserci un modo integrato per aumentare BigInteger
la potenza di a BigInteger
. C'è BigInteger.Pow(BigInteger value, int exponent)
, e BigInteger.ModPow(BigInteger value, BigInteger exponent, BigInteger modulus)
, ma nessun metodo per elevare a BigInteger
, alla potenza di un altro BigInteger
, modulo infinito.
Che chiodo brillante di un problema! Sembra che sia stato creato per essere risolto con il nostro 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;
}
}
Ora abbiamo un metodo di estensione Pow
, che può essere chiamato su a BigInteger
, e richiede un BigInteger
esponente e nessun modulo.
OK, facciamo un passo indietro. Come possiamo sapere se un particolare BealOperands
è un controesempio della congettura di Beal? Bene, due cose devono essere vere:
- Gli operandi, quando inseriti in quella formula nella parte superiore della pagina, devono formare una vera equazione.
- A, B e C NON devono avere un fattore primo comune (ovvero il loro GCD è 1).
Abbiamo ciò di cui abbiamo bisogno per verificare la prima condizione. E risulta che la seconda condizione è molto più facile da controllare di quanto sembri. BigInteger
fornisce un GreatestCommonDivisor
metodo adorabile , che ci consente di eludere convenientemente l'intero incubo del tentativo di implementarlo senza loop.
Quindi siamo pronti a scrivere un metodo per verificare se a BealOperands
è un controesempio. Ecco qui...
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 possiamo mettere tutto insieme con questo Main
metodo piuttosto fluido :
static class Program
{
static void Main()
{
var bealOperandGenerator = new BealOperandGenerator();
if (bealOperandGenerator.Any(o => o.IsBealsConjectureCounterExample()))
{
Console.WriteLine("IN YOUR FACE, BEAL!");
}
}
}