C #, без петель
Хорошо, я просмотрел несколько этих ссылок, но, честно говоря, они были немного скучными. Я не заинтересован в том, чтобы оптимизировать его с помощью хеш-таблиц и еще много чего. Зачем мне это нужно? У тебя чертов суперкомпьютер!
Черт, я даже не хочу возиться с петлями! Это решение будет следовать правилу без циклов .
Обратите внимание, что код, который я собираюсь написать, не является хорошим кодом или кодом, который я написал бы в реальной жизни (на случай, если кто-нибудь из потенциальных работодателей прочитает это). Этот кодекс подчеркивает краткость и умение работать в повествовании, а также подчеркивает надлежащие условности, ритуалы, циклы и так далее.
Чтобы продемонстрировать, о чем я говорю, начнем с шокирующего класса с открытыми полями для хранения операндов уравнения:
class BealOperands
{
public BigInteger A, B, C, x, y, z;
}
Хорошо, мы начнем с того, что, вероятно, самая сложная задача. Нам нужно найти способ перестановки каждой комбинации этих операндов. Несомненно, есть способы сделать это более эффективно, чем проверка каждой перестановки, но я не могу быть обеспокоен, выясняя их. А зачем мне? У нас есть чертов суперкомпьютер!
Вот алгоритм, который я придумал. Это невероятно неэффективно и повторяет одни и те же операнды снова и снова, но кого это волнует? Суперкомпьютер!
- Рассматривайте шесть операндов как число основания-2 и переставляйте каждую комбинацию.
- Рассматривайте шесть операндов как число основания-3 и переставляйте каждую комбинацию.
- Относитесь к шести операндам как к числу 4 и переставляйте каждую комбинацию.
- (...)
Как сделать все это без петель? Легко! Просто реализовать IEnumerable
и связанное IEnumerator
с откачкой перестановок. Позже мы будем использовать LINQ для запроса.
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() { }
}
Теперь мы в деле! Все, что нам нужно сделать, это перечислить экземпляр BealOperandGenerator
и найти контрпример к гипотезе Била.
Наша следующая большая проблема заключается в том, что не существует встроенного способа поднять a BigInteger
до степени a BigInteger
. Есть BigInteger.Pow(BigInteger value, int exponent)
, и BigInteger.ModPow(BigInteger value, BigInteger exponent, BigInteger modulus)
, но нет способа поднять BigInteger
, к власти другого BigInteger
, по модулю бесконечности.
Какой блестящий гвоздь проблемы! Похоже, это было решено с помощью нашего IEnumerable
/ IEnumerator
молотка!
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;
}
}
Теперь у нас есть метод расширения Pow
, который можно вызывать для a BigInteger
и который принимает BigInteger
показатель степени без модуля.
Хорошо, давайте отступим. Как мы можем определить, является ли конкретный BealOperands
контрпример гипотезы Била? Ну, две вещи должны быть правдой:
- Операнды, включенные в эту формулу вверху страницы, должны образовывать истинное уравнение.
- A, B и C НЕ должны иметь общий простой множитель (т.е. их GCD равен 1).
У нас есть то, что нам нужно, чтобы проверить первое условие. И оказывается, что второе условие гораздо проще проверить, чем кажется. BigInteger
предоставляет прекрасный GreatestCommonDivisor
метод, который позволяет нам удобно обходить весь кошмар, пытаясь реализовать это без циклов.
Итак, мы готовы написать метод, чтобы проверить, BealOperands
является ли контрпример. Вот оно...
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;
}
}
И, наконец, мы можем объединить все это с помощью этого довольно изящного Main
метода:
static class Program
{
static void Main()
{
var bealOperandGenerator = new BealOperandGenerator();
if (bealOperandGenerator.Any(o => o.IsBealsConjectureCounterExample()))
{
Console.WriteLine("IN YOUR FACE, BEAL!");
}
}
}