计算最有效的二进制函数


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 fs和3 0s 的表达式,因此我们必须再添加一个。从左争论开始,然后由右争论打破关系,我们得到f(0, 2) = 4,因为f(0, f(0, f(0, 0))) = f(0, f(0, 1)) = f(0, 2)

  • 继续,我们有f(0, 3) = 5f(1, 1) = 6f(2, 0) = 7f(3, 0) = 8f(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
好的,所以我认为这不是一个好的答案,但是要使其达到合理的效率,您需要做的是从函数参数中反复减去加泰罗尼亚语数字,直到它们小于下一个加泰罗尼亚语数字。然后,您发现了它们的表达长度。然后你可以使用排名/ unranking功能从本文,与修改,来计算结果。在完成所有这些工作之后,也许有可能在中间“取消”一些代码并找到一个合理的解决方案。
Orlp

实际上,我之前的评论中的方法不起作用。((0, (0, (0, 0))), 0)在字典上小于(((0, 0), 0), (0, 0)),但是后者的左侧较小。
Orlp

Answers:


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
Laikoni

感谢您的改进!绝对增加id=<<了曲目:)
半放荡

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个长度为1的值(即f(0,0)),C(2)=长度为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。

Python,非竞争

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)))

我相当确定我正在做大量不必要的计算,并且可以删除许多中间步骤。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.