Fungsi bijective ℤ → ℤⁿ


23

Sepele mungkin untuk membuat fungsi bijective dari (himpunan semua bilangan bulat) ke Z (misalnya fungsi identitas).ZZ

Dimungkinkan juga untuk membuat fungsi bijective dari ke Z 2 (himpunan semua pasangan 2 bilangan bulat; produk cartesian dari Z danZZ2Z ). Sebagai contoh, kita bisa mengambil kisi yang mewakili titik integer pada bidang 2D, menggambar spiral dari 0 ke luar, dan kemudian menyandikan pasangan bilangan bulat sebagai jarak sepanjang spiral ketika memotong titik itu.Z

Spiral

(Fungsi yang melakukan ini dengan bilangan alami dikenal sebagai fungsi berpasangan .)

Bahkan, ada keluarga fungsi bijective ini:

fk(x):ZZk

Tantangan

Tentukan sekumpulan fungsi (di mana k adalah bilangan bulat positif) dengan properti yang f k ( x ) bijectively peta bilangan bulat untukfk(x)kfk(x) -tuples bilangan bulat.k

Kiriman Anda harus, diberikan masukan dan x , return f k ( xkx .fk(x)

Ini adalah , sehingga jawaban terpendek yang valid (diukur dalam byte) menang.

Spesifikasi

  • Setiap keluarga fk(x) dapat digunakan asalkan memenuhi kriteria di atas.
  • Anda disarankan untuk menambahkan deskripsi tentang cara kerja keluarga fungsi Anda, serta cuplikan untuk menghitung kebalikan dari fungsi tersebut (ini tidak termasuk dalam jumlah byte Anda).
  • Tidak apa-apa jika fungsi invers tidak dapat dihitung, selama Anda dapat membuktikannya, fungsi tersebut bersifat objektif.
  • Anda dapat menggunakan representasi yang sesuai untuk bilangan bulat yang ditandatangani dan daftar bilangan bulat yang ditandatangani untuk bahasa Anda, tetapi Anda harus mengizinkan masukan ke fungsi Anda untuk tidak dibatasi.
  • Anda hanya perlu mendukung nilai hingga 127.k

Apakah saya tetap bisa mengambil versi string kdan xbukannya bilangan bulat?
JungHwan Min

@JungHwanMin Strings yang mewakili angka input baik-baik saja.
Buah Esolanging

Jawaban:


19

Alice , 14 12 byte

/O
\i@/t&Yd&

Cobalah online!

Fungsi terbalik (tidak golf):

/o     Q
\i@/~~\ /dt&Z

Cobalah online!

Penjelasan

Alice memiliki bawaan bawaan antara ection dan 2 , yang dapat dihitung denganY(membongkar) dan kebalikannyaZ (mengepak). Berikut adalah kutipan dari dokumen yang menjelaskan tentang penambangan:

Detail dari bijection kemungkinan tidak relevan untuk kebanyakan kasus penggunaan. Poin utamanya adalah memungkinkan pengguna menyandikan dua bilangan bulat dalam satu dan mengekstrak dua bilangan bulat lagi di lain waktu. Dengan menerapkan perintah paket berulang kali, seluruh daftar atau susunan bilangan bulat dapat disimpan dalam satu nomor (meskipun tidak dengan cara yang efisien memori). Pemetaan yang dihitung oleh operasi paket adalah fungsi bijective 2 → ℤ (yaitu pemetaan satu-ke-satu). Pertama, bilangan bulat {..., -2, -1, 0, 1, 2, ...} dipetakan ke bilangan asli (termasuk nol) seperti {..., 3, 1, 0, 2, 4 , ...}(dengan kata lain, bilangan bulat negatif dipetakan ke natural ganjil dan non bilangan bulat -negative dipetakan ke bahkan alami). Dua bilangan alami kemudian dipetakan ke satu melalui pemasangan pasangan , yang menulis naturals di sepanjang diagonal kuadran pertama dari grid integer. Secara khusus, {(0,0), (1,0), (0,1), (2,0), (1,1), (0,2), (3,0), ...} adalah dipetakan ke {0, 1, 2, 3, 4, 5, 6, ...} . Bilangan alami yang dihasilkan kemudian dipetakan kembali ke bilangan bulat menggunakan kebalikan dari penambangan sebelumnya. Perintah unpack menghitung persis kebalikan dari pemetaan ini.

Seperti disinggung di atas, kita bisa menggunakan operasi unpack ini untuk memetakan ke k juga. Setelah menerapkannya ke integer awal, kita dapat kembali membongkar integer kedua dari hasilnya, yang memberi kita daftar tiga integer. Jadi aplikasi k-1Y memberi kita k bilangan bulat sebagai hasilnya.

Kita dapat menghitung kebalikannya dengan mengemas daftar Zdari awal.

Jadi program itu sendiri memiliki struktur ini:

/O
\i@/...d&

Ini hanya template dasar untuk program yang membaca jumlah variabel bilangan bulat desimal sebagai input dan mencetak nomor variabel sebagai hasilnya. Jadi kode aktualnya benar-benar adil:

t   Decrement k.
&   Repeat the next command k-1 times.
Y   Unpack.

Satu hal yang ingin saya sampaikan adalah "mengapa Alice memiliki bawaan untuk for → ℤ 2 , bukankah itu wilayah bahasa golf"? Seperti sebagian besar bawaan Alice yang lebih aneh, alasan utamanya adalah prinsip desain Alice bahwa setiap perintah memiliki dua makna, satu untuk mode Cardinal (integer) dan satu untuk mode Ordinal (string), dan kedua makna ini entah bagaimana harus dikaitkan dengan pemberian Mode kardinal dan ordinal perasaan bahwa mereka adalah alam semesta cermin di mana segala sesuatunya sama tetapi juga berbeda. Dan cukup sering saya memiliki perintah untuk salah satu dari dua mode yang ingin saya tambahkan, dan kemudian harus mencari tahu apa perintah lain untuk memasangkannya.

Dalam kasus Y dan Zmode Ordinal yang lebih dulu: saya ingin memiliki fungsi untuk interleave dua string (zip) dan memisahkannya lagi (unzip). Kualitas ini yang ingin saya tangkap dalam mode Kardinal adalah untuk membentuk satu bilangan bulat dari dua dan dapat mengekstraksi dua bilangan bulat lagi nanti, yang menjadikan penambangan semacam itu sebagai pilihan alami.

Saya juga berpikir bahwa ini sebenarnya akan sangat berguna di luar golf, karena memungkinkan Anda menyimpan seluruh daftar atau bahkan pohon bilangan bulat dalam satu unit memori (elemen tumpukan, sel pita atau sel kotak).


Penjelasan hebat seperti biasa
Luis Mendo

Menemukan Ydan Zdalam dokumen Alice sebenarnya yang mendorong saya untuk memposting tantangan ini (saya telah memikirkannya untuk sementara waktu, tetapi ini mengingatkan saya).
Buah Esolanging

11

Python, 96 93 byte

def f(k,x):
 c=[0]*k;i=0
 while x:v=(x+1)%3-1;x=x//3+(v<0);c[i%k]+=v*3**(i//k);i+=1
 return c

Ini bekerja pada prinsipnya dengan mengubah bilangan masukan xmenjadi terner seimbang , dan kemudian mendistribusikan trit (digit ternary) paling tidak signifikan terlebih dahulu antara koordinat yang berbeda dalam mode round-robin. Jadi k=2misalnya, setiap trit yang diposisikan genap akan berkontribusi pada xkoordinat, dan setiap trit yang diposisikan ganjil akan berkontribusi pada ykoordinat. Karena k=3Anda memiliki kontribusi trit pertama, keempat dan ketujuh (dll ...) x, sedangkan yang kedua, kelima dan kedelapan berkontribusi paday , dan ketiga, keenam dan kesembilan z.

Misalnya, dengan k=2, mari kita lihat x=35. Dalam terner seimbang, 35adalah 110T(menggunakan notasi artikel Wikipedia di mana Tmewakili -1angka). Membagi trit memberi 1T( trit pertama dan ketiga, dihitung dari kanan) untukx koordinat dan 10(trit kedua dan keempat) untuk ykoordinat. Mengubah setiap koordinat kembali ke desimal, kita dapatkan 2, 3.

Tentu saja, saya tidak benar-benar mengubah seluruh angka menjadi terner seimbang sekaligus dalam kode golf. Saya hanya menghitung satu trit pada satu waktu (dalam vvariabel), dan menambahkan nilainya langsung ke koordinat yang sesuai.

Berikut adalah fungsi terbalik yang tidak dikoleksi yang mengambil daftar koordinat dan mengembalikan nomor:

def inverse_f(coords):
    x = 0
    i = 0
    while any(coords):
        v = (coords[i%3]+1) % 3 - 1
        coords[i%3] = coords[i%3] // 3 + (v==-1)
        x += v * 3**i
        i += 1
    return x

fFungsi saya mungkin terkenal karena kinerjanya. Hanya menggunakan O(k)memori dan membutuhkan O(k) + O(log(x))waktu untuk menemukan hasilnya, sehingga dapat bekerja dengan nilai input yang sangat besar. Coba f(10000, 10**10000)misalnya, dan Anda akan mendapatkan jawaban cukup banyak langsung (menambahkan nol ekstra untuk eksponen sehingga xmerupakan 10**100000merek itu mengambil 30 detik atau lebih pada PC lama saya). Fungsi kebalikannya tidak secepat, sebagian besar karena sulit baginya untuk mengetahui kapan itu selesai (ia memindai semua koordinat setelah setiap perubahan, sehingga dibutuhkan O(k*log(x))waktu seperti itu). Ini mungkin bisa dioptimalkan menjadi lebih cepat, tetapi mungkin sudah cukup cepat untuk parameter normal.


Anda dapat menghapus spasi (baris baru) di dalam loop sementara
Tn. Xcoder

Terima kasih, saya secara keliru berpikir bahwa ada semacam konflik antara satu lingkaran dan penggunaan ;untuk pernyataan berantai pada satu baris.
Blckknght

9

Sekam , 10 byte

§~!oΠR€Θݱ

Cobalah online!

Fungsi invers juga 10 byte.

§o!ȯ€ΠRΘݱ

Cobalah online!

Penjelasan

Arah maju:

§~!oΠR€Θݱ  Implicit inputs, say k=3 and x=-48
        ݱ  The infinite list [1,-1,2,-2,3,-3,4,-4,..
       Θ    Prepend 0: [0,1,-1,2,-2,3,-3,4,-4,..
 ~    €     Index of x in this sequence: 97
§    R      Repeat the sequence k times: [[0,1,-1,..],[0,1,-1,..],[0,1,-1,..]]
   oΠ       Cartesian product: [[0,0,0],[1,0,0],[0,1,0],[1,1,0],[-1,0,0],[0,0,1],..
  !         Index into this list using the index computed from x: [-6,1,0]

Arah terbalik:

§o!ȯ€ΠRΘݱ  Implicit inputs, say k=3 and y=[-6,1,0]
     ΠRΘݱ  As above, k-wise Cartesian product of [0,1,-1,2,-2,..
   ȯ€       Index of y in this sequence: 97
§o!         Index into the sequence [0,1,-1,2,-2,.. : -48

Produk bawaan Cartesian Πberperilaku baik untuk daftar yang tidak terbatas, menghitung setiap k -tuple tepat sekali.


[[0,1,-1,..],[[0,1,-1,..],[[0,1,-1,..]]apakah bagian ini seharusnya [[0,1,-1,..],[0,1,-1,..],[0,1,-1,..]]?
Erik the Outgolfer

@EriktheOutgolfer Umm ya, perbaiki sekarang.
Zgarb

Ini indah. Sebagai seorang programmer J, apakah Anda tahu jika ada cara yang baik untuk mengubah solusi daftar malas seperti ini menjadi J, yang tidak mendukung mereka? ^:^:_solusi tipe biasanya berakhir jauh lebih rumit ...
Jonah

@Jonah saya tidak yakin. Anda bisa mencoba untuk menghitung array dari semua k -tuple dengan entri dari i: xdan mengurutkannya dengan jumlah nilai absolut, kemudian indeks ke dalamnya. Idenya adalah bahwa array ini adalah awalan dari satu "array tak terbatas" yang berisi semua k -tuple.
Zgarb

7

Bahasa Wolfram (Mathematica) , 61 byte

SortBy[Range[-(x=2Abs@#+Boole[#>=0]),x]~Tuples~#2,#.#&][[x]]&

Cobalah online!

(Mengambil bilangan bulat dan kemudian panjang tuple sebagai input.)

Terbalik:

If[OddQ[#],#-1,-#]/2&@Tr@Position[SortBy[Range[-(x=Ceiling@Norm@#),x]~Tuples~Length@#,#.#&],#]&

Cobalah online!

Bagaimana itu bekerja

Idenya mudah: kita mengubah input integer menjadi integer positif (dengan memetakan 0,1,2,3, ... menjadi 1,3,5,7, ... dan -1, -2, -3, ... ke 2,4,6, ...) dan kemudian indeks ke semua k -tuple, diurutkan berdasarkan jarak dari titik asal dan kemudian oleh putus tie-default standar Mathematica.

Tetapi kita tidak dapat menggunakan daftar tanpa batas, jadi ketika kita mencari n th k -tuple, kita hanya menghasilkan k -tuple bilangan bulat dalam rentang {- n , ..., n }. Ini dijamin cukup, karena n th terkecil k -tuple oleh norma memiliki norma kurang dari n , dan semua tupel dari norma n atau kurang termasuk dalam daftar ini.

Untuk kebalikannya, kita cukup membuat daftar k -tuple yang cukup panjang , menemukan posisi k -tuple yang diberikan dalam daftar itu, dan kemudian membalikkan operasi "lipat menjadi bilangan bulat positif".


2
Berjalan dengan input [15, 5]menabrak PC saya ...
JungHwan Min

2
Itu akan terjadi. Pada prinsipnya, algoritme berfungsi untuk apa saja, tetapi dalam kasus Anda ia bekerja dengan menghasilkan semua 5-tupel dari rentang {-31, .., 31} dan kemudian mengambil yang ke-31, jadi agak menghabiskan banyak memori.
Misha Lavrov

3

J, 7 Bytes

#.,|:#:

Kode J untuk melakukan ini sangat memalukan

Fungsi pemasangan yang sangat sederhana (atau, fungsi tupling) adalah dengan hanya menyisipkan digit dari ekspansi biner dari masing-masing angka. Jadi, misalnya (47, 79)akan dipasangkan seperti itu:

1_0_0_1_1_1_1
 1_0_1_1_1_1
-------------
1100011111111

atau, 6399. Jelas, kita dapat melakukan generalisasi secara sepele untuk n-tuple mana pun.

Mari kita periksa bagaimana ini bekerja kata kerja demi kata kerja.

#:adalah anti-basis dua, ketika digunakan secara monadik ia mengembalikan ekspansi biner dari angka. #: 47 79memberikan hasilnya:

0 1 0 1 1 1 1
1 0 0 1 1 1 1

|:adalah operator transpose, yang hanya memutar array. Rotasi hasil #: 47 79memberi:

0 1
1 0
0 0
1 1
1 1
1 1
1 1

Ketika digunakan secara monadik, ,adalah operator ravel, ia menghasilkan daftar 1 dimensi dari tabel:

0 1 1 0 0 0 1 1 1 1 1 1 1 1

Akhirnya, #.konversi kembali ekspansi biner, memberi kita hasilnya 6339.

Solusi ini akan bekerja untuk setiap string bilangan bulat.


7
Bagaimana cara kerjanya untuk angka negatif?
Neil

2

Perl 6 , 148 byte

my@s=map ->\n{|grep {n==abs any |$_},(-n..n X -n..n)},^Inf;my&f={$_==1??+*!!do {my&g=f $_-1;my@v=map {.[0],|g .[1]},@s;->\n{@v[n>=0??2*n!!-1-2*n]}}}

Cobalah online!

Tidak Disatukan:

sub rect($n) {
    grep ->[$x,$y] { abs($x|$y) == $n }, (-$n..$n X -$n..$n);
}

my @spiral = map { |rect($_) }, ^Inf;

sub f($k) {
    if ($k == 1) {
        -> $_ { $_ }
    } else {
        my &g = f($k-1);
        my @v = map -> [$x, $y] { $x, |g($y) }, @spiral;
        -> $_ { $_ >= 0 ?? @v[2*$_] !! @v[-1-2*$_] }
    }
}

Penjelasan:

  • rect($n)adalah fungsi pembantu yang menghasilkan koordinat titik integral di tepi persegi panjang dari koordinat (-$n,$n)ke ($n, $n).

  • @spiral adalah daftar malas yang tak terbatas dari titik-titik integral di tepi persegi panjang dengan ukuran yang meningkat, mulai dari 0.

  • f($k)mengembalikan fungsi yang merupakan bijection dari integer ke $k-tuple integer.

Jika $kadalah 1, fmengembalikan pemetaan identitas -> $_ { $_ }.

Jika tidak, &g adalah pemetaan yang diperoleh secara rekursif dari integer ke $k-1-tuple integer.

Kemudian, kami @spiralkeluar dari titik asal, dan pada setiap titik membentuk $k-tuple dengan mengambil koordinat X dan hasil rata panggilan gdengan koordinat Y. Pemetaan yang dibuat dengan malas ini disimpan dalam array@v .

@vberisi semua $k-tupel yang dimulai dengan indeks 0, jadi untuk memperluas pengindeksan ke bilangan bulat negatif, kami hanya memetakan input positif ke bilangan genap dan input negatif ke bilangan ganjil. Fungsi (penutupan) dikembalikan yang mencari elemen @vdengan cara ini.


2

JavaScript, 155 byte

f=k=>x=>(t=x<0?1+2*~x:2*x,h=y=>(g=(v,p=[])=>1/p[k-1]?v||t--?0:p.map(v=>v&1?~(v/2):v/2):[...Array(1+v)].map((_,i)=>g(v-i,[...p,i])).find(u=>u))(y)||h(y+1))(0)

Versi prettify:

k => x => {
  // Map input to non-negative integer
  if (x > 0) t = 2 * x; else t = 2 * -x - 1;
  // we try to generate all triples with sum of v
  g = (v, p = []) => {
    if (p.length === k) {
      if (v) return null;
      if (t--) return null;
      // if this is the t-th one we generate then we got it
      return p;
    }
    for (var i = 0; i <= v; i++) {
      var r = g(v-i, [...p, i]);
      if (r) return r;
    }
  }
  // try sum from 0 to infinity
  h = x => g(x) || h(x + 1);
  // map tuple of non-negative integers back
  return h(0).map(v => {
    if (v % 2) return -(v + 1) / 2
    else return v / 2;
  });
}
  • Pertama, kami memetakan semua bilangan bulat ke semua bilangan bulat non-negatif satu per satu:
    • jika n> 0 maka hasil = n * 2
    • jika tidak hasil = -n * 2 - 1
  • Kedua, kami memberi pesanan semua tupel dengan bilangan bulat non-negatif k-length:
    • menghitung jumlah semua elemen, yang lebih kecil lebih dulu
    • jika jumlah sama, bandingkan dari kiri ke kanan, yang lebih kecil lebih dulu
    • Sebagai hasilnya, kami mendapatkan peta untuk semua bilangan bulat non-negatif untuk tupel dengan k bilangan bulat non-negatif
  • Akhirnya, petakan bilangan bulat non-negatif dalam tuple yang diberikan pada langkah kedua ke semua bilangan bulat dengan rumus yang sama di langkah pertama

Saya pikir x<0?~x-x:x+xmenghemat 2 byte.
Neil

2

Bahasa Wolfram (Mathematica) , 107 byte

(-1)^#⌈#/2⌉&@Nest[{w=⌊(√(8#+1)-1)/2⌋;x=#-w(w+1)/2,w-x}~Join~{##2}&@@#&,{2Abs@#-Boole[#<0]},#2-1]&

Cobalah online!

Inverse, 60 byte

(-1)^#⌈#/2⌉&@Fold[+##(1+##)/2+#&,2Abs@#-Boole[#<0]&/@#]&

Cobalah online!

Penjelasan:

Z -> N0 via f(n) = 2n if n>=0 and -2n-1 if n<0

N0 -> N0 ^ 2 melalui kebalikan dari fungsi pemasangan

N0 -> N0 ^ k Berulang-ulang terapkan di atas ke nomor paling kiri sampai kita mendapatkan panjang k

N0 ^ k -> Z ^ k via f(n) = (-1)^n * ceil(n/2), elemen-bijaksana


Mathematica, 101 byte

(-1)^#⌈#/2⌉&@Nest[{a=#~IntegerExponent~2+1,#/2^a+1/2}~Join~{##2}&@@#&,{2Abs@#+Boole[#<=0]},#2-1]&

Mirip dengan di atas (menggunakan N bukan N0), tetapi menggunakan kebalikan dari bijection f: N ^ 2 -> N via f(a, b) = 2^(a - 1)(2b - 1)


Maksudmu ... tidak ada Mathematica bawaan untuk itu (ketika Alice memilikinya)? Saya terdiam.
JayCe

1

JavaScript, 112 byte

k=>x=>(r=Array(k).fill(''),[...`${x<0?2*~x+1:2*x}`].map((c,i,s)=>r[(s.length-i)%k]+=c),r.map(v=>v&1?~(v/2):v/2))
  1. ubah menjadi non-negatif
  2. (n * k + i) digit ke-ke-ke-i
  3. mengkonversi kembali

@HermanLauenstein tidak perlu mundur?
tsh

Saya pikir x<0?~x-x:x+xmenghemat 2 byte.
Neil

-5 byte menggunakan [...BT${x<0?~x-x:x+x}BT].reverse().map((c,i)=>r[i%k]+=c),(kredit ke @Neil untuk x<0?~x-x:x+x). .reverse()digunakan alih-alih (s.length-i)karena menghindari kebutuhan parameter tambahan ske yang pertama .map. Tidak perlu membalikkan kembali karena array sementara tidak digunakan lagi. (Saya belum mengujinya tetapi mungkin berhasil)
Herman L

Byte lain dapat disimpan dengan mengganti .fill('')dengan .fill(0), karena nol di depan tidak membuat perbedaan (setidaknya tidak saat diuji di Safari)
Herman L

@HermanLauenstein Apakah Anda sudah mencoba .fill`` ? Mungkin menyimpan beberapa byte lagi.
Neil


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.