Обчисліть найбільш ефективну двійкову функцію


13

Сьогодні ми будемо обчислювати найбільш ефективну бінарну функцію. Більш конкретно, ми будемо обчислювати функцію, яка, коли вираз створюється від застосування функції до постійного вводу 0 або власного виводу, може представляти всі додатні цілі числа з найкоротшими можливими виразами, надаючи більш високий пріоритет меншим цілим числам.

Ця функція будується так:

Для кожного цілого числа, починаючи з 1 і піднімаючи вгору, вибираємо найкоротший вираз, якому ми ще не призначили вихід, і робимо це ціле число результатом цього виразу. Прив’язки довжини виразу будуть розбиті на менший лівий аргумент, а потім на менший правий аргумент. Ось як це працює:

  • Спочатку 1 не призначено. Найкоротший непризначений вираз є f(0, 0), тому ми встановимо його на 1.

  • Тепер 2 не призначено. Найкоротші непризначені вирази - f(f(0, 0), 0)= f(1, 0)і f(0, f(0, 0))= f(0, 1). Зв'язки розбиті на менший лівий аргумент, так f(0, 1) = 2.

  • Залишився найкоротший непризначений вираз f(f(0, 0), 0)= f(1, 0), значить f(1, 0) = 3.

  • Тепер у нас немає виразів лише з 2 fс і 3 0с, тому нам доведеться додати ще одне з кожного. Розриваючи зв'язки лівим аргументом, потім правим аргументом, ми отримуємо f(0, 2) = 4, оскільки f(0, f(0, f(0, 0))) = f(0, f(0, 1)) = f(0, 2).

  • Продовжуючи, у нас є f(0, 3) = 5, f(1, 1) = 6, f(2, 0) = 7, f(3, 0) = 8, f(0, 4) = 9, ...

Ось таблиця, яку я заповнив для перших кількох значень:

    0  1  2  3  4  5  6  7  8
 /---------------------------
0|  1  2  4  5  9 10 11 12 13
1|  3  6 14 15 37 38 39 40 41
2|  7 16 42 43
3|  8 17 44 45
4| 18 46
5| 19 47
6| 20 48
7| 21 49
8| 22 50

Ще один спосіб поглянути на це - кожен вихід має розмір, рівний сумі розмірів його входів плюс один. Таблиця заповнюється в порядку збільшення розміру виводу, зв'язки розбиваються мінімізацією лівого вводу та правого введення.

Ваша задача полягає в тому, що ви маєте два негативних цілих числа, як вхід, обчислення та виведення значення цієї функції. Це код гольфу. Найкоротше рішення, в байтах, виграє. Стандартні лазівки заборонені.


Схоже на A072766 , але відрізняється від f (3, 1).
kennytm

2
Це перший виклик через деякий час, який спантеличує мене дещо ефективно підрахувати. Я вважаю, що з каталонськими номерами щось можливо, але не можу відразу придумати рішення. Хм ...
orlp

2
Гаразд, тому я не думаю, що це дасть гарну відповідь на гольф, але те, що ви можете зробити, щоб зробити його досить ефективним, - це багаторазово віднімати каталонські числа від аргументів функції, поки вони не будуть меншими, ніж наступне каталонське число. Тоді ви знайшли довжину їх виразів. Тоді ви можете використовувати функції ранжування / відключення з цього документу з модифікацією для обчислення результату. Можливо, зробивши все можливе, можна «скасувати» біти коду посередині і знайти досить елегантне рішення.
orlp

Насправді, підхід з мого попереднього коментаря не працює. ((0, (0, (0, 0))), 0)лексикографічно менше (((0, 0), 0), (0, 0)), проте останній має меншу ліву частину.
orlp

Відповіді:


6

Haskell, 110 байт

f q=head[i|let c=[(-1,0)]:[[(f a,f b)|n<-[0..k],a<-c!!n,b<-c!!(k-n)]|k<-[0..]],(p,i)<-zip(concat c)[0..],p==q]

Аргументом тут вважається кортеж (x,y). Досить схожий на відповідь вище, але список пошуку містить лише пари лівих та правих індексів замість дерев.


1
Гарна відповідь! head[...]є [...]!!0і (p,i)<-zip(concat c)[0..]може бути скорочений до (i,p)<-zip[0..]$id=<<c.
Лайконі

Дякуємо за покращення! Однозначно додавання id=<<до репертуару :)
halfflat

5

Python 3, 154 байти

b=lambda n:[(l,r)for k in range(1,n)for l in b(k)for r in b(n-k)]+[0]*(n<2)
def f(x,y):r=sum((b(n)for n in range(1,x+y+3)),[]);return r.index((r[x],r[y]))

Це не дуже швидко і не дуже гофро, але це початок.


5

Оце Так! Мені фактично вдалося скласти ефективний алгоритм обчислення. Я спочатку цього не очікував. Рішення досить елегантне. Він неодноразово виводить все більше і більше, а потім повторюється аж до базового випадку 0. У цій відповіді функція C (n) позначає каталонські числа .

Найважливішим першим кроком є ​​визнання, що є C (0) = 1 значення довжини нульової (а саме 0), C (1) = 1 значення довжини один (а саме f (0, 0)), C (2) = 2 значення довжини два (f (0, f (0, 0)) і f (f (0, 0), 0)).

Це означає, що якщо ми шукаємо n-й вираз, і знаходимо найбільший k такий, що C (0) + C (1) + ... + C (k) <= n, то знаємо, що n має довжину k .

Але зараз ми можемо продовжувати! Оскільки вираз, який ми шукаємо, - це n - C (0) - C (1) - ... - C (k) -ий вираз у його класі довжини.

Тепер ми можемо скористатися аналогічним трюком, щоб знайти довжину лівого відрізка, а потім ранг у цьому підрозділі. А потім повторюйте ті чини, які ми знайшли!

Він виявив, що f (5030, 3749) = 1542317211 у мить ока.

Пітон, неконкурентоспроможний

def C(n):
    r = 1
    for i in range(n):
        r *= 2*n - i
        r //= i + 1
    return r//(n+1)

def unrank(n):
    if n == 0: return 0

    l = 0
    while C(l) <= n:
        n -= C(l)
        l += 1

    right_l = l - 1
    while right_l and n >= C(l - 1 - right_l) * C(right_l):
        n -= C(l - 1 - right_l) * C(right_l)
        right_l -= 1

    right_num = C(right_l)

    r_rank = n % right_num
    l_rank = n // right_num

    for sz in range(l - 1 - right_l): l_rank += C(sz)
    for sz in range(right_l): r_rank += C(sz)

    return (unrank(l_rank), unrank(r_rank))

def rank(e):
    if e == 0: return 0
    left, right = e

    l = str(e).count("(")
    left_l = str(left).count("(")
    right_l = str(right).count("(")
    right_num = C(right_l)

    n = sum(C(sz) for sz in range(l))
    n += sum(C(sz)*C(l - 1 - sz) for sz in range(left_l))

    n += (rank(left) - sum(C(sz) for sz in range(left_l))) * C(right_l)
    n += rank(right) - sum(C(sz) for sz in range(right_l))

    return n

def f(x, y):
    return rank((unrank(x), unrank(y)))

Я впевнений, що роблю купу непотрібних обчислень, і багато середніх кроків можна буде усунути.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.