Hitung fungsi biner yang paling efisien

13

Hari ini, kita akan menghitung fungsi biner yang paling efisien. Lebih khusus lagi, kita akan menghitung fungsi yang, ketika ekspresi dibuat dari penerapan fungsi ke input konstan 0 atau outputnya sendiri, dapat mewakili semua bilangan bulat positif dengan ekspresi sesingkat mungkin, menempatkan prioritas yang lebih tinggi pada bilangan bulat yang lebih kecil.

Fungsi ini dibangun sebagai berikut:

Untuk setiap integer, mulai dari 1 dan naik ke atas, pilih ekspresi terpendek yang belum kami tetapkan untuk output, dan buat integer itu output dari ekspresi itu. Ikatan dalam panjang ekspresi akan dipecah oleh argumen kiri yang lebih kecil, dan kemudian oleh argumen kanan yang lebih kecil. Begini cara kerjanya:

  • Awalnya, 1 tidak ditugaskan. Ekspresi unassigned terpendek adalah f(0, 0), jadi kami akan menetapkan itu menjadi 1.

  • Sekarang, 2 tidak ditugaskan. Ekspresi unassigned terpendek adalah f(f(0, 0), 0)= f(1, 0)dan f(0, f(0, 0))= f(0, 1). Ikatan dipecah menjadi argumen kiri yang lebih kecil, jadi f(0, 1) = 2.

  • Ekspresi unassigned terpendek yang tersisa adalah f(f(0, 0), 0)= f(1, 0), jadi f(1, 0) = 3.

  • Sekarang, kita kehabisan ekspresi dengan hanya 2 fdetik dan 3 0detik, jadi kita harus menambahkan satu lagi masing-masing. Memutus ikatan dengan argumen kiri, lalu argumen kanan, kita dapatkan f(0, 2) = 4, sejak itu f(0, f(0, f(0, 0))) = f(0, f(0, 1)) = f(0, 2).

  • Melanjutkan, kita memiliki f(0, 3) = 5, f(1, 1) = 6, f(2, 0) = 7, f(3, 0) = 8, f(0, 4) = 9, ...

Inilah tabel yang saya isi untuk beberapa nilai pertama:

    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

Cara lain untuk melihatnya adalah bahwa setiap output memiliki ukuran, sama dengan jumlah ukuran inputnya ditambah satu. Tabel diisi sesuai dengan peningkatan ukuran output, ikatan rusak dengan meminimalkan input kiri kemudian input kanan.

Tantangan Anda adalah, diberi dua bilangan bulat non-negatif sebagai input, menghitung dan menampilkan nilai fungsi ini. Ini golf kode. Solusi terpendek, dalam byte, menang. Celah standar dilarang.

isaacg
sumber
Tampak mirip dengan A072766 , tetapi berbeda mulai dari f (3, 1).
kennytm
2
Ini adalah tantangan pertama dalam beberapa saat yang membuat saya agak bingung untuk menghitung secara efisien. Saya percaya ada sesuatu yang mungkin dengan angka Catalan, tetapi tidak dapat langsung memikirkan solusi. Hmm ...
orlp
2
Baiklah, jadi saya tidak berpikir itu akan menjadi jawaban golf yang bagus, tetapi apa yang dapat Anda lakukan untuk membuatnya cukup efisien adalah berulang kali mengurangi angka Catalan dari argumen fungsi sampai lebih kecil dari angka Catalan berikutnya. Maka Anda telah menemukan panjang ekspresi mereka. Kemudian Anda dapat menggunakan fungsi pemeringkatan / unranking dari makalah ini , dengan modifikasi, untuk menghitung hasilnya. Mungkin setelah melakukan semua itu mungkin untuk 'membatalkan' bit kode di tengah dan menemukan solusi yang cukup elegan.
orlp
Sebenarnya, pendekatan dari komentar saya sebelumnya tidak berhasil. ((0, (0, (0, 0))), 0)secara leksikografis lebih kecil dari (((0, 0), 0), (0, 0)), namun yang kedua memiliki sisi kiri yang lebih kecil.
orlp

Jawaban:

6

Haskell, 110 byte

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]

Argumen di sini dianggap sebagai tuple (x,y). Cukup mirip dengan jawaban di atas, tetapi daftar pencarian hanya memegang pasangan indeks kiri dan kanan, bukan pohon.

halfflat
sumber
1
Jawaban bagus! head[...]adalah [...]!!0dan (p,i)<-zip(concat c)[0..]dapat disingkat menjadi (i,p)<-zip[0..]$id=<<c.
Laikoni
Terima kasih atas perbaikannya! Pasti menambah id=<<repertoar :)
halfflat
5

Python 3, 154 byte

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

Ini tidak terlalu cepat atau sangat golf, tapi ini awal.

orlp
sumber
5

Wow! Saya sebenarnya berhasil membuat algoritma perhitungan yang efisien. Saya tidak mengharapkan ini pada awalnya. Solusinya cukup elegan. Berulang kali menyimpulkan lebih dan lebih, kemudian berulang sampai ke kasus dasar 0. Dalam jawaban ini fungsi C (n) menunjukkan angka Catalan .

Langkah pertama yang penting adalah mengakui bahwa ada nilai C (0) = 1 panjang nol (yaitu 0 itu sendiri), C (1) = 1 nilai panjang satu (yaitu f (0, 0)), C (2) = 2 nilai panjang dua (f (0, f (0, 0)) dan f (f (0, 0), 0)).

Ini berarti bahwa jika kita mencari ekspresi ke-n, dan kami menemukan k terbesar sehingga C (0) + C (1) + ... + C (k) <= n maka kita tahu bahwa n memiliki panjang k .

Tapi sekarang kita bisa melanjutkan! Karena ekspresi yang kita cari adalah ekspresi n - C (0) - C (1) - ... - C (k) di kelas panjangnya.

Sekarang kita dapat menggunakan trik serupa untuk menemukan panjang segmen kiri, dan kemudian peringkat dalam subbagian itu. Dan kemudian muncul kembali pada peringkat yang kami temukan!

Ditemukan bahwa f (5030, 3749) = 1542317211 dalam sekejap mata.

Python, tidak bersaing

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

Saya cukup yakin saya melakukan banyak perhitungan yang tidak perlu dan banyak langkah tengah bisa dihilangkan.

orlp
sumber