Menemukan sepasang vektor bit yang tidak tumpang tindih

17

Saya memberi Anda daftar bitvektor dengan lebar . Tujuan Anda adalah mengembalikan dua vektor bit dari daftar yang tidak memiliki 1s sama, atau melaporkan bahwa tidak ada pasangan semacam itu.knk

Misalnya, jika saya memberi Anda maka satu-satunya solusi adalah . Atau, input tidak memiliki solusi. Dan daftar apa pun yang berisi bitvector semua-nol dan elemen lain memiliki solusi sepele .{ 00110 , 11000 } [ 111 , 011 , 110 , 101 ] 000 ... 0 e { e , 000 ... 0 }[00110,01100,11000]{00110,11000}[111,011,110,101]000...0e{e,000...0}

Berikut adalah contoh yang sedikit lebih sulit, tanpa solusi (setiap baris adalah vektor bit, kotak hitam adalah 1s dan kotak putih adalah 0s):

■ ■ ■ ■ □ □ □ □ □ □ □ □ □
■ □ □ □ ■ ■ ■ □ □ □ □ □ □ 
■ □ □ □ □ □ □ ■ ■ ■ □ □ □
■ □ □ □ □ □ □ □ □ □ ■ ■ ■
□ ■ □ □ □ ■ □ □ □ ■ ■ □ □
□ ■ □ □ ■ □ □ □ ■ □ □ □ ■
□ ■ □ □ □ □ ■ ■ □ □ □ ■ □ <-- All row pairs share a black square
□ □ ■ □ □ □ ■ □ ■ □ ■ □ □
□ □ ■ □ □ ■ □ ■ □ □ □ □ ■
□ □ ■ □ ■ □ □ □ □ ■ □ ■ □
□ □ □ ■ ■ □ □ ■ □ □ ■ □ □
□ □ □ ■ □ □ ■ □ □ ■ □ □ ■
□ □ □ ■ □ ■ □ □ ■ □ □ ■ □

Seberapa efisien dua bitvektor yang tidak tumpang tindih dapat ditemukan, atau ditunjukkan tidak ada?

Algoritma naif, di mana Anda hanya membandingkan setiap pasangan yang mungkin, adalah . Apakah mungkin melakukan lebih baik?O(n2k)

Craig Gidney
sumber
Pengurangan yang mungkin: Anda memiliki grafik dengan satu simpul untuk setiap vektor dan tepi antara dua simpul jika kedua vektor yang bersesuaian memiliki 1 persamaan. Anda ingin tahu apakah diameter grafik adalah . Tetapi tampaknya sulit untuk pergi lebih cepat daripada . 2 O ( n 2 k )G2O(n2k)
François
@ FrançoisGodi Setiap komponen grafik yang terhubung dengan tiga node dan sisi yang hilang memiliki diameter setidaknya dua. Dengan representasi daftar adjacency, dibutuhkan waktu untuk memeriksanya. O(V)
Craig Gidney
@ Strilanc Tentu, jika tidak ada solusi grafik selesai (lebih jelas daripada diameter = 1, Anda benar), tetapi menghitung representasi daftar adjacency bisa lama.
François
Apakah lebih kecil dari lebar kata mesin Anda? k
Raphael
1
@ TomvanderZanden Kedengarannya seperti itu akan melanggar invarian yang bergantung pada struktur data. Secara khusus, kesetaraan itu harus transitif. Saya sudah berpikir tentang menggunakan trie dan saya tidak melihat bagaimana menghindari faktor-of-2 blowup setiap kali permintaan bitmask memiliki 0.
Craig Gidney

Jawaban:

10

Pemanasan: bitvektor acak

Sebagai pemanasan, kita bisa mulai dengan kasus di mana setiap bitvector dipilih secara seragam secara acak. Kemudian ternyata masalah tersebut dapat diselesaikan dalam waktu (lebih tepatnya, dapat diganti dengan ).O(n1.6min(k,lgn))1.6lg3

Kami akan mempertimbangkan varian dua set masalah berikut:

Set diberikan dari bitvectors, menentukan di mana terdapat non-tumpang tindih pasangan .S,T{0,1}ksS,tT

Teknik dasar untuk menyelesaikan ini adalah membagi-dan-taklukkan. Berikut ini adalah algoritma waktu menggunakan divide-and-conquer:O(n1.6k)

  1. Split dan berdasarkan posisi bit pertama. Dengan kata lain, bentuk , , , .STS0={sS:s0=0}S1={sS:s0=1}T0={tT:t0=0}T1={tT:t0=1}

  2. Sekarang cari secara rekursif untuk pasangan yang tidak tumpang tindih dari , dari , dan dari . Jika ada panggilan rekursif menemukan pasangan yang tidak tumpang tindih, output itu, jika tidak output "Tidak ada pasangan yang tumpang tindih".S0,T0S0,T1T1,S0

Karena semua bitvektor dipilih secara acak, kita dapat mengharapkan dan . Dengan demikian, kami memiliki tiga panggilan rekursif, dan kami telah mengurangi ukuran masalah dengan faktor dua (kedua set ukurannya dikurangi dengan faktor dua). Setelah terpecah, salah satu dari dua set turun ke ukuran 1, dan masalahnya dapat diselesaikan dalam waktu linier. Kami mendapatkan relasi berulang di sepanjang garis , yang solusinya adalah . Akuntansi untuk menjalankan waktu lebih tepatnya dalam kasus dua set, kita melihat waktu berjalan adalah .|Sb||S|/2|Tb||T|/2lgmin(|S|,|T|)T(n)=3T(n/2)+O(nk)T(n)=O(n1.6k)O(min(|S|,|T|)0.6max(|S|,|T|)k)

Ini dapat ditingkatkan lebih lanjut, dengan mencatat bahwa jika , maka probabilitas bahwa pasangan yang tidak tumpang tindih ada secara eksponensial kecil. Secara khusus, jika adalah dua vektor acak, probabilitas bahwa mereka tidak tumpang tindih adalah . Jika , ada pasangan demikian, maka dengan ikatan gabungan, probabilitas ada pasangan yang tidak tumpang tindih adalah paling banyak . Ketika , ini adalah . Jadi, sebagai langkah pra-pemrosesan, jikak2.5lgn+100x,y(3/4)k|S|=|T|=nn2n2(3/4)kk2.5lgn+1001/2100k2.5lgn+100, maka kita dapat segera mengembalikan "Tidak ada pasangan yang tidak tumpang tindih" (probabilitas ini salah adalah sangat kecil), jika tidak kita jalankan algoritma di atas.

Dengan demikian kita mencapai waktu berjalan (atau untuk varian dua set yang diusulkan di atas), untuk kasus khusus di mana bitvektor dipilih secara seragam secara acak.O(n1.6min(k,lgn))O(min(|S|,|T|)0.6max(|S|,|T|)min(k,lgn))

Tentu saja, ini bukan analisis kasus terburuk. Bitvektor acak jauh lebih mudah daripada kasus terburuk - tetapi mari kita perlakukan itu sebagai pemanasan, untuk mendapatkan beberapa ide yang mungkin bisa kita terapkan pada kasus umum.

Pelajaran dari pemanasan

Kita bisa belajar beberapa pelajaran dari pemanasan di atas. Pertama, bagi-dan-taklukkan (pemisahan pada posisi bit) tampaknya membantu. Kedua, Anda ingin membagi pada posisi bit dengan sebanyak mungkin dalam posisi itu; semakin banyak ada, semakin sedikit pengurangan dalam ukuran subproblem yang Anda dapatkan.10

Ketiga, ini menunjukkan bahwa masalahnya semakin sulit karena kepadatan semakin kecil - jika ada sangat sedikit di antara bitvectors (sebagian besar adalah 's), masalahnya terlihat cukup sulit, karena setiap split mengurangi ukuran subproblem sedikit. Jadi, tentukan densitas menjadi fraksi bit yang (yaitu, dari semua bit ), dan densitas posisi bit menjadi fraksi bitvektor yang pada posisi .110Δ1nki1i

Penanganan kepadatan sangat rendah

Sebagai langkah selanjutnya, kita mungkin bertanya-tanya apa yang terjadi jika kepadatannya sangat kecil. Ternyata jika kepadatan di setiap posisi bit lebih kecil dari , kami dijamin bahwa ada pasangan non-tumpang tindih: ada argumen keberadaan (non-konstruktif) yang menunjukkan bahwa beberapa non-tumpang tindih pasangan harus ada. Ini tidak membantu kami menemukannya, tetapi setidaknya kami tahu itu ada.1/k

Mengapa demikian? Mari kita mengatakan bahwa sepasang bitvectors adalah ditutupi demi sedikit posisi jika . Perhatikan bahwa setiap pasangan bitvektor yang tumpang tindih harus dilindungi oleh beberapa posisi bit. Sekarang, jika kita memperbaiki posisi bit tertentu , jumlah pasangan yang dapat dicakup oleh posisi bit tersebut paling banyak . Menjumlahkan semua dari posisi bit, kami menemukan bahwa jumlah total pasangan yang dicakup oleh beberapa posisi bit adalahx,yixi=yi=1i(nΔ(i))2<n2/kk<n2. Ini berarti harus ada beberapa pasangan yang tidak dicakup oleh posisi bit apa pun, yang menyiratkan bahwa pasangan ini tidak tumpang tindih. Jadi jika kepadatan cukup rendah di setiap posisi bit, maka pasangan yang tidak tumpang tindih pasti ada.

Namun, saya bingung untuk mengidentifikasi algoritma cepat untuk menemukan pasangan yang tidak tumpang tindih, dalam rezim ini, meskipun satu dijamin ada. Saya tidak segera melihat teknik yang akan menghasilkan waktu berjalan yang memiliki ketergantungan sub-kuadrat pada . Jadi, ini adalah kasus khusus yang bagus untuk difokuskan, jika Anda ingin meluangkan waktu memikirkan masalah ini.n

Menuju algoritma kasus umum

Dalam kasus umum, heuristik alami tampaknya adalah: memilih posisi bit dengan jumlah paling banyak (yaitu, dengan kepadatan tertinggi), dan membaginya. Dengan kata lain:i1

  1. Temukan posisi bit yang memaksimalkan .iΔ(i)

  2. Split dan berdasarkan posisi bit . Dengan kata lain, bentuk , , , .STiS0={sS:si=0}S1={sS:si=1}T0={tT:ti=0}T1={tT:ti=1}

  3. Sekarang cari secara rekursif untuk pasangan yang tidak tumpang tindih dari , dari , dan dari . Jika ada panggilan rekursif menemukan pasangan yang tidak tumpang tindih, output itu, jika tidak output "Tidak ada pasangan yang tumpang tindih".S0,T0S0,T1T1,S0

Tantangannya adalah untuk menganalisis kinerjanya dalam kasus terburuk.

Mari kita asumsikan bahwa sebagai langkah pra-pemrosesan, pertama-tama kita menghitung kepadatan setiap posisi bit. Juga, jika untuk setiap , asumsikan bahwa langkah pra-pemrosesan menghasilkan "Pasangan yang tumpang tindih ada" (Saya menyadari bahwa ini tidak menunjukkan contoh pasangan yang tumpang tindih, tapi mari kita kesampingkan itu sebagai tantangan tersendiri). Semua ini dapat dilakukan dalam waktu . Informasi kepadatan dapat dipertahankan secara efisien karena kami melakukan panggilan rekursif; itu tidak akan menjadi kontributor dominan untuk waktu berjalan.Δ(i)<1/kiO(nk)

Apa yang akan menjadi waktu berjalan dari prosedur ini? Saya tidak yakin, tapi di sini ada beberapa pengamatan yang mungkin bisa membantu. Setiap tingkat rekursi mengurangi ukuran masalah sekitar bitvectors (mis., Dari bitvectors ke bitvectors). Oleh karena itu, rekursi hanya dapat mencapai kedalaman . Namun, saya tidak segera yakin bagaimana cara menghitung jumlah daun di pohon rekursi (ada banyak kurang dari daun), jadi saya tidak yakin berapa lama waktu berjalan ini seharusnya mengarah untuk.n/knnn/kk3k

DW
sumber
ad kepadatan rendah: ini tampaknya semacam argumen lubang-merpati. Mungkin jika kami menggunakan gagasan umum Anda (membagi kolom dengan yang paling banyak), kami mendapatkan batas yang lebih baik karena -kas (kami tidak berulang) sudah menyingkirkan yang "paling"? (S1,T1)
Raphael
Jumlah total yang mungkin menjadi parameter yang berguna. Anda telah menunjukkan batas bawah yang dapat kita gunakan untuk memotong pohon; dapatkah kita menunjukkan batas atas juga? Sebagai contoh, jika ada lebih dari , kita memiliki setidaknya tumpang tindih. ckc
Raphael
Ngomong-ngomong, bagaimana Anda mengusulkan kami melakukan split pertama; sewenang-wenang? Mengapa tidak membagi seluruh set input dengan beberapa kolom ? Kita hanya perlu perulangan di -case (tidak ada solusi di antara mereka yang berbagi satu di ). Dengan harapan, yang memberi melalui batas (jika diperbaiki). Untuk batas umum, Anda telah menunjukkan bahwa kami dapat (dengan asumsi batas bawah yang Anda usulkan) bahwa kami menyingkirkan setidaknya elemen dengan setiap pemisahan, yang tampaknya menyiratkan terikat kasus terburuk. Atau apakah saya melewatkan sesuatu? i0iT(n)=T(n/2)+O(nk)O(nk)kn/kO(nk)
Raphael
Ah, itu salah, tentu saja, karena tidak mempertimbangkan 0-1-ketidakcocokan. Itulah yang saya dapatkan untuk mencoba berpikir sebelum sarapan, saya kira.
Raphael
@Raphael, ada dua masalah: (a) vektor mungkin sebagian besar nol, jadi Anda tidak bisa mengandalkan pembagian 50-50; perulangan akan menjadi sesuatu yang lebih seperti , (b) yang lebih penting, itu tidak cukup untuk hanya berulang pada subset 0; Anda juga perlu memeriksa pasangan antara vektor dari 0-subset dan vektor dari 1-subset, jadi ada satu atau dua rekursi tambahan yang harus dilakukan. (Saya pikir? Saya harap saya benar.)T(n)=T((nn/k)k)+O(nk)
DW
8

Solusi lebih cepat saat , menggunakan perkalian matriksnk

Misalkan . Tujuan kami adalah melakukan lebih baik daripada waktu berjalan .n=kO(n2k)=O(n3)

Kita dapat menganggap bitvektor dan posisi bit sebagai node dalam grafik. Ada tepi antara node bitvector dan node posisi bit ketika bitvector memiliki 1 di posisi itu. Grafik yang dihasilkan adalah bipartit (dengan node yang mewakili bitvector di satu sisi dan node yang mewakili bitposition di sisi lain), dan memiliki node.n+k=2n

Dengan matriks adjacency dari sebuah grafik, kita dapat mengetahui apakah ada jalur dua hop antara dua simpul dengan mengkuadratkan dan memeriksa apakah matriks yang dihasilkan memiliki "tepi" antara dua simpul tersebut (yaitu entri tepi dalam matriks kuadrat tidak nol). Untuk tujuan kami, entri nol dalam matriks adjacency kuadrat sesuai dengan sepasang bitvektor yang tidak tumpang tindih (yaitu solusi). Kurangnya nol berarti tidak ada solusi.M M

Mengkuadratkan matriks nxn dapat dilakukan dalam waktu , di mana diketahui berada di bawah dan dikira .O(nω)ω2.3732

Jadi algoritmanya adalah:

  • Konversi bitvektor dan posisi bit menjadi grafik bipartit dengan node dan paling banyak edge. Ini membutuhkan waktu .n+knkO(nk)
  • Hitung matriks adjacency dari grafik. Ini membutuhkan waktu dan ruang .O((n+k)2)
  • Kuadratkan matriks adjacency. Ini membutuhkan waktu .O((n+k)ω)
  • Cari bagian bitvector dari matriks adjacency untuk entri nol. Ini membutuhkan waktu .O(n2)

Langkah paling mahal adalah mengkuadratkan adjacency matrix. Jika maka keseluruhan algoritma membutuhkan waktu , yang lebih baik daripada waktu naif .n=kO((n+k)ω)=O(nω)O(n3)

Solusi ini juga lebih cepat ketika tumbuh tidak terlalu banyak lebih lambat dan tidak terlalu banyak lebih cepat dari . Selama dan , maka lebih baik dari . Untuk yang diterjemahkan menjadi (asimtotik). Jika membatasi ke 2, maka batas melebar ke arah .knkΩ(nω2)kO(n2ω1)(n+k)ωn2kw2.373n0.731kn1.373wnϵkn2ϵ

Craig Gidney
sumber
1. Ini juga lebih baik daripada solusi naif jika tetapi . 2. Jika , heuristik bisa berupa: pilih himpunan bagian acak dari posisi bit, batasi pada posisi bit itu dan gunakan perkalian matriks untuk menghitung semua pasangan yang tidak tumpang tindih dalam posisi bit itu; untuk setiap pasangan seperti itu, periksa apakah itu memecahkan masalah asli. Jika tidak banyak pasangan yang tidak tumpang tindih pada posisi bit itu, ini memberikan percepatan atas algoritma naif. Namun saya tidak tahu batas atas yang baik pada jumlah pasangan tersebut. k=Ω(n)k=o(n1.457)knnnn
DW
4

Ini setara dengan menemukan bit vektor yang merupakan bagian dari komplemen dari vektor lain; yaitu 1 nya hanya terjadi di mana 0's terjadi di yang lain.

Jika k (atau jumlah 1) kecil, Anda bisa mendapatkan waktu hanya dengan membuat semua himpunan bagian dari komplemen dari setiap bitvector dan menempatkannya dalam sebuah trie (menggunakan backtracking). Jika bitvector ditemukan dalam trie (kita dapat memeriksa masing-masing sebelum penyisipan komplemen-subset) maka kita memiliki pasangan yang tidak tumpang tindih.O(n2k)

Jika angka 1 atau 0 dibatasi ke angka yang bahkan lebih rendah dari k, maka eksponen dapat diganti dengan itu. Subset-indexing bisa pada setiap vektor atau komplemennya, selama probing menggunakan yang sebaliknya.

Ada juga skema untuk superset-menemukan dalam sebuah trie yang hanya menyimpan masing-masing vektor hanya sekali, tetapi tidak melewatkan selama penyelidikan untuk apa yang saya percaya adalah kompleksitas agregat yang sama; yaitu ia memiliki penyisipan tetapi pencarian .o(k)o(2k)

KWillets
sumber
Terima kasih. Kompleksitas solusi Anda adalah , di mana adalah probabilitas 1 dalam bitvector. Beberapa detail implementasi: meskipun ini sedikit perbaikan, tidak perlu menghitung dan menyimpan komplemen dalam trie. Cukup mengikuti cabang pelengkap saat memeriksa pertandingan yang tidak tumpang tindih sudah cukup. Dan, mengambil 0 secara langsung sebagai wildcard, tidak ada wildcard khusus yang diperlukan. n2(1p)kp
Mauro Lacy
2

Mewakili vektor bit sebagai matriks . Ambil dan antara 1 dan .n×kMijn

(MMT)ij=lMilMjl.

(MMT)ij , produk titik dari vektor ke - dan ke- , tidak-nol jika, dan hanya jika, vektor-vektor dan memiliki kesamaan 1. Jadi, untuk menemukan solusi, hitung dan kembalikan posisi entri nol, jika entri tersebut ada.ijijMMT

Kompleksitas

Menggunakan perkalian naif, ini membutuhkan operasi aritmatika . Jika , dibutuhkan operasi menggunakan algoritma Coppersmith-Winograd yang sama sekali tidak praktis, atau menggunakan algoritma Strassen. Jika , maka masalahnya dapat diselesaikan menggunakan operasi .O(n2k)n=kO(n2.37)O(n2.8)k=O(n0.302)n2+o(1)

Ben
sumber
Apa bedanya dengan jawaban Strilanc ?
DW
1
@DW Menggunakan -by- matriks bukan sebuah -by- matriks merupakan perbaikan. Juga disebutkan cara untuk memotong faktor k ketika k << n, sehingga mungkin berguna. nk(n+k)(n+k)
Craig Gidney