假設在某個時間點,您有一個N
數字的集合,並且知道中間元素:M
。現在,您獲得了一個新值X
,因此您可能需要更新M
。 (或者說,假設你所處理的數字都是唯一的,並且所有的樣本都是串行接收的,所以併發性沒有問題。)快速中值更新算法
計算新的均值非常簡單:take老的意思是,加X
,乘以N
,除以N + 1
。 (從檢查N元素的平均值是如何定義的,這一點很明顯,目前我並不太在意數值)
我的問題是:任何人都可以提出一個創意/最優)方法更新中位數的問題?我將在下面提供一個例子(簡單介紹我自己的設計),並進行一些分析:
在本示例中,我將使用std::forward_list
,因爲C++ 11是我最近遇到的這種情況。在不失一般性的情況下,我會假設你正在以正確的方式進行:維護到目前爲止遇到的元素(類型T)的有序列表,std::forward_list<T> sorted;
出現T x;
時,只需使用以下代碼將其摺疊到位:
sorted.merge(std::forward_list<T> {{ x }});
順便說一下,我很好奇,如果任何人有一個更好(更有效/優雅)的方法。 Gripes歡迎。
所以,X
現在是sorted
一部分,這是我的想法,概括地說:
auto it = sorted.begin(), itend = sorted.end();
typename std::forward_list<T>::size_type count = std::distance(it, itend);
for (const auto &e : sorted) {
if (it == itend || ++it == itend) {
M = (count % 2) ? e : (e + M)/2;
break;
} else { ++it; }
}
這裏發生的是,漂亮的(如果不是有點難以看到)的事情:因爲你移動迭代器轉發兩次(並且安全,我可能會添加,但以兩個比較爲代價),當達到end()
時,我們將處於適當的(中值)值。如果存在奇數個元素,則M
就是該樣本,如果不是,則只是該元素的平均值和舊(推出)中值。由於奇數和偶數交替出現,舊的或新的M
實際上將在集合中。這個推理是合理的,是嗎?
如果你認爲它是垃圾/你的好得多,你不需要評論我的O(3n)方法;我只是把它作爲一個起點。
你當然可以做到在O(日誌(n))的時間。將元素添加到平衡二叉樹並選擇第k個最大元素都是O(log(n))時間操作。對於更有趣的事情,也許嘗試2堆,其中一個用於n/2個最大的元素,另一個用於最小的元素。 –
@MarcGlisse:看到我的答案,你不必搜索第k個大元素,因爲你已經知道它或第(k + 1)個或第(k-1)個最大元素:-) –
@SauceMaster看起來你正在跟蹤列表中出現次數最多的元素。這不是中位數。 –