Dapat random suitless


19

Saya memiliki data nyata yang saya gunakan untuk permainan kartu simulasi. Saya hanya tertarik pada jajaran kartu, bukan jas. Namun itu adalah dek kartu standar sehingga hanya ada dari setiap peringkat yang mungkin di dek. Dek dikocok dengan baik untuk masing-masing tangan, dan kemudian saya output seluruh dek ke file. Jadi hanya ada kemungkinan simbol dalam file output yang . ( = sepuluh peringkat). Jadi tentu saja kita dapat mengemas ini menggunakan bit per simbol, tetapi kemudian kita membuang dari kemungkinan penyandian. Kita dapat melakukan yang lebih baik jika kita mengelompokkan simbol sekaligus, dan kemudian mengompresnya, karena4 13 2 , 3 , 4 , 5 , 6 , 7 ,524132,3,4,5,6,7,8,9,T,J,Q,K,AT43164134 = dan itu bisa masuk agak "pas" menjadi bit, bukan . Batas bitpacking teoretis adalah log ( ) / log ( ) = untuk data dengan simbol acak untuk setiap kartu yang mungkin. Namun kita tidak dapat memiliki raja misalnya di dek ini. Kami HARUS memiliki hanya dari setiap peringkat di setiap dek sehingga pengodean entropi turun sekitar setengah bit per simbol menjadi sekitar .28,56115161323.70044135243.2

Ok, jadi inilah yang saya pikirkan. Data ini tidak sepenuhnya acak. Kita tahu ada peringkat masing-masing sehingga di setiap blok kartu (sebut saja setumpuk kartu), sehingga kita dapat membuat beberapa asumsi dan optimisasi. Salah satunya adalah kita tidak harus menyandikan kartu terakhir, karena kita akan tahu apa yang seharusnya. Penghematan lain adalah jika kita berakhir pada peringkat tunggal; misalnya, jika kartu terakhir di dek adalah , kita tidak perlu menyandikannya karena decoder akan menghitung kartu hingga saat itu dan melihat bahwa semua peringkat lainnya telah terisi, dan akan mengasumsikan " kartu yang hilang "semuanya detik.452377737

Jadi pertanyaan saya ke situs ini adalah, optimasi apa yang mungkin dilakukan untuk mendapatkan file output yang lebih kecil pada tipe data ini, dan jika kita menggunakannya, dapatkah kita mengalahkan entropi bitpacking teoritis (sederhana) sebesar bit per simbol, atau bahkan mendekati batas entropi akhir sekitar bit per simbol rata-rata? Jika ya, bagaimana caranya?3.700443.2

Ketika saya menggunakan program jenis ZIP (WinZip misalnya), saya hanya melihat tentang kompresi , yang memberitahu saya itu hanya melakukan bitpack "malas" menjadi bit. Jika saya "pra-kompres" data menggunakan bitpacking saya sendiri, sepertinya lebih baik, karena ketika saya menjalankannya melalui program zip, saya mendapatkan sedikit lebih dari kompresi 2: 1 . Apa yang saya pikirkan adalah, mengapa tidak melakukan semua kompresi sendiri (karena saya memiliki lebih banyak pengetahuan tentang data daripada program Zip). Saya bertanya-tanya apakah saya dapat mengalahkan "batas" entropi dari log ( 13 ) / log ( 2 ) = 3.700442:142:11323.70044. Saya kira saya bisa dengan beberapa "trik" yang saya sebutkan dan beberapa lagi saya mungkin bisa mengetahuinya. File keluaran tentu saja tidak harus "dapat dibaca manusia". Selama pengkodean lossless itu valid.

Berikut ini tautan ke 3 juta deck shuffled yang dapat dibaca manusia ( 1 per baris). Siapa pun dapat "berlatih" pada sebagian kecil dari baris ini dan kemudian membiarkannya merobek seluruh file. Saya akan terus memperbarui filesize (terkecil) terbaik berdasarkan data ini.

https://drive.google.com/file/d/0BweDAVsuCEM1amhsNmFITnEwd2s/view

Ngomong-ngomong, jika Anda tertarik pada jenis permainan kartu apa yang digunakan untuk data ini, berikut adalah tautan ke pertanyaan aktif saya (dengan poin karunia). Saya diberitahu itu adalah masalah yang sulit untuk dipecahkan (tepatnya) karena akan membutuhkan sejumlah besar ruang penyimpanan data. Beberapa simulasi setuju dengan perkiraan probabilitasnya. Belum ada solusi matematis murni yang disediakan. Kurasa terlalu sulit.300

/math/1882705/probability-2-player-card-game-with-multiple-patterns-to-win-who-has-the- keuntungan

Saya memiliki algoritma yang baik yang menunjukkan bit untuk menyandikan dek pertama dalam data sampel saya. Data ini dihasilkan secara acak menggunakan algoritma shuffle Fisher-Yates. Ini adalah data acak nyata, jadi algoritme yang baru saya buat tampaknya berfungsi SANGAT baik, yang membuat saya bahagia.168

Mengenai kompresi "tantangan", saya saat ini sekitar 160 bit per deck. Saya pikir saya bisa turun ke mungkin 158. Ya saya mencoba dan saya mendapat 158,43 bit per deck. Saya pikir saya semakin mendekati batas algoritma saya jadi saya berhasil turun di bawah 166 bit per deck tapi saya gagal mendapatkan 156 bit yang akan menjadi 3 bit per kartu tapi itu latihan yang menyenangkan. Mungkin di masa depan saya akan memikirkan sesuatu untuk mengurangi rata-rata setiap deck sebesar 2,43 bit atau lebih.


8
Jika Anda membuat sendiri deck yang dikocok ini (daripada menggambarkan keadaan setumpuk kartu secara fisik, misalnya), Anda tidak perlu menyimpan deck sama sekali - cukup simpan seed RNG yang menghasilkan deck.
jasonharper

3
Deskripsi dan jawaban Anda sangat mirip dengan konsep yang umumnya dikenal sebagai pengkodean rentang ( en.wikipedia.org/wiki/Range_encoding ). Anda menyesuaikan propabilitas setelah setiap kartu sehingga mencerminkan kemungkinan kartu yang tersisa.
H. Idden

Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Gilles 'SANGAT berhenti menjadi jahat'

Jawaban:


3

Hal lain yang perlu dipertimbangkan: jika Anda hanya peduli mengompres set lengkap beberapa juta deck dan Anda juga tidak peduli dengan urutannya, Anda dapat memperoleh fleksibilitas pengodean tambahan dengan membuang informasi tentang pemesanan set set deck. . Ini akan menjadi kasus, misalnya, jika Anda perlu memuat set untuk menghitung semua deck dan memprosesnya, tetapi tidak peduli urutan apa mereka diproses.

Anda mulai dengan menyandikan setiap deck secara terpisah, karena jawaban lain telah menjelaskan caranya. Lalu, urutkan nilai yang disandikan itu. Menyimpan serangkaian perbedaan antara nilai kode yang diurutkan (di mana perbedaan pertama dimulai dari dek yang disandikan '0'). Dengan sejumlah besar deck, perbedaannya akan cenderung lebih kecil dari rentang encoding penuh, sehingga Anda dapat menggunakan beberapa bentuk pengkodean varint untuk menangani perbedaan besar sesekali sambil tetap menyimpan perbedaan yang lebih kecil secara efisien. Skema varint yang tepat akan tergantung pada berapa banyak deck yang Anda miliki di set (sehingga menentukan ukuran perbedaan rata-rata.)

Sayangnya saya tidak tahu matematika berapa banyak ini akan membantu kompresi Anda, tetapi berpikir ide ini mungkin berguna untuk dipertimbangkan.


1
Sangat kasar berbicara, jika Anda memiliki beberapa juta deck acak maka perbedaan rata-rata akan menjadi satu (beberapa juta) dari kisaran penuh, yang berarti bahwa Anda berharap untuk menghemat sekitar 20-bit bit per nilai. Anda kehilangan sedikit untuk pengkodean varint Anda.
Steve Jessop

2
@ DavidJames: jika urutan spesifik deck tidak penting, hanya saja tidak ada bias di dalamnya, Anda dapat mengocok 3 juta deck setelah dekompresi (yaitu jangan mengubah salah satu deck, hanya mengubah urutan daftar 3 juta deck).
Steve Jessop

2
Ini hanya cara untuk mengurangi konten informasi sedikit lebih jauh jika informasi pemesanan tidak penting; jika ini penting, ini tidak berlaku dan dapat diabaikan. Yang mengatakan, jika satu-satunya kepentingan untuk pemesanan set deck adalah bahwa itu 'acak', Anda bisa mengacak urutan setelah dekompresi, seperti yang dinyatakan @SteveJessop.
Dan Bryant

@ DavidJames Melihat bahwa 173 pertama deck Anda mulai dengan KKKK, dan tidak melihat beberapa juta lainnya, dan menyimpulkan bahwa mereka semua mulai dengan KKKK, adalah hal yang sangat bodoh untuk dilakukan. Terutama jika mereka jelas dalam urutan yang diurutkan.
user253751

3
@ DavidJames: data ini dikompresi, dan rutin dekompresi dapat mengacak kembali jika diinginkan. "Seseorang yang naif" tidak akan mendapatkan apa-apa, mereka bahkan tidak akan menemukan cara menafsirkannya sebagai setumpuk kartu. Ini bukan cacat dalam format penyimpanan data (dalam hal ini format lossy), bahwa seseorang yang menggunakannya perlu RTFM untuk mendapatkan data yang benar.
Steve Jessop

34

Berikut ini adalah algoritma lengkap yang mencapai batas teoritis.

Prolog: Mengkode urutan integer

A 13-integer sequence "integer dengan batas atas , integer dengan batas atas b - 1 ," integer dengan batas atas c - 1 , integer dengan batas atas d - 1 , ... integer dengan batas atas m - 1 " selalu dapat dikodekan dengan efisiensi sempurna.a1b1c1d1m1

  1. Ambil bilangan bulat pertama, kalikan dengan , tambahkan yang kedua, kalikan hasilnya dengan c , tambahkan yang ketiga, kalikan hasilnya dengan d , ... kalikan hasilnya dengan m , tambahkan ketigabelas - dan itu akan menghasilkan angka unik antara 0 dan a b c d e f g h i j k l m - 1 .bcdm0abcdefghijklm1
  2. Tuliskan angka itu dalam biner.

Kebalikannya juga mudah. Bagi dengan dan sisanya adalah bilangan bulat ketiga belas. Bagilah hasilnya dengan l dan sisanya adalah integer kedua belas. Lanjutkan sampai Anda dibagi dengan b : sisanya adalah bilangan bulat kedua dan hasil bagi adalah bilangan bulat pertama.mlb

Jadi untuk mengkode kartu Anda dengan cara terbaik, yang harus kita lakukan adalah menemukan korespondensi yang sempurna antara urutan 13-integer (dengan batas atas yang diberikan) dan pengaturan kartu yang dikocok.

Inilah cara melakukannya.

Korespondensi antara pengocokan dan urutan bilangan bulat

Mulailah dengan urutan 0 kartu di atas meja di depan Anda.

Langkah 1

Ambil empat 2 dalam paket Anda dan letakkan di atas meja.

Pilihan apa yang Anda miliki? Sebuah kartu atau kartu dapat ditempatkan di awal urutan yang sudah ada di atas meja, atau setelah salah satu kartu di urutan itu. Dalam hal ini, ini berarti ada tempat yang memungkinkan untuk meletakkan kartu.1+0=1

Jumlah total cara menempatkan 4 kartu di 1 tempat adalah . Encode masing-masing cara itu sebagai angka antara 0 dan 1 - 1 . Ada 1 nomor tersebut.1011

Saya mendapat 1 dengan mempertimbangkan cara-cara menulis 0 sebagai jumlah dari 5 bilangan bulat: itu adalah .4×3×2×14!

Langkah 2

Ambil empat 3 dalam paket Anda dan letakkan di atas meja.

Pilihan apa yang Anda miliki? Sebuah kartu atau kartu dapat ditempatkan di awal urutan yang sudah ada di atas meja, atau setelah salah satu kartu di urutan itu. Dalam hal ini, ini berarti ada tempat yang memungkinkan untuk meletakkan kartu.1+4=5

Jumlah total cara menempatkan 4 kartu di 5 tempat adalah . Encode masing-masing cara itu sebagai angka antara 0 dan 70 - 1 . Ada 70 angka seperti itu.700701

Saya mendapat 70 dengan mempertimbangkan cara-cara menulis 4 sebagai jumlah dari 5 bilangan bulat: itu adalah .8×7×6×54!

Langkah 3

Ambil empat 4s dalam kemasan Anda dan letakkan di atas meja.

Pilihan apa yang Anda miliki? Sebuah kartu atau kartu dapat ditempatkan di awal urutan yang sudah ada di atas meja, atau setelah salah satu kartu di urutan itu. Dalam hal ini, ini berarti ada tempat yang memungkinkan untuk meletakkan kartu.1+8=9

Total cara menempatkan 4 kartu di 9 tempat adalah . Encode masing-masing cara itu sebagai angka antara 0 dan 495 - 1 . Ada 495 angka seperti itu.49504951

Saya mendapat 495 dengan mempertimbangkan cara-cara menulis 8 sebagai jumlah dari 5 bilangan bulat: itu adalah .12×11×10×94!

Dan seterusnya, sampai ...

Langkah 13

Ambil empat ace dalam paket Anda dan letakkan di atas meja.

Pilihan apa yang Anda miliki? Sebuah kartu atau kartu dapat ditempatkan di awal urutan yang sudah ada di atas meja, atau setelah salah satu kartu di urutan itu. Dalam hal ini, ini berarti ada tempat yang memungkinkan untuk meletakkan kartu.1+48=49

Jumlah total cara menempatkan 4 kartu di 49 tempat adalah . Encode masing-masing cara itu sebagai angka antara 0 dan 270725 - 1 . Ada 270725 angka seperti itu.27072502707251

Saya mendapat 270725 dengan mempertimbangkan cara-cara menulis 48 sebagai jumlah 5 bilangan bulat: .52×51×50×494!


Prosedur ini menghasilkan korespondensi 1-ke-1 antara (a) pengocokan kartu di mana Anda tidak peduli dengan suit dan (b) urutan bilangan bulat di mana yang pertama adalah antara dan 1 - 1 , yang kedua adalah antara 0 dan 1 70 - 1 , ketiga adalah antara 0 dan 495 - 1 , dan seterusnya sampai ketiga belas, yaitu antara 0 dan 270.725 - 1 .01107010495102707251

Mengacu pada "Pengkodean urutan bilangan", Anda dapat melihat bahwa urutan bilangan bulat tersebut berada dalam korespondensi 1-1 dengan angka antara dan ( 1 × 70 × 495 × × 270725 ) - 1 . Jika Anda melihat ekspresi "produk dibagi dengan faktorial" dari masing-masing bilangan bulat ( seperti yang dijelaskan dalam huruf miring pada akhir setiap langkah ) maka Anda akan melihat bahwa ini berarti angka antara 0 dan 52 !0(1×70×495××270725)10yang jawaban saya sebelumnya menunjukkan adalah yang terbaik mungkin.

52!(4!)131,

Jadi kami memiliki metode yang sempurna untuk mengompresi kartu Anda yang dikocok.


Algoritma

Precompute daftar semua cara penulisan 0 sebagai jumlah dari 5 bilangan bulat, dari penulisan 4 sebagai jumlah dari 5 bilangan bulat, dari penulisan 8 sebagai jumlah dari 5 bilangan bulat, ... dari penulisan 48 sebagai jumlah dari 5 bilangan bulat. Daftar terpanjang memiliki 270.725 elemen, sehingga tidak terlalu besar. (Precomputation tidak sepenuhnya diperlukan karena Anda dapat dengan mudah mensintesis setiap daftar sebagai dan ketika Anda membutuhkannya: mencoba dengan Microsoft QuickBasic, bahkan melalui daftar elemen 270725 lebih cepat daripada yang bisa dilihat mata)

Untuk beralih dari pengacakan ke urutan bilangan bulat:

2s tidak berkontribusi apa-apa, jadi mari kita abaikan saja. Tulis angka antara 0 dan 1-1.

The 3s: Berapa banyak 2s yang ada sebelum 3 pertama? Berapa banyak sebelum yang kedua? ketiga? tanggal 4? setelah tanggal 4? Jawabannya adalah 5 bilangan bulat yang jelas menambahkan hingga 4. Jadi, perhatikan urutan 5 bilangan bulat di daftar "menulis 4 sebagai jumlah dari 5 bilangan bulat", dan perhatikan posisinya di daftar itu. Itu akan menjadi angka antara 0 dan 70-1. Tuliskan.

4s: Ada berapa 2s atau 3s sebelum 4 yang pertama? Berapa banyak sebelum yang kedua? ketiga? tanggal 4? setelah tanggal 4? Jawabannya adalah 5 bilangan bulat yang jelas menambahkan hingga 8. Jadi, perhatikan urutan 5 bilangan bulat di dalam daftar "menulis 8 sebagai jumlah dari 5 bilangan bulat", dan perhatikan posisinya di daftar itu. Itu akan menjadi angka antara 0 dan 495-1. Tuliskan.

Dan seterusnya, sampai ...

Kartu As: Berapa banyak kartu non-kartu As yang ada sebelum kartu As pertama? Berapa banyak sebelum yang kedua? ketiga? tanggal 4? setelah tanggal 4? Jawabannya adalah 5 bilangan bulat yang jelas menambahkan hingga 48. Jadi, perhatikan urutan 5 bilangan bulat di dalam daftar "menulis 48 sebagai jumlah dari 5 bilangan bulat", dan perhatikan posisinya di daftar itu. Itu akan menjadi angka antara 0 dan 270725-1. Tuliskan.

Anda sekarang telah menuliskan 13 bilangan bulat. Encode mereka (seperti yang dijelaskan sebelumnya) menjadi satu nomor antara dan 52 !0 . Tuliskan angka itu dalam biner. Ini akan memakan waktu hanya di bawah 166 bit.52!(4!)13

Ini adalah kompresi terbaik, karena mencapai batas informasi-teoritis.

Dekompresi mudah: pergi dari angka besar ke urutan 13 bilangan bulat, dan kemudian menggunakannya untuk membangun urutan kartu seperti yang sudah dijelaskan.


Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
DW

Solusi ini tidak jelas bagi saya dan tidak lengkap. Itu tidak menunjukkan bagaimana untuk benar-benar mendapatkan nomor 166 bit dan mendekodekannya kembali ke dek. Sama sekali tidak mudah untuk hamil bagi saya jadi saya tidak tahu bagaimana menerapkannya. Formula langkah Anda pada dasarnya hanya membongkar formula menjadi 13 bagian yang benar-benar tidak banyak membantu saya. Saya pikir itu akan membantu jika Anda membuat diagram atau bagan untuk mungkin langkah 2 dengan 70 cara yang mungkin untuk mengatur kartu. Solusi Anda terlalu abstrak untuk diterima dan diproses oleh otak saya. Saya lebih suka contoh dan ilustrasi aktual. 52!/(4!13)13
David James

23

Daripada mencoba menyandikan setiap kartu secara terpisah menjadi 3 atau 4 bit, saya sarankan Anda menyandikan status seluruh dek menjadi 166 bit. Seperti yang dijelaskan Martin Kochanski , ada kurang dari kemungkinan pengaturan kartu yang mengabaikan pakaian, sehingga itu berarti keadaan seluruh dek dapat disimpan dalam 166 bit.2166

Bagaimana Anda melakukan kompresi dan dekompresi ini secara algoritmik, dengan cara yang efisien? Saya sarankan menggunakan pemesanan leksikografis dan pencarian biner. Ini akan memungkinkan Anda untuk melakukan kompresi dan dekompresi secara efisien (dalam ruang dan waktu), tanpa memerlukan tabel pencarian besar atau asumsi tidak realistis lainnya.

Secara lebih rinci: Mari kita memesan deck dengan menggunakan urutan leksikografis pada representasi dek yang tidak terkompresi, yaitu, sebuah dek diwakili dalam bentuk yang tidak terkompresi sebagai string seperti 2222333344445555666677778888999999TTTTJJJJQQQQKKKKKAAAA; Anda dapat memesannya sesuai dengan urutan leksikografis. Sekarang, anggaplah Anda memiliki prosedur yang memberikan deck , menghitung jumlah deck yang datang sebelumnya (dalam urutan leksikografis). Kemudian Anda dapat menggunakan prosedur ini untuk mengompres sebuah dek: diberikan sebuah dek D , Anda kompres ke angka 166-bit dengan menghitung jumlah deck yang datang sebelumnya dan kemudian menghasilkan angka itu. Angka itu adalah representasi terkompresi dari dek.DD

Untuk mendekompres, gunakan pencarian biner. Diberi nomor , Anda ingin menemukan dek ke- n dalam urutan leksikografis semua dek. Anda dapat melakukan ini menggunakan prosedur di sepanjang baris pencarian biner: pilih satu deck D 0 , hitung jumlah deck sebelum D 0 , dan bandingkan dengan n . Itu akan memberi tahu Anda apakah akan menyesuaikan D 0nnD0D0nD0datang lebih awal atau lambat. Saya sarankan Anda mencoba secara iteratif mendapatkan simbol dengan benar: jika Anda ingin memulihkan string seperti 22223333444455556666777788889999TTTJJJJQQQQKKKKAAAA, pencarian pertama untuk menemukan apa yang akan digunakan sebagai simbol pertama dalam string (coba saja semua 12 kemungkinan, atau gunakan pencarian biner dari 12 kemungkinan) ), lalu ketika Anda telah menemukan nilai yang tepat untuk simbol pertama, cari untuk menemukan simbol kedua, dan seterusnya.

Semua yang tersisa adalah untuk datang dengan prosedur yang efisien untuk menghitung jumlah deck yang datang leksikografi sebelum . Ini terlihat seperti latihan kombinatorial yang langsung tetapi membosankan. Secara khusus, saya sarankan Anda membuat subrutin untuk masalah berikut: diberi awalan (seperti 222234), hitung jumlah deck yang dimulai dengan awalan itu. Jawaban untuk masalah ini terlihat seperti latihan yang cukup mudah dalam koefisien binomial dan faktorial. Kemudian, Anda dapat meminta subrutin ini sejumlah kecil kali untuk menghitung jumlah deck yang datang sebelum D .DD


Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
DW

8

Jumlah kemungkinan pengaturan kartu yang mengabaikan setelan adalah yang basis logaritma 2-nya adalah 165.976, atau 3.1919 bit per kartu, yang lebih baik dari batas yang Anda berikan.

52!(4!)13,

Pengkodean "bit per kartu" apa pun yang diperbaiki tidak akan masuk akal karena, seperti yang Anda perhatikan, kartu terakhir selalu dapat dikodekan dalam bit dan dalam banyak kasus beberapa kartu terakhir juga dapat dikodekan . Itu berarti bahwa untuk beberapa cara menuju "ekor" paket jumlah bit yang dibutuhkan untuk setiap kartu akan jauh lebih sedikit daripada yang Anda pikirkan.0

Sejauh ini cara terbaik untuk mengompresi data adalah dengan menemukan 59 bit data lain yang ingin Anda kemas dengan data kartu Anda (sebenarnya, 59,6 bit) dan, menulis 59 bit tersebut sebagai modulo angka 13-digit 13 (= ), Tetapkan satu suit untuk setiap kartu (satu digit memilih antara 4 ! Cara menetapkan suit ke kartu As, yang lain melakukan hal yang sama untuk raja-raja, dan seterusnya). Maka Anda memiliki paket 52 kartu yang sepenuhnya berbeda. 52 ! kemungkinan bisa dikodekan dalam 225,58 bit memang sangat mudah.4!4!52!

Tetapi melakukannya tanpa mengambil kesempatan untuk mengkodekan bit-bit tambahan itu juga mungkin sampai batas tertentu, dan saya akan memikirkannya karena saya yakin semua orang memilikinya. Terima kasih atas masalah yang sangat menarik!


1
Bisakah pendekatan yang mirip dengan pencurian ciphertext digunakan di sini? Seperti pada, data yang Anda enkode dalam 59 bit tambahan tersebut adalah 59 bit terakhir dari representasi yang disandikan?
John Dvorak

@ JanD, saya berpikir untuk menyelidiki sesuatu seperti ini. Tapi kemudian ternyata ada algoritma yang mencapai batas teoretis dan langsung dan 100% andal, sehingga tidak ada gunanya mencari lebih jauh.
Martin Kochanski

@ MartinKochanski - Saya tidak akan mengatakannya sebagai "mengabaikan pakaian" karena kita masih menghormati standar 4 setelan per peringkat. Kata-kata yang lebih baik mungkin "Jumlah pengaturan yang mungkin berbeda dari geladak adalah" ...
David James

3

Ini adalah masalah yang sudah lama terpecahkan.

Ketika Anda memberikan setumpuk 52 kartu, setiap kartu yang Anda tangani memiliki satu hingga 13 peringkat dengan probabilitas yang diketahui. Probabilitas berubah dengan setiap kartu dibagikan. Itu ditangani secara optimal menggunakan teknik kuno yang disebut pengkodean aritmatika adaptif, suatu perbaikan untuk pengkodean Huffman. Biasanya ini digunakan untuk probabilitas yang diketahui dan tidak berubah, tetapi bisa juga digunakan untuk mengubah probabilitas. Baca artikel wikipedia tentang kode aritmatika:

https://en.wikipedia.org/wiki/Arithmetic_coding


Oke tapi ini tidak menjawab pertanyaan saya jika bisa mendekati, mencocokkan, atau mengalahkan batas pengkodean entropi teoritis. Tampaknya karena ada n deck yang mungkin masing-masing dengan probabilitas 1 / n, maka enkode entropi adalah batasnya dan kita tidak dapat melakukan yang lebih baik (kecuali kita "menipu" dan memberi tahu decoder sesuatu tentang data input ke enkoder sebelumnya.
David James

3

Baik DW dan Martin Kochanski telah menjelaskan algoritma untuk membangun sebuah penambangan antara kesepakatan dan bilangan bulat dalam kisaran , tetapi sepertinya tidak satu pun dari mereka telah mengurangi masalah menjadi bentuk yang paling sederhana. (Catatan 1)[0,52!(4!)13)

Misalkan kita memiliki (parsial) dek dijelaskan oleh daftar memerintahkan , di mana sebuah i adalah jumlah kartu jenis i . Dalam OP, dek awal dijelaskan oleh daftar 13 elemen, masing-masing dengan nilai 4. Jumlah pengocokan berbeda dari dek tersebut adalahaaii

c(a)=(ai)!ai!

yang merupakan generalisasi sederhana dari koefisien binomial, dan memang dapat dibuktikan dengan hanya mengatur objek satu jenis pada satu waktu, seperti yang disarankan oleh Martin Kochanski. (Lihat di bawah, catatan 2)

Sekarang, untuk setiap seperti (parsial) dek, kita dapat memilih shuffle satu kartu pada satu waktu, menggunakan yang satu i > 0 . Jumlah pengocokan unik dimulai dengan i adalahiai>0i

{0if ai=0c(a1,...,ai1,ai1,ai+1,...,an)if ai>0.

dan dengan formula di atas, kita punya

c(a1,...,ai1,ai1,ai+1,...,an)=aic(a)ai

Kami kemudian dapat mengulang (atau mengulangi) melalui dek sampai shuffle selesai dengan mengamati bahwa jumlah pengocokan yang sesuai dengan awalan secara leksikografis lebih kecil dari awalan hingga adalahi

c(a)j=1iajj=1naj

Saya menulis ini dengan Python untuk menggambarkan algoritma; Python adalah pseudocode yang masuk akal. Perhatikan bahwa sebagian besar aritmatika melibatkan ketelitian yang diperluas; nilai (mewakili ordinal shuffle) dan n (jumlah total kemungkinan pengocokan untuk dek parsial yang tersisa) adalah bignum 166-bit. Untuk menerjemahkan kode ke bahasa lain, perlu menggunakan semacam perpustakaan bignum.kn

Juga, saya hanya menggunakan daftar bilangan bulat daripada nama kartu, dan - tidak seperti matematika di atas - bilangan bulat berbasis 0.

Untuk menyandikan shuffle, kami berjalan melalui shuffle, mengumpulkan pada setiap titik jumlah shuffle yang dimulai dengan kartu yang lebih kecil menggunakan rumus di atas:

from math import factorial
T = factorial(52) // factorial(4) ** 13

def encode(vec):
    a = [4] * 13
    cards = sum(a)
    n = T
    k = 0
    for idx in vec:
        k += sum(a[:idx]) * n // cards
        n = a[idx] * n // cards
        a[idx] -= 1
        cards -= 1
    return k

Decoding angka 166-bit adalah kebalikan sederhana. Pada setiap langkah, kami memiliki deskripsi sebagian deck dan ordinal; kita perlu melompati shuffles yang dimulai dengan kartu yang lebih kecil dari yang sesuai dengan ordinal, dan kemudian kita menghitung output kartu yang dipilih, mengeluarkannya dari deck yang tersisa, dan menyesuaikan jumlah kemungkinan shuffle dengan awalan yang dipilih:

def decode(k):
    vec = []
    a = [4] * 13
    cards = sum(a)
    n = T
    while cards > 0:
        i = cards * k // n
        accum = 0
        for idx in range(len(a)):
            if i < accum + a[idx]:
                k -= accum * n // cards
                n = a[idx] * n // cards
                a[idx] -= 1
                vec.append(idx)
                break
            accum += a[idx]
        cards -= 1
    return vec

Saya tidak melakukan upaya nyata untuk mengoptimalkan kode di atas. Saya menjalankannya terhadap seluruh file 3mil.TXT, memeriksa yang encode(decode(line))menghasilkan pengkodean asli; hanya butuh kurang dari 300 detik. (Tujuh baris dapat dilihat pada tes online pada ideone .) Menulis ulang dalam bahasa tingkat rendah dan mengoptimalkan pembagian (yang mungkin) mungkin akan mengurangi waktu itu menjadi sesuatu yang dapat dikelola.

Karena nilai yang dikodekan hanyalah bilangan bulat, itu bisa menjadi output dalam 166 bit. Tidak ada nilai dalam menghapus nol terkemuka, karena kemudian tidak akan ada cara untuk mengetahui di mana pengkodean diakhiri, jadi itu benar-benar pengkodean 166-bit.

Namun, perlu dicatat bahwa dalam aplikasi praktis, mungkin tidak perlu untuk menyandikan shuffle; acak acak dapat dihasilkan dengan menghasilkan nomor acak 166-bit dan mendekodekannya. Dan tidak benar-benar perlu bahwa semua 166 bit menjadi acak; akan mungkin untuk, misalnya, mulai dengan integer acak 32-bit dan kemudian mengisi 166 bit menggunakan RNG standar apa pun yang diunggulkan dengan angka 32-bit. Jadi, jika tujuannya hanya untuk dapat secara acak menyimpan sejumlah besar acak acak, Anda dapat mengurangi persyaratan penyimpanan per-kesepakatan secara kurang lebih.

Jika Anda ingin menyandikan sejumlah besar dari transaksi aktual (dibuat dengan cara lain) tetapi tidak peduli dengan urutan penawaran, Anda dapat delta-menyandikan daftar angka yang diurutkan, menghemat kira-kira log 2 N bit untuk masing-masing jumlah. (Penghematan hasil dari fakta bahwa urutan diurutkan memiliki lebih sedikit entropi daripada urutan tidak disortir. Itu tidak mengurangi entropi dari nilai tunggal dalam urutan.)Nlog2N

Dengan asumsi bahwa kita perlu untuk mengkodekan daftar diurutkan k nomor-bit, kita dapat melanjutkan sebagai berikut:N k

  1. Pilih sebagai bilangan bulat dekat dengan log 2 N (baik lantai atau langit-langit akan bekerja; saya biasanya pergi untuk langit-langit).halcatatan2N

  2. Kami secara implisit membagi rentang angka menjadi interval dengan awalan biner. Setiap k nomor-bit dibagi menjadi p -bit awalan dan k - p -bit akhiran; kami hanya menuliskan sufiks (berurutan). Ini membutuhkan N ( k - p ) bit.2halkhalk-halN(k-hal)

  3. Selain itu, kami membuat bit-sequence: Untuk masing-masing awalan (kecuali awalan 0 ) kami menuliskan 0 untuk setiap nomor dengan awalan itu (jika ada) diikuti oleh 1 . Urutan ini jelas memiliki 2 p + N bit: 2 p 1 s dan N 0 s.2hal0012hal+N2hal 1N 0

Untuk memecahkan kode angka kita mulai penghitung awalan pada 0, dan lanjutkan untuk bekerja melalui urutan bit. Ketika kita melihat , kita menampilkan awalan saat ini dan akhiran berikutnya dari daftar akhiran; ketika kita melihat 1 , kita menambah awalan saat ini.01

Panjang total pengkodean adalah yang sangat dekat dengan N ( k - p ) + N + N , atau N ( k - p + 2 ) , untuk rata-rata dari k - p + 2 bit per nilai.N(k-hal)+N+2halN(k-hal)+N+NN(k-hal+2)k-hal+2

Catatan

  1. adalah9202424223027104035710832080187204484475000000000000, danlog252!52!(4!)139202424223027104035710832080187204484475000000000000 kira-kira165,9765. Dalam teks, saya kadang-kadang berpura-pura bahwa logaritma base-2 benar-benar166; dalam hal menghasilkan ordinals acak dalam jangkauan, algoritma penolakan dapat digunakan yang hanya akan sangat jarang menolak nomor acak yang dihasilkan.catatan252!(4!)13165.9765166
  2. Untuk kenyamanan, saya menulis untuk n Σ i = k a i ; maka a 1 objek tipe 1 dapat ditempatkan di ( S 1Sksaya=knSebuahsayaSebuah11cara, dan kemudian objek tipe2dapat ditempatkan di(S2(S1Sebuah1)2cara, dan sebagainya. Sejak ( Si(S2Sebuah2), yang mengarah ke jumlah total(SsayaSebuahsaya)=Ssaya!Sebuahsaya!(Ssaya-Sebuahsaya)!=Ssaya!Sebuahsaya!Ssaya+1!

saya=1nSsaya!saya=1nSebuahsaya!Ssaya+1!

yang menyederhanakan rumus di atas.


Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
DW

@rici - Saya memberi Anda +100 karunia karena Anda menjelaskan jawaban Anda dalam apa yang tampak seperti presentasi yang lebih baik termasuk kode sedangkan jawaban lainnya lebih abstrak / teoretis, meninggalkan beberapa detail tentang bagaimana sebenarnya menerapkan encode / decode. Seperti yang Anda ketahui, ada banyak detail saat menulis kode. Saya akui algoritme saya bukan yang paling mudah, sederhana, mudah dimengerti, tetapi saya benar-benar membuatnya bekerja tanpa banyak usaha dan seiring waktu saya dapat membuatnya bekerja lebih cepat dengan lebih banyak kompresi. Jadi terima kasih atas jawaban Anda dan terus bekerja dengan baik.
David James

2

Sebagai solusi alternatif untuk masalah ini, algoritma saya menggunakan bit pecahan majemuk (non integer) per kartu untuk kelompok kartu di dek berdasarkan pada berapa banyak peringkat yang tidak terisi yang tersisa. Ini adalah algoritma yang agak elegan. Saya memeriksa algoritma penyandian saya dengan tangan dan terlihat bagus. Encoder mengeluarkan bitstring yang tampaknya benar (dalam bentuk byte untuk kesederhanaan).

3754SEBUAH236J7131372613762,748,51722667,108,864241313428,56121532,76815/4=3,7526/7=3.71426/7

54SEBUAH236J23456789TJQKSEBUAH547131015,565,9752600111011011000010010010111

2615,565,9751354SEBUAH236J7

13,12,11...,2,1)13,12,11 ...21312122125248,832218262,14418/53.61326/73.71455553333

Inilah daftar lengkap biaya saya (# bit per kartu) untuk semua peringkat yang mungkin dilihat:

13    26/7=3.714=3  5/7
12    18/5=3.600=3  3/5
11      7/2=3.500=3  1/2
10    10/3=3.333=3  1/3
  9    16/5=3.200=3  1/5
  8      3/1=3.000=3
  7    17/6=2.833=2  5/6
  6    13/5=2.600=2  3/5
  5      7/3=2.333=2  1/3
  4      2/1=2.000=2
  3      5/3=1.667=1  2/3
  2      1/1=1.000=1
  1      0/1..4=0,0=0

75,6,7,7,7,7,KK1312713K21,2,3 ...3131720

16813,12,11

10777748747s. Jika dek berakhir pada pasangan (seperti 77), triple / set (seperti 777) atau quad (seperti 7777), kami mendapatkan penghematan tambahan untuk dek tersebut menggunakan algoritma saya.

3222613163232

Di dek pertama dalam file data, pengkodean kartu adalah sebagai berikut (diagram yang akan datang nanti). Formatnya adalah (groupsize, bits, mode encode peringkat):

7,26,1372613
7,26,13
7,26,13
5,18,12
5,18,12
3,10,10
3,  9,  8
6,17,  7
5,13,  6
3,  5,  3
1,  0,  1

521683.23

181/33.23.254545454722772277 ...322223333444455556666777788889999TTTTJJJJQQQQKKKKSEBUAHSEBUAHSEBUAHSEBUAH40

1103,7K8101kartu tersisa. Ini penting karena membuat proses pengkodean lebih efisien ketika decoder dapat membuat asumsi yang benar tanpa encoder harus menyampaikan pesan tambahan kepadanya.

313121110

         26             26             26            18         18       10      9          17           13        5     0
    54SEBUAH236J  87726Q3  3969SEBUAHSEBUAHSEBUAH  QJK7T  9292Q  36K  J57   T8TKJ4  48Q8T  55K  4
13                                            12                    xy     98         7              6        543     2 1  0

2166175168bit. Perhatikan bahwa kita hanya mendapatkan 4 tunggal di ujung geladak tetapi jika sebaliknya kita punya keempat 4 di sana, itu adalah kasus yang lebih baik dan kita akan membutuhkan hanya 161 bit untuk menyandikan dek itu, sebuah kasus di mana pengepakan benar-benar mengalahkan entropi dari kode biner lurus dari posisi ordinal itu.

Saya sekarang memiliki kode yang diterapkan untuk menghitung persyaratan bit dan itu menunjukkan kepada saya rata-rata, sekitar 175 bit per dek dengan rendah 155 dan tinggi 183 untuk file tes 3 juta dek. Jadi algoritma saya tampaknya menggunakan 9 bit tambahan per dek vs penyandian biner lurus dari metode posisi ordinal. Tidak terlalu buruk hanya membutuhkan 5,5% ruang penyimpanan tambahan. 176 bit persis 22 byte sehingga sedikit lebih baik dari 52 byte per dek. Dek case terbaik (tidak muncul dalam 3 juta file test deck) paket hingga 136 bit dan case deck terburuk (muncul di testfile 8206 kali), adalah 183 bit. Analisis menunjukkan kasus terburuk adalah ketika kita tidak mendapatkan quad pertama hingga mendekati (atau pada) kartu 40. Kemudian karena mode encode ingin turun dengan cepat, kita "terjebak" mengisi blok (sebesar 7 kartu) dalam sebuah mode pengodean bit yang lebih tinggi. Orang mungkin berpikir bahwa tidak mendapatkan paha depan sampai kartu 40 akan sangat jarang menggunakan deck yang dikocok dengan baik, tetapi program saya mengatakan kepada saya bahwa itu terjadi 321 kali dalam testfile dari 3 juta deck sehingga sekitar 1 dari setiap 9346 deck. Itu lebih sering yang saya harapkan. Saya bisa memeriksa kasus ini dan menanganinya dengan bit lebih sedikit tetapi sangat jarang sehingga tidak akan mempengaruhi bit rata-rata cukup.

Juga ada hal lain yang sangat menarik. Jika saya mengurutkan deck pada data deck mentah, panjang awalan yang mengulang # kali signifikan hanya sekitar panjang 6 (seperti 222244). Namun dengan data yang dikemas, panjangnya meningkat menjadi sekitar 16. Itu berarti jika saya mengurutkan data yang dikemas, saya harus bisa mendapatkan penghematan yang signifikan dengan hanya menunjukkan ke decoder awalan 16 bit dan kemudian hanya menampilkan sisa deck. (minus awalan berulang) yang memiliki awalan yang sama, kemudian pergi ke awalan berikutnya dan ulangi. Dengan asumsi saya menyimpan bahkan 10 bit per deck dengan cara ini, saya harus mengalahkan 166 bit per deck. Dengan teknik enumerasi yang dinyatakan oleh orang lain, saya tidak yakin apakah awalan akan sepanjang dengan algoritma saya. Juga kecepatan pengemasan dan pembongkaran menggunakan algoritma saya sangat bagus.

Mengenai tingkat kompresi ke-2 di mana saya mengurutkan bitstrings output dari algoritma saya, kemudian menggunakan pengkodean "perbedaan": Metode yang sangat sederhana adalah untuk mengkodekan 61.278 prefiks 16 bit unik yang muncul setidaknya dua kali dalam data output (dan maksimum dari 89 kali dilaporkan) hanya sebagai bit 0 di output untuk menunjukkan ke dekompresor tingkat 2 bahwa kita sedang mengkodekan awalan (seperti 0000111100001111) dan kemudian setiap deck yang dikemas dengan awalan yang sama akan mengikuti dengan 1 bit terkemuka untuk menunjukkan bagian non awalan dari dek yang sudah dikemas. Rata-rata # dari tumpukan deck dengan awalan yang sama adalah sekitar 49 untuk setiap awalan, tidak termasuk beberapa yang unik (hanya 1 deck memiliki awalan tertentu). Tampaknya saya dapat menghemat sekitar 15 bit per deck menggunakan strategi sederhana ini (menyimpan awalan umum sekali).

Setelah level 2 kompresi menggunakan perbedaan (awalan) pengkodean dari output bitstring yang diurutkan dari encoder pertama, saya sekarang mendapatkan sekitar 160 bit per deck. Saya menggunakan awalan panjang 18 dan hanya menyimpannya utuh. Karena hampir semua (245013 dari 262144 = 93,5%) dari kemungkinan awalan 18 bit muncul, akan lebih baik untuk menyandikan awalan. Mungkin saya bisa menggunakan 2 bit untuk mengkodekan tipe data apa yang saya miliki. 00 = panjang reguler 18 awalan disimpan, 01 = "awalan 1 up" (sama dengan awalan sebelumnya kecuali 1 ditambahkan), 11 = penyandian langsung dari kemasan tingkat 1 (rata-rata sekitar 175 bit). 10 = ekspansi di masa depan ketika saya memikirkan hal lain untuk dikodekan yang akan menghemat bit.

Apakah ada orang lain yang mengalahkan 160 bit per deck? Saya pikir saya bisa mendapatkan milik saya sedikit lebih rendah dengan beberapa bereksperimen dan menggunakan deskriptor 2 bit yang saya sebutkan di atas. Mungkin itu akan keluar di 158ish. Tujuan saya adalah untuk membuatnya menjadi 156 bit (atau lebih baik) karena itu akan menjadi 3 bit per kartu atau kurang. Sangat mengesankan. Banyak percobaan untuk turun ke tingkat itu karena jika saya mengubah pengkodean tingkat pertama maka saya harus menguji ulang yang merupakan pengkodean tingkat 2 terbaik dan ada banyak kombinasi untuk mencoba. Beberapa perubahan yang saya buat mungkin baik untuk data acak serupa lainnya tetapi beberapa mungkin bias terhadap dataset ini. Tidak benar-benar yakin tetapi jika saya mendapatkan dorongan saya dapat mencoba set data 3 juta deck lain untuk melihat apa yang terjadi seperti jika saya mendapatkan hasil yang sama di atasnya.

1050

Adakah yang punya ide tentang bagaimana membuat algoritma saya lebih baik seperti kasus lain apa yang harus saya encode yang akan mengurangi bit penyimpanan untuk setiap deck rata-rata? Siapa saja?

2 lebih banyak hal: 1) Saya agak kecewa bahwa lebih banyak orang tidak memperbaiki solusi saya yang walaupun tidak optimal dalam hal ruang, masih layak dan cukup mudah untuk diterapkan (saya dapat pekerjaan saya dengan baik). 2) Saya melakukan analisis pada datafile 3 juta dek saya dan memperhatikan bahwa kartu yang paling sering terjadi di mana peringkat 1 terisi (seperti 4444) ada pada kartu 26. Ini terjadi sekitar 6,711% dari waktu (untuk 201322 dari 3 juta deck) ). Saya berharap untuk menggunakan info ini untuk kompres lebih seperti memulai dalam 12 mode penyandian simbol karena kita tahu rata-rata kita tidak akan melihat setiap peringkat sampai sekitar middeck tetapi metode ini gagal untuk mengompres apa pun karena biaya overhead itu melebihi penghematan. Saya mencari beberapa perubahan pada algoritma saya yang sebenarnya dapat menghemat bit.

Jadi apakah ada yang punya ide apa yang harus saya coba selanjutnya untuk menyimpan beberapa bit per deck menggunakan algoritma saya? Saya mencari pola yang cukup sering terjadi sehingga saya dapat mengurangi bit per deck bahkan setelah overhead tambahan memberitahu decoder pola apa yang diharapkan. Saya sedang memikirkan sesuatu dengan probabilitas yang diharapkan dari kartu yang tidak terlihat yang tersisa dan menyatukan semua kartu yang tersisa menjadi satu ember. Ini akan memungkinkan saya untuk masuk ke mode encode yang lebih rendah lebih cepat dan mungkin menyimpan beberapa bit tapi saya ragu.

Juga, FYI, saya menghasilkan 10 juta shuffles acak dan menyimpannya dalam database untuk memudahkan analisis. Hanya 488 dari mereka berakhir di quad (seperti 5555). Jika saya mengemas hanya mereka yang menggunakan algoritma saya, saya mendapatkan rata-rata 165,71712 bit dengan rendah 157 bit dan tinggi 173 bit. Hanya sedikit di bawah 166 bit menggunakan metode pengkodean lainnya. Saya agak terkejut melihat betapa jarangnya kasus ini (sekitar 1 dari setiap 20.492 mengocok rata-rata).


3
Saya perhatikan bahwa Anda telah membuat sekitar 24 suntingan dalam waktu 9 jam. Saya menghargai keinginan Anda untuk meningkatkan jawaban Anda. Namun, setiap kali Anda mengedit jawabannya, ini menabrak ini ke bagian atas halaman depan. Karena alasan itu, kami mencegah pengeditan berlebihan. Jika Anda berharap untuk melakukan banyak pengeditan, apakah mungkin untuk mengumpulkan hasil edit Anda, jadi Anda hanya perlu mengedit satu kali setiap beberapa jam? (Kebetulan, perhatikan bahwa menempatkan "EDIT:" dan "UPDATE:" dalam jawaban Anda biasanya gaya yang buruk. Lihat meta.cs.stackexchange.com/q/657/755. )
DW

4
Ini bukan tempat untuk menaruh laporan kemajuan, pembaruan status, atau item blog. Kami menginginkan jawaban yang lengkap, tidak "segera hadir" atau "Saya punya solusi tapi saya tidak akan menjelaskan apa itu".
DW

3
Jika seseorang tertarik, dia akan menemukan solusi yang lebih baik. Cara terbaik adalah menunggu jawaban lengkap dan mempostingnya kemudian. Jika Anda memiliki beberapa pembaruan, blog akan melakukannya. Saya tidak mendorong ini, tetapi jika Anda benar-benar harus (saya tidak melihat alasan yang sah mengapa), Anda dapat menulis komentar di bawah posting Anda dan bergabung nanti. Saya juga mendorong Anda untuk menghapus semua komentar yang sudah usang dan memasukkannya ke dalam satu pertanyaan yang mulus - sulit untuk membaca semua. Saya mencoba membuat algoritme saya sendiri, berbeda dari yang disajikan, tetapi saya tidak puas dengan hasilnya - jadi saya tidak memposting parsial untuk diedit - kotak jawaban untuk yang penuh.
Evil

3
@ DavidJames, saya mengerti. Namun, itu masih tidak mengubah pedoman kami: tolong jangan membuat banyak suntingan. (Jika Anda ingin mengusulkan perbaikan ke situs web, jangan ragu untuk membuat posting di Meta Ilmu Komputer kami atau di meta.stackexchange.com menyarankannya. Pengembang tidak membaca utas komentar ini.) Tetapi sementara itu, kami bekerja dengan perangkat lunak yang kami miliki, dan melakukan banyak pengeditan tidak disarankan karena menabrak pertanyaan ke atas. Pada titik ini, membatasi diri Anda untuk mengedit satu kali per hari mungkin merupakan pedoman yang baik untuk memotret. Jangan ragu untuk menggunakan editor offline atau StackEdit jika itu membantu!
DW

3
Saya tidak mengangkat jawaban Anda karena beberapa alasan. 1) itu tidak perlu panjang dan JAUH terlalu bertele-tele. Anda dapat secara drastis mengurangi presentasinya. 2) ada jawaban yang lebih baik diposting, yang Anda pilih untuk diabaikan karena alasan tanpa sepengetahuan saya. 3) bertanya tentang kurangnya upvotes biasanya merupakan "bendera merah" bagi saya. 4) Ini terus-menerus tetap di halaman depan karena jumlah suntingan GILA.
Nicholas Mancuso
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.