Pengkodean integer yang kompak menjadi bitstring

8

Saya ingin secara kompak kode bilangan bulat positif xmenjadi bit, dengan cara yang memungkinkan decoding kembali ke bilangan bulat asli untuk decoder stateless mengetahui nilai maksimum mmasing-masing x; akan mungkin untuk secara unik memecahkan kode penyandian pengkodean, seperti halnya dalam pengkodean Huffman.
[Pengantar di atas memotivasi yang lain, tetapi bukan bagian dari definisi formal]

Notasi: untuk bilangan bulat non-negatif i, biarkan n(i)jumlah bit yang diperlukan untuk direpresentasikan idalam biner; itu adalah bilangan bulat non-negatif terkecil ksehinggai>>k == 0

  i :   0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  ..
n(i):   0   1   2   2   3   3   3   3   4   4   4   4   4   4   4   4   5   5   5   ..

Saya ingin fungsi yang F(x,m)didefinisikan untuk 0<xdan x<=m, dengan menghasilkan string ' 0' atau ' 1', memiliki properti ini:

  1. F(x,m)memiliki panjang kurang dari 2*n(x)atau 2*n(m)-1, mana yang lebih kecil.
  2. Jika x<ykemudian:
    • F(x,m)tidak lebih dari F(y,m);
    • F(x,m)dan F(y,m)berbeda pada beberapa posisi sepanjang F(x,m);
    • ada 0di F(x,m)pada posisi yang pertama.
  3. Ketika untuk mproperti tertentu 1 dan 2 tidak secara unik mendefinisikan F(x,m)untuk semua positif xpaling banyak m, kami menolak setiap pengkodean memberikan lebih F(x,m)dari beberapa pengkodean yang dapat diterima, untuk yang terkecil xyang panjangnya tidak cocok.

Catatan: Dalam contoh di atas, secara implisit, 0<x, 0<y, 0<m, x<=m, dan y<=m, sehingga F(x,m)dan F(y,m)didefinisikan.

Diminta program terpendek yang, mengingat xdan mmemenuhi batasan di atas dan hingga 9 angka desimal, menghasilkan F(x,m)konsisten dengan aturan di atas. Kerangka kerja C berikut (atau yang setara dalam bahasa lain) tidak dihitung:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define C(c) putchar((c)?'1':'0') // output '0' or '1'
#define S(s) fputs((s),stdout)    // output a string
typedef unsigned long t;          // at least 32 bits
void F(t x,t m);                  // prototype for F
int main(int argc,char *argv[]){
 if(argc==3)F((t)atol(argv[1]),(t)atol(argv[2]));
 return 0;}
void F(t x,t m) {   // counting starts on next line
}                   // counting ends before this line

Komentar: Properti 1 secara agresif membatasi panjang yang disandikan; properti 2 memformalkan bahwa decoding tidak ambigu dimungkinkan, dan mengkanonik encoding; Saya menegaskan (tanpa bukti) ini cukup untuk secara unik mendefinisikan output Fkapan m+1kekuatan dua, dan bahwa properti 3 cukup untuk secara unik mendefinisikan Funtuk lainnya m.

Ini adalah tabel parsial (buatan tangan; versi pertama yang diposting penuh dengan kesalahan, maaf):

  x     :   1       2       3       4       5       6       7       8       9       10      11      12      13      14      15
F(x,1)  : empty
F(x,2)  :   0       1
F(x,3)  :   0       10      11
F(x,4)  :   0       10      110     111
F(x,5)  :   0       10      110     1110    1111
F(x,6)  :   0       100     101     110     1110    1111
F(x,7)  :   0       100     101     1100    1101    1110    1111
F(x,8)  :   0       100     101     110     11100   11101   11110   11111

F(x,15) :   0       100     101     11000   11001   11010   11011   111000  111001  111010  111011  111100  111101  111110  111111
fgrieu
sumber
"Pengkodean yang ringkas?" Bagaimana ini lebih kompak daripada representasi basa-2 canonical integer? :)
Martin Ender
@ m.buettner: Masalah dengan representasi kanonik dalam basis 2 adalah bahwa 3 kemudian 7, 7 lalu 3, 1 kemudian 15, dan sejumlah hal lainnya, semua disandikan sebagai 11111, membuat penguraian sandi mustahil dilakukan. Dengan pengkodean yang diusulkan, gabungan beberapa output dapat diterjemahkan secara unik (termasuk dengan mperubahan maksimum secara dinamis). Saya mencoba mengklarifikasi itu.
fgrieu
1
Saya tidak setuju dengan meja Anda. Untuk F (x, 5) Anda memiliki 0,100,101,110,111. Tetapi, pengkodean 0,10.110.1110.1111 memenuhi (1) dan (2), dan karena 10 lebih pendek dari 100, itu menyebabkan pengkodean Anda untuk F (x, 5) ditolak.
nneonneo
1
Oh, tetapi untuk F (x, 8) meja Anda masih salah. 0,100,101,110,11100,11101,11110,11111 lebih baik untuk F (4,8) (110 vs 1100).
nneonneo
1
Nggak. Itu ilegal karena F (7,8) harus kurang dari 6 digit.
nneonneo

Jawaban:

1

Python 3, 163 byte

n=int.bit_length
R=range
def F(x,m,c=0):
 for i in R(x):L=2*n(m)-2;D=1<<L;r=n(D-c-sum(D>>min(2*n(x+1)-1,L)for x in R(i+1,m)))-1;v=c;c+=1<<r
 return bin(v)[2:L+2-r]

Abaikan c=0parameter, itu trik golf.

Ini dengan rakus memilih representasi yang sesingkat mungkin untuk setiap angka, dengan syarat bahwa jumlah yang tersisa masih dapat diwakili. Oleh karena itu, dengan konstruksi, itu persis memenuhi sifat yang diinginkan. Sebenarnya tidak sulit untuk memodifikasi kode ini untuk mendukung serangkaian aturan pengkodean yang berbeda.

Sebagai contoh, berikut adalah output hingga m=15:

m\x |   1       2       3       4       5       6       7       8       9       10      11      12      13      14      15
----+----------------------------------------------------------------------------------------------------------------------------
1   |   
2   |   0       1
3   |   0       10      11
4   |   0       10      110     111
5   |   0       10      110     1110    1111
6   |   0       100     101     110     1110    1111
7   |   0       100     101     1100    1101    1110    1111
8   |   0       100     101     110     11100   11101   11110   11111
9   |   0       100     101     110     11100   11101   11110   111110  111111
10  |   0       100     101     1100    1101    11100   11101   11110   111110  111111
11  |   0       100     101     1100    1101    11100   11101   111100  111101  111110  111111
12  |   0       100     101     1100    11010   11011   11100   11101   111100  111101  111110  111111
13  |   0       100     101     1100    11010   11011   11100   111010  111011  111100  111101  111110  111111
14  |   0       100     101     11000   11001   11010   11011   11100   111010  111011  111100  111101  111110  111111
15  |   0       100     101     11000   11001   11010   11011   111000  111001  111010  111011  111100  111101  111110  111111
nneonneo
sumber
2

Python, 171

def F(x,m):
 a=0;b=[9]
 while len(b)<m:
    if 4**len(bin(len(b)))*2>64*a<4**len(bin(m)):
     if(a&a+1)**a:b+=[a];a+=1
     else:a*=2
    else:a=b.pop()*2
 return bin((b+[a])[x])[2:]

Perhatikan bahwa garis yang tampaknya dimulai dengan 4 spasi sebenarnya dimulai dengan tab.

Pengujian, dengan skrip uji bitpwner:

         1        2        3        4        5        6        7        8        9        10       11       12       13       14       15      
F(x,1)   0       
F(x,2)   0        1       
F(x,3)   0        10       11      
F(x,4)   0        10       110      111     
F(x,5)   0        10       110      1110     1111    
F(x,6)   0        100      101      110      1110     1111    
F(x,7)   0        100      101      1100     1101     1110     1111    
F(x,8)   0        100      101      110      11100    11101    11110    11111   
F(x,9)   0        100      101      110      11100    11101    11110    111110   111111  
F(x,10)  0        100      101      1100     1101     11100    11101    11110    111110   111111  
F(x,11)  0        100      101      1100     1101     11100    11101    111100   111101   111110   111111  
F(x,12)  0        100      101      1100     11010    11011    11100    11101    111100   111101   111110   111111  
F(x,13)  0        100      101      1100     11010    11011    11100    111010   111011   111100   111101   111110   111111  
F(x,14)  0        100      101      11000    11001    11010    11011    11100    111010   111011   111100   111101   111110   111111  
F(x,15)  0        100      101      11000    11001    11010    11011    111000   111001   111010   111011   111100   111101   111110   111111  

Penjelasan:

Ini semua didasarkan pada pengamatan bahwa antara dua elemen berturut-turut dari kode, F (x, m) dan F (x + 1, m), kami selalu menambahkan satu ke nomor biner, lalu mengalikannya dengan dua beberapa kali. Jika langkah-langkah ini diikuti, maka itu adalah kode yang valid. Sisanya hanya menguji untuk memastikan itu cukup pendek.

Golf: 175 -> 171: diubah 2 ** (2 * ... menjadi 4 **

isaacg
sumber
Nit: F (1,1) harus menjadi string kosong.
nneonneo
1

Python - 370

Membuat pohon huffman, menyeimbangkannya agar sesuai dengan aturan, lalu berjalan-jalan di pohon untuk mendapatkan nilai akhir.

def w(n,m,c,p=""):
    try:[w(n[y],m,c,p+`y`)for y in 1,0]
    except:c[n[0]]=p
d=lambda x:len(x)>1and 1+d(x[1])
v=lambda x,y:v(x[1],y-1)if y else x
def F(x,m):
    r=[m];i=j=0
    for y in range(1,m):r=[[m-y],r]
    while d(r)>len(bin(m))*2-6-(m==8):g=v(r,i);g[1],g[1][0]=g[1][1],[g[1][0],g[1][1][0]];i,j=[[i+1+(d(g)%2<1&(1<i<5)&(m%7<1)),j],[j+1]*2][d(g)<5]
    c={};w(r,m,c);return c[x]

Untuk solusi yang lebih ringkas yang didasarkan pada pola yang muncul, lihat jawaban isaacg.
Ini adalah contoh yang baik tentang bagaimana pendekatan yang sama sekali berbeda dapat menyelesaikan masalah.

Uji:

chars = 8
maxM = 15
print " "*chars,
for m in range(1,maxM+1):
    p = `m`
    print p+" "*(chars-len(p)),
print
for m in range(1,maxM+1):
    p = "F(x,"+`m`+")"
    print p+" "*(chars-len(p)),
    for x in range(1,maxM+1):
        try:
            q = `F(x,m)`[1:-1]
            print q+" "*(chars-len(q)),
        except:
            print
            break

Hasil:

         1        2        3        4        5        6        7        8        9        10       11       12       13       14       15      
F(x,1)           
F(x,2)   0        1       
F(x,3)   0        10       11      
F(x,4)   0        10       110      111     
F(x,5)   0        10       110      1110     1111    
F(x,6)   0        100      101      110      1110     1111    
F(x,7)   0        100      101      1100     1101     1110     1111    
F(x,8)   0        100      101      1100     1101     1110     11110    11111   
F(x,9)   0        100      101      1100     1101     1110     11110    111110   111111  
F(x,10)  0        100      101      1100     1101     11100    11101    11110    111110   111111  
F(x,11)  0        100      101      1100     1101     11100    11101    111100   111101   111110   111111  
F(x,12)  0        100      101      11000    11001    11010    11011    11100    11101    11110    111110   111111  
F(x,13)  0        100      101      11000    11001    11010    11011    11100    11101    111100   111101   111110   111111  
F(x,14)  0        100      101      11000    11001    11010    11011    11100    111010   111011   111100   111101   111110   111111  
F(x,15)  0        100      101      11000    11001    11010    11011    111000   111001   111010   111011   111100   111101   111110   111111  
Vektor
sumber
Seperti yang ditunjukkan oleh nneonneo, F(7,8)dari 111110yang salah, untuk itu memiliki panjang 6, dan "kurang dari 2*n(x)" menyiratkan kurang dari 6.
fgrieu