WedX - журнал о программировании и компьютерных науках

Вычисление треугольного корня с помощью сложения, вычитания и деления пополам

Согласно правилу конкретной игры, сила персонажа пропорциональна треугольному корню опыта персонажа. Например, 15-20 опыта дают 5 силы, 21-27 опыта дают 6 силы, 28-35 опыта дают 7 силы и т. д. Известно, что некоторые игроки достигли опыта в сотни миллиардов.

Я пытаюсь реализовать эту игру на 8-битной машине, которая имеет только три арифметических инструкции: сложение, вычитание и деление на 2. Например, чтобы умножить число на 4, программа дважды сложит его сама с собой. Общее умножение намного медленнее; Я написал программную подпрограмму, чтобы сделать это, используя таблицу в четверть квадрата.

Я рассматривал возможность вычисления треугольного корня T(p) с помощью поиска пополам для последовательных треугольных чисел, ограничивающих число опыта. сверху и снизу. Мой план состоял в том, чтобы использовать повторяющуюся идентичность для T(2*p) до тех пор, пока она не превысит опыт, а затем использовать ее в качестве верхней границы для поиска пополам. Но у меня возникли проблемы с поиском идентификатора для T((x+y)/2) в делении пополам, в котором не используются ни x*y, ни (x+y)^2.

Существует ли эффективный алгоритм для вычисления треугольного корня числа с помощью простого сложения, вычитания и деления пополам? Или мне придется выполнять O(log n) умножений, по одному для вычисления каждой средней точки в поиске пополам? Или было бы лучше рассмотреть возможность деления в длину, чтобы использовать метод Ньютона?

Определение T(x):

T(x) = (n * (n + 1))/2

Тождества, которые я получил:

T(2*x) = 4*T(x) - x
# e.g. T(5) = 15, T(10) = 4*15 - 5 = 55

T(x/2) = (T(x) + x/2)/4
# e.g. T(10) = 55, T(5) = (55 + 5)/4 = 15

T(x + y) = T(x) + T(y) + x*y
# e.g. T(3) = 6, T(7) = 28, T(10) = 6 + 28 + 21 = 55

T((x + y)/2) = (T(x) + T(y) + x*y + (x + y)/2)/4
# e.g. T(3) = 6, T(7) = 28, T(5) = (6 + 28 + 21 + 10/2)/4 = 15

Ответы:


1

Выполните поиск пополам, но убедитесь, что y - x всегда является степенью двойки. (Это не увеличивает асимптотическое время выполнения.) Затем T((x + y) / 2) = T(x) + T(h) + x * h, где h — степень двойки, поэтому x * h можно вычислить со сдвигом.

Вот доказательство концепции Python (написано наспех, более или менее неоптимизировано, но позволяет избежать дорогостоящих операций).

def tri(n):
    return ((n * (n + 1)) >> 1)

def triroot(t):
    y = 1
    ty = 1

    # Find a starting point for bisection search by doubling y using
    # the identity T(2*y) = 4*T(y) - y. Stop when T(y) exceeds t.
    # At the end, x = 2*y, tx = T(x), and ty = T(y).
    while (ty <= t):
        assert (ty == tri(y))
        tx = ty
        ty += ty
        ty += ty
        ty -= y
        x = y
        y += y

    # Now do bisection search on the interval [x .. x + h),
    # using these identities:
    # T(x + h) = T(x) + T(h) + x*h
    # T(h/2) = (T(h) + h/2)/4
    th = tx
    h = x
    x_times_h = ((tx + tx) - x)
    while True:
        assert (tx == tri(x))
        assert (x_times_h == (x * h))

        # Divide h by 2
        h >>= 1
        x_times_h >>= 1
        if (not h):
            break
        th += h
        th >>= 1
        th >>= 1

        # Calculate the midpoint of the search interval
        tz = ((tx + th) + x_times_h)
        z = (x + h)
        assert (tz == tri(z))

        # If the midpoint is below the target, move the lower bound
        # of the search interval up to the midpoint
        if (t >= tz):
            tx = tz
            x = z
            x_times_h += ((th + th) - h)
    return x
for q in range(1, 100):
    p = triroot(q)
    assert (tri(p) <= q < tri((p + 1)))
    print(q, p)
27.03.2014
  • Вот процедуры извлечения квадратного корня для ARM, которые вдохновили на этот ответ: finesse.demon. co.uk/steven/sqrt.html 28.03.2014
  • Спасибо. Я на мгновение забыл, что при делении пополам (y - x) всегда будет степенью двойки. 28.03.2014

  • 2

    Как видно на связанной странице на math.stackexchange.com, существует прямая формула для решения этой проблемы, и если x = n*(n+1)/2, то наоборот:

    n = (sqrt(1+8*x) - 1)/2
    

    Теперь есть квадратный корень и другие вещи, но я бы предложил использовать эту прямую формулу с реализацией, подобной следующей:

    tmp  = x + x;   '2*x
    tmp += tmp;     '4*x
    tmp += tmp + 1; '8*x + 1
    n = 0;
    n2 = 0;
    while(n2 <= tmp){
        n2 += n + n + 1; 'remember that (n+1)^2 - n^2 = 2*n + 1
        n++;
    }
    'here after the loops  n = floor(sqrt(8*x+1)) + 1
    n -= 2;         'floor(sqrt(8*x+1)) - 1
    n /= 2;         '(floor(sqrt(8*x+1)) - 1) / 2
    

    Конечно, это можно улучшить для повышения производительности, если необходимо, например, учитывая, что целые значения floor(sqrt(8*x+1)) + 1 равны, поэтому n можно увеличивать с шагом 2 (соответственно переписав вычисление n2: n2 += n + n + n + n + 4, которое само по себе может быть записано лучше, чем это).

    27.03.2014
  • Этот алгоритм выглядит как O(n), чего я надеялся избежать. Сколько итераций он будет выполнять с x = 10 миллиардов? 28.03.2014
  • O (n ^ 0,5), потому что есть только один цикл, и он используется для вычисления квадратного корня. Действительно, этот подход показывает, что поставленная вами задача (вычисление треугольного корня) эквивалентна вычислению квадратного корня (все остальные шаги выполняются только один раз, так же как и O (1). Это говорит о том, что теперь для решения вашей проблемы вы можете возьмите любой хороший алгоритм для квадратного корня и поместите его вместо текущего цикла while.Так что, если вы хотите использовать деление пополам, теперь вы можете применить его к квадратному корню вместо этого треугольного корня, и, возможно, вы можете найти много решений для этого в литературе и вокруг. 29.03.2014
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]