Computar a função binária mais eficiente


13

Hoje, calcularemos a função binária mais eficiente. Mais especificamente, calcularemos a função que, quando uma expressão é criada a partir da aplicação da função na entrada constante 0 ou em sua própria saída, pode representar todos os números inteiros positivos com as expressões mais curtas possíveis, colocando maior prioridade nos números inteiros menores.

Esta função é criada da seguinte maneira:

Para cada número inteiro, começando em 1 e subindo, escolha a expressão mais curta à qual ainda não atribuímos uma saída e torne esse número inteiro a saída dessa expressão. Os laços no comprimento da expressão serão quebrados pelo argumento menor à esquerda e depois pelo argumento menor à direita. Veja como funciona:

  • Inicialmente, 1 não está atribuído. A expressão mais curta não atribuída é f(0, 0), portanto, definiremos isso como 1.

  • Agora, 2 não está atribuído. As expressões mais curtas não atribuídas são f(f(0, 0), 0)= f(1, 0)e f(0, f(0, 0))= f(0, 1). Os laços são quebrados para um argumento menor à esquerda, então f(0, 1) = 2.

  • A expressão mais curta não atribuída restante é f(f(0, 0), 0)= f(1, 0), então f(1, 0) = 3.

  • Agora, estamos sem expressões com apenas 2 se f3 0s, então teremos que adicionar mais uma de cada. Rompendo laços com o argumento da esquerda, depois o argumento da direita, entendemos f(0, 2) = 4desde então f(0, f(0, f(0, 0))) = f(0, f(0, 1)) = f(0, 2).

  • Continuando, temos f(0, 3) = 5, f(1, 1) = 6, f(2, 0) = 7, f(3, 0) = 8, f(0, 4) = 9, ...

Aqui está uma tabela que preenchi para os primeiros valores:

    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

Outra maneira de ver é que cada saída tem um tamanho igual à soma dos tamanhos de suas entradas mais um. A tabela é preenchida em ordem crescente de tamanho de saída, empates quebrados minimizando a entrada esquerda e a entrada direita.

Seu desafio é, considerando dois números inteiros não negativos como entrada, calcule e produza o valor dessa função. Isso é código de golfe. A solução mais curta, em bytes, vence. As brechas padrão são proibidas.


Parece um A072766 , mas difere de f (3, 1).
— kennytm

2
Este é o primeiro desafio em um tempo que me intriga a calcular de maneira eficiente. Acredito que algo é possível com os números catalães, mas não consigo pensar imediatamente em uma solução. Hmm ...
— orlp 20/03/2019

2
Tudo bem, então eu não acho que seria uma boa resposta, mas o que você pode fazer para torná-la razoavelmente eficiente é subtrair repetidamente os números catalães dos argumentos da função até que eles sejam menores que o próximo número catalão. Então você encontrou o comprimento de suas expressões. Em seguida, você pode usar as funções de classificação / não classificação deste documento , com modificação, para calcular o resultado. Talvez depois de fazer tudo isso, seja possível 'cancelar' bits de código no meio e encontrar uma solução razoavelmente elegante.
— orlp

Na verdade, a abordagem do meu comentário anterior não funciona. ((0, (0, (0, 0))), 0)é lexicograficamente menor que (((0, 0), 0), (0, 0)), no entanto, este último tem um lado esquerdo menor.
— Ou orp

Respostas:


6

Haskell, 110 bytes

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]

O argumento aqui é considerado a tupla (x,y). Bem parecido com a resposta acima, mas a lista de pesquisa contém apenas os pares de índices esquerdo e direito em vez das árvores.


1
Boa resposta! head[...]é [...]!!0e (p,i)<-zip(concat c)[0..]pode ser reduzido para (i,p)<-zip[0..]$id=<<c.
— Laikoni 22/03/19

Obrigado pelas melhorias! Definitivamente adicionando id=<<ao repertório :)
— halfflat

5

Python 3, 154 bytes

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

Não é muito rápido nem muito golfe, mas é um começo.


5

Uau! Na verdade, eu consegui criar um algoritmo de computação eficiente. Eu não esperava isso no começo. A solução é bastante elegante. Deduz repetidamente mais e mais, depois retorna até o caso base de 0. Nesta resposta, a função C (n) indica os números catalães .

O primeiro passo crucial é reconhecer que existem valores C (0) = 1 de comprimento zero (ou seja, o próprio 0), C (1) = 1 valores de comprimento um (ou seja, f (0, 0)), C (2) = 2 valores de comprimento dois (f (0, f (0, 0)) ef (f (0, 0), 0)).

Isso significa que, se estamos procurando a enésima expressão, e encontramos a maior k, de modo que C (0) + C (1) + ... + C (k) <= n, então sabemos que n tem comprimento k .

Mas agora podemos continuar! Porque a expressão que procuramos é a expressão n - C (0) - C (1) - ... - C (k) na sua classe de comprimento.

Agora podemos usar um truque semelhante para encontrar o comprimento do segmento esquerdo e a classificação nessa subseção. E depois recuar sobre as classificações que encontramos!

Ele descobriu que f (5030, 3749) = 1542317211 em um piscar de olhos.

Python, não-competitivo

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

Estou bastante certo de que estou fazendo um monte de cálculos desnecessários e muitos passos intermediários podem ser removidos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.