Menghapus duplikat secara efisien dan dengan overhead memori rendah

9

Saya ingin memfilter secara efisien daftar bilangan bulat untuk duplikat dengan cara yang hanya disimpan oleh set hasil.

Salah satu cara ini dapat dilihat:

  • kami memiliki serangkaian bilangan bulat dengan besar (katakanlah )N 2 40S={1,,N}N240
  • kami memiliki fungsi dengan, konon, banyak tabrakan (gambar didistribusikan secara seragam dalam )Sf:SSS
  • kita perlu menyimpan , yaitu{ f ( x ) | x S }f[S]{f(x)|xS}

Saya memiliki estimasi (probabilistik) yang cukup akurat tentang apaadalah, dan karena itu dapat mengalokasikan struktur data di muka (katakan ).| f [ S ] | 2 30|f[S]||f[S]|230

Saya punya beberapa ide, tetapi saya tidak yakin apa yang akan menjadi pendekatan terbaik:

  • bitet keluar dari pertanyaan karena set input tidak sesuai dengan memori.
  • tabel hash, tetapi (1) membutuhkan beberapa overhead memori, katakanlah 150% daridan (2) tabel harus dieksplorasi ketika dibangun yang membutuhkan waktu tambahan karena overhead memori.|f[S]|
  • semacam "on the fly", lebih disukai dengan kompleksitas (jenis non-perbandingan). Mengenai itu, saya tidak yakin apa perbedaan utama antara jenis ember dan flashsort .O(N)
  • array sederhana dengan pohon pencarian biner, tetapi ini membutuhkan waktu .O(Nlog|f[S]|)
  • mungkin menggunakan filter Bloom atau struktur data serupa dapat berguna dalam relaksasi (dengan positif palsu) dari masalahnya.

Beberapa pertanyaan tentang stackoverflow tampaknya mengatasi hal-hal semacam ini ( /programming/12240997/sorting-array-in-on-run-time , /programming/3951547/java -array-Finding-duplikat ), tetapi tampaknya tidak ada yang cocok dengan persyaratan saya.

dokter
sumber
2
Apakah Anda perlu menghitung f [S] (apa pun itu), atau untuk dapat mengetahui dengan cepat apakah ada x di dalamnya?
Gilles 'SO- stop being evil'
@Gilles: Saya percaya bahwa, karena tidak ada struktur yang jelas dapat ditemukan di f [S], kedua solusi itu setara.
doc
Jumlah Anda tidak bertambah. Gambar yang diharapkan dari sebuah fungsi acak pada domain dari ukuran kira-kira . Masalah lain adalah bahwa melalui akan memakan waktu terlalu lama kecuali Anda memiliki superkomputer atau sekelompok besar yang Anda inginkan. ( 1 - 1 / e ) N 2 56N(11/e)N256
Yuval Filmus
1
Waktu untuk pohon pencarian biner adalah , yang mungkin atau mungkin tidak dekat dengan dalam praktiknya tetapi masih lebih akurat. O ( N log N )O(Nlog|f[S]|)O(NlogN)
jmad
1
Dengan , bukankah algoritma waktu linear juga akan menjadi penghalang? (Dari perhitungan saya, bahkan jika Anda mempertimbangkan satu elemen dalam 1 nano-detik, itu akan membawa Anda 2 tahun yang baik!). SN256S
Aryabhata

Jawaban:

1

Kenapa tidak bin dan rantai?

Idenya adalah untuk menyimpan bilangan bulat positif yang dapat direpresentasikan oleh bit dalam array dari entri yang mewakili rentang nilai: entri , , mewakili kisaran . Untuk setiap kita dapat menulis mana memiliki bit dan memiliki bit . Cobalah untuk menyimpan (bukan !) Di lokasi :A 2 k A [ y ] y 0 [ 2 m y , 2 m ( y + 1 ) - 1 ] 1 x < 2 n x = 2 m y + z y k z m z x yn=k+mA2kA[y]y0[2my,2m(y+1)1]1x<2nx=2my+zykzmzxy

  • Ketika sudah, jangan lakukan apa-apa: adalah duplikat.xA[y]=zx

  • Ketika tidak diinisialisasi, simpan di .z A [ y ]A[y]zA[y]

  • Jika tidak, simpan indeks ke dalam array terpisah yang digunakan untuk rantai 's (yang bertabrakan pada ) dalam daftar tertaut. Anda harus mencari secara linear melalui daftar yang dikepalai oleh dan, tergantung pada apa yang ditemukan oleh pencarian, berpotensi memasukkan ke dalam daftar.y A [ y ] zzyA[y]z

Pada akhirnya, mudah untuk dipulihkan dengan memutar melalui entri yang diinisialisasi dari dan - dengan hanya menggabungkan dua bitstrings - memasang kembali setiap ditemukan di lokasi (baik secara langsung atau dalam rantai yang direferensikan di sana) ke aslinya nilai .A z y x = 2 m y + zf(S)Azyx=2my+z

Ketika distribusi mendekati seragam dan melebihi , tidak akan ada banyak rantai (ini dapat dinilai dengan cara biasa) dan rantai akan cenderung pendek. Ketika distribusinya tidak seragam, algoritme masih berfungsi, tetapi dapat mencapai waktu kuadratik. Jika itu suatu kemungkinan, gunakan sesuatu yang lebih efisien daripada rantai (dan bayar sedikit overhead untuk penyimpanan). N2kN

Penyimpanan yang dibutuhkan paling banyak adalah bit untuk dan bit untuk rantai (dengan asumsi ). Ini adalah persis ruang yang dibutuhkan untuk menyimpan nilai setiap bit. Jika Anda yakin dengan keseragaman, Anda dapat mengalokasikan simpanan yang kurang untuk rantai. Jika ketidakseragaman adalah suatu kemungkinan, Anda mungkin ingin meningkatkan dan menganjurkan penyimpanan rantai sepenuhnya. A 2 2 k m k 2 k n k2nA22kmk2knk

Cara alternatif untuk memikirkan solusi ini adalah bahwa ia adalah tabel hash dengan fungsi hash yang sangat bagus (ambil bit yang paling signifikan) dan, karena itu, kita hanya perlu menyimpan bit paling tidak signifikan dalam tabel .m = n - kkm=nk

Ada beberapa cara untuk overlay penyimpanan untuk rantai dengan penyimpanan untuk tetapi tampaknya tidak layak repot, karena itu tidak akan menghemat banyak (dengan asumsi jauh lebih kecil dari ) ruang dan akan membuat kode lebih sulit untuk dikembangkan, debug, dan pertahankan.m kAmk

whuber
sumber
1
Saya pikir paragraf kedua hingga terakhir adalah yang utama di sini, dan mungkin harus di atas (sebagai ide). Saya tidak tahu istilah "bin and chain" (meskipun masuk akal setelah membaca posting). Gagasan ini dapat diperluas untuk mencoba .
Raphael
Jadi, ini pada input yang berdistribusi buruk. Saya tidak melihat bagaimana ini efisien. Θ(n2)
einpoklum
@einpoklum Jawaban ini secara eksplisit menjelaskan kondisi di mana solusinya efisien.
Whuber