Saat ini saya sedang mengerjakan algoritme untuk menerapkan filter median bergulir (analog dengan filter rata-rata bergulir) di C. Dari penelusuran literatur saya, tampaknya ada dua cara yang cukup efisien untuk melakukannya. Yang pertama adalah mengurutkan jendela nilai awal, kemudian melakukan pencarian biner untuk memasukkan nilai baru dan menghapus nilai yang sudah ada di setiap iterasi.
Yang kedua (dari Hardle dan Steiger, 1995, JRSS-C, Algoritma 296) membangun struktur heap berujung ganda, dengan maxheap di satu ujung, minheap di sisi lain, dan median di tengah. Ini menghasilkan algoritma waktu-linier, bukan yang O (n log n).
Inilah masalah saya: menerapkan yang pertama dapat dilakukan, tetapi saya perlu menjalankannya pada jutaan rangkaian waktu, jadi efisiensi sangat penting. Yang terakhir ini terbukti sangat sulit untuk diterapkan. Saya menemukan kode di file Trunmed.c dari kode untuk paket statistik R, tetapi ini agak tidak dapat diuraikan.
Apakah ada yang tahu tentang implementasi C yang ditulis dengan baik untuk algoritma median waktu linier berguling?
Edit: Tautan ke kode Trunmed.c http://google.com/codesearch/p?hl=id&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c
Jawaban:
Saya telah melihat R
src/library/stats/src/Trunmed.c
beberapa kali karena saya menginginkan sesuatu yang serupa juga dalam subrutin C ++ class / C mandiri. Perhatikan bahwa ini sebenarnya adalah dua implementasi sekaligus, lihatsrc/library/stats/man/runmed.Rd
(sumber file bantuan) yang menyatakanAkan menyenangkan melihat ini digunakan kembali dengan cara yang lebih mandiri. Apakah Anda menjadi sukarelawan? Saya dapat membantu dengan beberapa bit R.
Sunting 1 : Selain tautan ke versi Trunmed.c yang lebih lama di atas, berikut adalah salinan SVN saat ini
Srunmed.c
(untuk versi Stuetzle)Trunmed.c
(untuk versi Turlach)runmed.R
untuk fungsi R yang memanggil iniSunting 2 : Ryan Tibshirani memiliki beberapa kode C dan Fortran pada binning median cepat yang mungkin merupakan titik awal yang cocok untuk pendekatan berjendela.
sumber
Saya tidak dapat menemukan implementasi modern dari struktur data c ++ dengan statistik pesanan sehingga akhirnya menerapkan kedua ide di tautan pembuat kode teratas yang disarankan oleh MAK ( Editor Pertandingan : gulir ke bawah ke FloatingMedian).
Dua multiset
Ide pertama mempartisi data menjadi dua struktur data (heaps, multisets, dll) dengan O (ln N) per penyisipan / penghapusan tidak memungkinkan kuantil diubah secara dinamis tanpa biaya yang besar. Yaitu kita dapat memiliki median bergulir, atau 75% bergulir tetapi tidak keduanya pada saat yang bersamaan.
Pohon segmen
Ide kedua menggunakan pohon segmen yaitu O (ln N) untuk penyisipan / penghapusan / kueri tetapi lebih fleksibel. Yang terbaik dari semua "N" adalah ukuran rentang data Anda. Jadi jika median bergulir Anda memiliki jendela satu juta item, tetapi datanya bervariasi dari 1..65536, maka hanya 16 operasi yang diperlukan per pergerakan jendela bergulir 1 juta !!
Kode c ++ mirip dengan yang diposting Denis di atas ("Berikut algoritme sederhana untuk data terkuantisasi")
Pohon Statistik Pesanan GNU
Tepat sebelum menyerah, saya menemukan bahwa stdlibc ++ berisi pohon statistik pesanan !!!
Ini memiliki dua operasi penting:
Lihat libstdc ++ manual policy_based_data_structures_test (cari "split and join").
Saya telah membungkus pohon untuk digunakan dalam tajuk praktis untuk kompiler yang mendukung typedefs parsial gaya c ++ 0x / c ++ 11:
sumber
Saya telah melakukan implementasi C di sini . Beberapa detail lebih lanjut ada dalam pertanyaan ini: Rolling median dalam implementasi C - Turlach .
Penggunaan sampel:
sumber
Saya menggunakan penaksir median tambahan ini:
yang memiliki bentuk yang sama dengan penaksir rata-rata yang lebih umum:
Di sini eta adalah parameter kecepatan pembelajaran kecil (misalnya
0.001
), dansgn()
merupakan fungsi signum yang mengembalikan salah satu{-1, 0, 1}
. (Gunakan konstantaeta
seperti ini jika datanya tidak stasioner dan Anda ingin melacak perubahan dari waktu ke waktu; jika tidak, untuk sumber stasioner gunakan sesuatu sepertieta = 1 / n
konvergen, di manan
jumlah sampel yang terlihat sejauh ini.)Juga, saya memodifikasi penaksir median agar berfungsi untuk kuantil sewenang-wenang. Secara umum, fungsi kuantil memberi tahu Anda nilai yang membagi data menjadi dua pecahan:
p
dan1 - p
. Berikut ini memperkirakan nilai ini secara bertahap:Nilainya
p
harus di dalam[0, 1]
. Ini pada dasarnya menggesersgn()
keluaran simetris fungsi{-1, 0, 1}
untuk condong ke satu sisi, mempartisi sampel data menjadi dua nampan berukuran tidak sama (pecahanp
dan1 - p
data masing-masing kurang dari / lebih besar dari perkiraan kuantil). Perhatikan bahwa untukp = 0.5
, ini mengurangi penduga median.sumber
Berikut algoritme sederhana untuk data terkuantisasi (beberapa bulan kemudian):
sumber
Rolling median dapat ditemukan dengan mempertahankan dua partisi angka.
Untuk memelihara partisi, gunakan Min Heap dan Max Heap.
Max Heap akan berisi angka yang lebih kecil dari sama dengan median.
Min Heap akan berisi angka yang lebih besar dari sama dengan median.
Balancing Constraint: jika jumlah total elemen genap maka kedua heap harus memiliki elemen yang sama.
jika jumlah elemen ganjil maka Max Heap akan memiliki satu elemen lebih banyak dari Min Heap.
Elemen Median: Jika kedua partisi memiliki jumlah elemen yang sama maka median adalah setengah dari jumlah elemen maks dari partisi pertama dan elemen min dari partisi kedua.
Jika tidak, median akan menjadi elemen maks dari partisi pertama.
sumber
Mungkin ada baiknya menunjukkan bahwa ada kasus khusus yang memiliki solusi tepat sederhana: ketika semua nilai dalam aliran adalah bilangan bulat dalam rentang yang ditentukan (relatif) kecil. Misalnya, asumsikan semuanya harus berada di antara 0 dan 1023. Dalam kasus ini, cukup tentukan larik 1024 elemen dan hitungan, dan hapus semua nilai ini. Untuk setiap nilai dalam aliran, tambahkan bin yang sesuai dan hitungannya. Setelah aliran berakhir, temukan bin yang berisi hitungan / 2 nilai tertinggi - mudah dilakukan dengan menambahkan bin berurutan mulai dari 0. Dengan menggunakan metode yang sama, nilai urutan peringkat arbitrer dapat ditemukan. (Ada sedikit kerumitan jika perlu mendeteksi saturasi bin dan "meningkatkan" ukuran nampan penyimpanan ke jenis yang lebih besar selama proses.)
Kasus khusus ini mungkin tampak artifisial, tetapi dalam praktiknya sangat umum. Ini juga dapat diterapkan sebagai perkiraan untuk bilangan real jika mereka berada dalam rentang dan tingkat presisi yang "cukup baik" diketahui. Ini akan berlaku untuk hampir semua rangkaian pengukuran pada sekelompok objek "dunia nyata". Misalnya, tinggi atau berat sekelompok orang. Bukan set yang cukup besar? Ini akan bekerja dengan baik untuk panjang atau berat semua (individu) bakteri di planet ini - dengan asumsi seseorang dapat menyediakan data!
Sepertinya saya salah membaca aslinya - yang sepertinya menginginkan median jendela geser, bukan hanya median aliran yang sangat panjang. Pendekatan ini masih berhasil untuk itu. Muat nilai aliran N pertama untuk jendela awal, lalu untuk nilai aliran N + 1, tambahkan bin yang sesuai sambil mengurangi bin yang sesuai dengan nilai aliran ke-0. Dalam hal ini diperlukan untuk mempertahankan nilai N terakhir untuk memungkinkan penurunan, yang dapat dilakukan secara efisien dengan secara siklis menangani larik berukuran N. Karena posisi median hanya dapat berubah sebesar -2, -1,0,1 , 2 di setiap langkah jendela geser, tidak perlu menjumlahkan semua nampan hingga median di setiap langkah, cukup sesuaikan "penunjuk median" tergantung pada nampan sisi mana yang dimodifikasi. Misalnya, jika nilai baru dan nilai yang dihapus berada di bawah median saat ini, maka nilai tersebut tidak berubah (offset = 0). Metode ini rusak ketika N menjadi terlalu besar untuk disimpan dengan nyaman dalam memori.
sumber
Jika Anda memiliki kemampuan untuk mereferensikan nilai sebagai fungsi titik waktu, Anda dapat mengambil sampel nilai dengan penggantian, menerapkan bootstrap untuk menghasilkan nilai median yang di-bootstrap dalam interval keyakinan. Ini memungkinkan Anda menghitung perkiraan median dengan efisiensi yang lebih besar daripada terus-menerus menyortir nilai yang masuk ke dalam struktur data.
sumber
Bagi yang membutuhkan running median di Java ... PriorityQueue adalah teman Anda. O (log N) masukkan, O (1) median saat ini, dan O (N) hapus. Jika Anda mengetahui distribusi data Anda, Anda dapat melakukan jauh lebih baik daripada ini.
sumber
}), higher = new PriorityQueue<Integer>();
ataunew PriorityQueue<Integer>(10,
. Saya tidak bisa menjalankan kode.Ini adalah salah satu yang dapat digunakan ketika keluaran yang tepat tidak penting (untuk tujuan tampilan, dll.) Anda membutuhkan totalcount dan lastmedian, ditambah nilai baru.
Menghasilkan hasil yang cukup tepat untuk hal-hal seperti page_display_time.
Aturan: aliran input harus lancar pada urutan waktu tampilan halaman, jumlah besar (> 30 dll), dan memiliki median bukan nol.
Contoh: waktu buka halaman, 800 item, 10ms ... 3000ms, rata-rata 90ms, median nyata: 11ms
Setelah 30 input, kesalahan median umumnya <= 20% (9ms..12ms), dan semakin berkurang. Setelah 800 masukan, kesalahannya adalah + -2%.
Pemikir lain dengan solusi serupa ada di sini: Median Filter Implementasi super efisien
sumber
Berikut adalah implementasi java
sumber
Jika Anda hanya membutuhkan rata-rata yang diperhalus, cara cepat / mudah adalah mengalikan nilai terakhir dengan x dan nilai rata-rata dengan (1-x) lalu menjumlahkannya. Ini kemudian menjadi rata-rata baru.
edit: Bukan apa yang diminta pengguna dan tidak valid secara statistik tetapi cukup baik untuk banyak penggunaan.
Saya akan meninggalkannya di sini (terlepas dari suara negatifnya) untuk pencarian!
sumber