C#: Однострочное решение и цикл While
На вход с консоли пользователь вводит любое число от 0 до что-то около long.Max Это число кладётся на левую чашу весов. Степенями тройки нужно сбалансировать эти весы. Ну то есть если на вход даётся 7, то на левые к 7 кладём 3, а на правые 9 и 1. Веса не могут повторяться. То есть нельзя дважды класть 1.
Цель, решить данную задачу в одну, максимально короткую строку. Не важно насколько она будет не читабельной.
Пока что я смог дойти вот до такого трёхстрочного решения :
List<List<long>> weights = new List<List<long>> { new List<long> { long.Parse(Console.ReadLine()) }, new List<long>() };
while (weights[0].Sum() != weights[1].Sum()) weights[weights[0].Sum() > weights[1].Sum() ? 1 : 0].Add((long)Math.Pow(3, ((Func<long, long, bool>)((number, power) => number > (long)((Math.Pow(3, power) - 1) / 2)))(weights[0].Sum() > weights[1].Sum() ? weights[0].Sum() - weights[1].Sum() : weights[1].Sum() - weights[0].Sum(), ((Func<long, long>)(number => (long)Math.Ceiling(Math.Log(number, 3))))(weights[0].Sum() > weights[1].Sum() ? weights[0].Sum() - weights[1].Sum() : weights[1].Sum() - weights[0].Sum())) ? ((Func<long, long>)(number => (long)Math.Ceiling(Math.Log(number, 3))))(weights[0].Sum() > weights[1].Sum() ? weights[0].Sum() - weights[1].Sum() : weights[1].Sum() - weights[0].Sum()) : ((Func<long, long>)(number => (long)Math.Ceiling(Math.Log(number, 3))))(weights[0].Sum() > weights[1].Sum() ? weights[0].Sum() - weights[1].Sum() : weights[1].Sum() - weights[0].Sum()) - 1));
Console.WriteLine(JsonSerializer.Serialize(weights));
Полагаю, что желаемого можно достичь используя метод Agreggate из Linq или используя лямбды, но сильно смущает наличие while. Возможно ли с ним что-то сделать?
Вот изначальное моё решение, которое я пытаюсь ужать до одной строки :
class Program
{
static void Main(string[] args)
{
long weight = long.Parse(Console.ReadLine());
List<List<long>> weights = new List<List<long>>
{
new List<long>(),
new List<long>()
};
weights[0].Add(weight);
FindThreeDegreeWeights(weights);
Console.WriteLine(JsonSerializer.Serialize(weights));
}
static long MinPowerOfThreeNotLessThan(long number)
{
long power = (long)Math.Ceiling(Math.Log(number, 3));
return power;
}
static bool IsNumberGreaterThanSumOfPreviousPowersOfThree(long number, long power)
{
long sum = (long)((Math.Pow(3, power) - 1) / 2);
return number > sum;
}
static void FindThreeDegreeWeights(List<List<long>> weights)
{
long leftSum = weights[0].Sum();
long rightSum = weights[1].Sum();
if (leftSum == rightSum)
return;
if (leftSum > rightSum)
{
long difference = leftSum - rightSum;
long upperDegree = MinPowerOfThreeNotLessThan(difference);
if (IsNumberGreaterThanSumOfPreviousPowersOfThree(difference, upperDegree))
{
weights[1].Add((long)Math.Pow(3, upperDegree));
}
else
{
weights[1].Add((long)Math.Pow(3, upperDegree - 1));
}
}
else
{
long difference = rightSum - leftSum;
long upperDegree = MinPowerOfThreeNotLessThan(difference);
if (IsNumberGreaterThanSumOfPreviousPowersOfThree(difference, upperDegree))
{
weights[0].Add((long)Math.Pow(3, upperDegree));
}
else
{
weights[0].Add((long)Math.Pow(3, upperDegree - 1));
}
}
FindThreeDegreeWeights(weights);
}
}
Ответы (2 шт):
Может, полезно будет: целочисленный алгоритм перевода в уравновешенную троичную систему на Python (нули убрал)
def TriEqSystem(n):
lst = []
p = 1
while n:
n, m = divmod(n, 3)
if m:
lst.append(p*(3-2*m)) #добавляет p или (-p) для остатков 1 и 2
n += (m-1) #увеличить следующий разряд для остатка 2
p *= 3
return lst
for i in range(1,101):
l = TriEqSystem(i)
print(i, sum(l), l)
...
10 10 [1, 9]
11 11 [-1, 3, 9]
12 12 [3, 9]
13 13 [1, 3, 9]
14 14 [-1, -3, -9, 27]
15 15 [-3, -9, 27]
16 16 [1, -3, -9, 27]
17 17 [-1, -9, 27]
...
ОБНОВЛЕНО
Вот решение в одну строку по алгоритму @MBo (но я его сам придумал):
using System;
static class Program {
static void Main() {
for (long i = 1, n = Int64.Parse(Console.ReadLine()), j = n % 3; n > 0; i += i << 1, n = n / 3 + (j == 0 ? 0 : j - 1), j = n % 3 ) if (j > 0) Console.WriteLine(i * ((j << 1) - 3));
}
}
Если степень тройки в ответе положительная, то кладётся вместе с числом.
Можно и с применением Linq:
using System;
using System.Linq;
static class Program {
static void Main() {
foreach (var m in Enumerable.Range(Int64.Parse(Console.ReadLine()!) is long N && 1L is long i? 0 : 0, 40)
.Select(s => new { x = N = Math.DivRem(N, 3, out long j), Rem = j, y = j == 0 ? N : N += j - 1 })
.Where(w => w.Rem > 0).Select(s => (i *= 3) / 3 * ((s.Rem << 1) - 3))) Console.WriteLine(m);
}
}