Thuật toán trung bình cuộn trong C


114

Tôi hiện đang làm việc trên một thuật toán để triển khai bộ lọc trung bình luân phiên (tương tự như bộ lọc trung bình cuộn) ở C. Từ việc tìm kiếm tài liệu của tôi, có vẻ như có hai cách hợp lý hiệu quả để thực hiện. Đầu tiên là sắp xếp cửa sổ ban đầu của các giá trị, sau đó thực hiện tìm kiếm nhị phân để chèn giá trị mới và loại bỏ giá trị hiện có ở mỗi lần lặp.

Phương pháp thứ hai (từ Hardle và Steiger, 1995, JRSS-C, Thuật toán 296) xây dựng cấu trúc đống hai đầu, với cực đại ở một đầu, cực tiểu ở đầu kia và trung vị ở giữa. Điều này tạo ra một thuật toán thời gian tuyến tính thay vì một thuật toán là O (n log n).

Đây là vấn đề của tôi: triển khai cái trước là có thể làm được, nhưng tôi cần chạy cái này trên hàng triệu chuỗi thời gian, vì vậy hiệu quả quan trọng rất nhiều. Điều thứ hai được chứng minh là rất khó thực hiện. Tôi đã tìm thấy mã trong tệp Trunmed.c của mã cho gói số liệu thống kê của R, nhưng nó không thể giải mã được.

Có ai biết về cách triển khai C được viết tốt cho thuật toán trung vị cuộn thời gian tuyến tính không?

Chỉnh sửa: Liên kết đến mã Trunmed.c http://google.com/codesearch/p?hl=vi&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c


Chỉ cần thực hiện một phương tiện di chuyển ... trung bình di chuyển có phần phức tạp hơn. Thử di chuyển dải phân cách trên googling.
Matt

Đã thử google và tìm kiếm mã google. Nó đã bật mã Trunmed.c và triển khai bằng ngôn ngữ khác cho một cổng SGI của mã Trunmed (theo những gì tôi có thể biết). Ngoài ra, thuật toán JRSS mà tôi đã trích dẫn dường như là thuật toán duy nhất trong loạt bài của tạp chí mà mã gốc không được lưu trữ.
AWB

Bạn có bao nhiêu số trong mỗi chuỗi thời gian? Ngay cả với một triệu trong số chúng, nếu bạn chỉ có vài nghìn con số, có thể không lâu hơn một hoặc hai phút để chạy (nếu mã của bạn được viết hiệu quả).
Dana the Sane

16
làm thế nào là giải pháp hai đống là tuyến tính? đó là O (n log k) trong đó k là kích thước cửa sổ vì độ xóa của đống là O (log k).
yairchu

3
Một số hiện thực và so sánh: github.com/suomela/median-filter
Jukka Suomela

Câu trả lời:


28

Tôi đã xem xét R src/library/stats/src/Trunmed.cmột vài lần vì tôi cũng muốn một cái gì đó tương tự trong một chương trình con C ++ class / C độc lập. Lưu ý rằng đây thực sự là hai triển khai trong một, hãy xem src/library/stats/man/runmed.Rd(nguồn của tệp trợ giúp) cho biết

\details{
  Apart from the end values, the result \code{y = runmed(x, k)} simply has
  \code{y[j] = median(x[(j-k2):(j+k2)])} (k = 2*k2+1), computed very
  efficiently.

  The two algorithms are internally entirely different:
  \describe{
    \item{"Turlach"}{is the Härdle-Steiger
      algorithm (see Ref.) as implemented by Berwin Turlach.
      A tree algorithm is used, ensuring performance \eqn{O(n \log
        k)}{O(n * log(k))} where \code{n <- length(x)} which is
      asymptotically optimal.}
    \item{"Stuetzle"}{is the (older) Stuetzle-Friedman implementation
      which makes use of median \emph{updating} when one observation
      enters and one leaves the smoothing window.  While this performs as
      \eqn{O(n \times k)}{O(n * k)} which is slower asymptotically, it is
      considerably faster for small \eqn{k} or \eqn{n}.}
  }
}

Sẽ thật tuyệt nếu nó được sử dụng lại theo một kiểu độc lập hơn. Bạn đang tình nguyện? Tôi có thể giúp với một số R bit.

Chỉnh sửa 1 : Ngoài liên kết đến phiên bản cũ của Trunmed.c ở trên, đây là các bản sao SVN hiện tại của

Chỉnh sửa 2 : Ryan Tibshirani có một số mã C và Fortran về binning trung bình nhanh có thể là điểm khởi đầu thích hợp cho cách tiếp cận theo cửa sổ.


Cảm ơn Dirk. Khi tôi nhận được một giải pháp sạch, tôi đang có kế hoạch phát hành nó theo GPL. Tôi cũng muốn thiết lập giao diện R và Python.
AWB

9
@AWB Điều gì đã xảy ra với ý tưởng này? Bạn đã kết hợp giải pháp của mình vào một gói chưa?
Xu Wang,

20

Tôi không thể tìm thấy cách triển khai hiện đại của cấu trúc dữ liệu c ++ với thống kê thứ tự, vì vậy tôi đã kết thúc việc triển khai cả hai ý tưởng trong liên kết lập trình hàng đầu do MAK đề xuất ( Match Editorial : cuộn xuống FloatingMedian).

Hai bộ nhiều

Ý tưởng đầu tiên phân vùng dữ liệu thành hai cấu trúc dữ liệu (heaps, multisets, v.v.) với O (ln N) mỗi lần chèn / xóa không cho phép thay đổi lượng tử tự động mà không tốn kém chi phí lớn. Tức là chúng ta có thể có trung bình cuộn hoặc 75% nhưng không phải cả hai cùng một lúc.

Cây phân đoạn

Ý tưởng thứ hai sử dụng cây phân đoạn là O (ln N) để chèn / xóa / truy vấn nhưng linh hoạt hơn. Hơn hết, "N" là kích thước của dải dữ liệu của bạn. Vì vậy, nếu trung bình luân phiên của bạn có cửa sổ là một triệu mục, nhưng dữ liệu của bạn thay đổi từ 1..65536, thì chỉ cần 16 thao tác cho mỗi chuyển động của cửa sổ luân chuyển là 1 triệu !!

Mã c ++ tương tự như những gì Denis đã đăng ở trên ("Đây là một thuật toán đơn giản cho dữ liệu lượng tử hóa")

Cây thống kê đơn hàng GNU

Ngay trước khi bỏ cuộc, tôi thấy rằng stdlibc ++ chứa cây thống kê thứ tự !!!

Chúng có hai hoạt động quan trọng:

iter = tree.find_by_order(value)
order = tree.order_of_key(value)

Xem libstdc ++ manual policy_based_data_structures_test (tìm kiếm "split and join").

Tôi đã gói cây để sử dụng trong một tiêu đề tiện lợi cho các trình biên dịch hỗ trợ typedefs một phần kiểu c ++ 0x / c ++ 11:

#if !defined(GNU_ORDER_STATISTIC_SET_H)
#define GNU_ORDER_STATISTIC_SET_H
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

// A red-black tree table storing ints and their order
// statistics. Note that since the tree uses
// tree_order_statistics_node_update as its update policy, then it
// includes its methods by_order and order_of_key.
template <typename T>
using t_order_statistic_set = __gnu_pbds::tree<
                                  T,
                                  __gnu_pbds::null_type,
                                  std::less<T>,
                                  __gnu_pbds::rb_tree_tag,
                                  // This policy updates nodes'  metadata for order statistics.
                                  __gnu_pbds::tree_order_statistics_node_update>;

#endif //GNU_ORDER_STATISTIC_SET_H

Trên thực tế, các vùng chứa mở rộng libstdc ++ không cho phép nhiều giá trị! Theo thiết kế! Theo đề xuất của tên tôi ở trên (t_order_statistic_set), nhiều giá trị được hợp nhất. Vì vậy, họ cần phải làm việc nhiều hơn một chút cho các mục đích của chúng tôi :-(
Leo Goodstadt

Chúng ta cần 1) tạo bản đồ các giá trị để đếm (thay vì tập hợp) 2) kích thước nhánh phải phản ánh số lượng các khóa (libstdc ++ - v3 / include / ext / pb_ds / detail / tree_policy / order_statistics_imp.hpp) kế thừa từ cây và 3) quá tải insert () để tăng số lượng / gọi update_to_top () nếu giá trị đã có sẵn 4) quá tải xóa () để giảm số lượng / gọi update_to_top () nếu giá trị không phải là duy nhất (Xem libstdc ++ - v3 / include / ext / pb_ds / detail / rb_tree_map_ / rb_tree_.hpp) Có tình nguyện viên nào không ??
Leo Goodstadt

15

Tôi đã thực hiện triển khai C ở đây . Một vài chi tiết khác trong câu hỏi này: Rolling median trong triển khai C - Turlach .

Sử dụng mẫu:

int main(int argc, char* argv[])
{
   int i,v;
   Mediator* m = MediatorNew(15);

   for (i=0;i<30;i++)
   {
      v = rand()&127;
      printf("Inserting %3d \n",v);
      MediatorInsert(m,v);
      v=MediatorMedian(m);
      printf("Median = %3d.\n\n",v);
      ShowTree(m);
   }
}

6
Triển khai tuyệt vời, nhanh chóng và rõ ràng dựa trên đống min-median-max. Công việc rất tốt.
Johannes Rudolph

Làm cách nào để tìm phiên bản Java của giải pháp này?
Hengameh,

10

Tôi sử dụng công cụ ước tính trung bình gia tăng này:

median += eta * sgn(sample - median)

có cùng dạng với công cụ ước tính trung bình phổ biến hơn:

mean += eta * (sample - mean)

Ở đây eta là một tham số tốc độ học tập nhỏ (ví dụ 0.001) và sgn()là hàm dấu hiệu trả về một trong số {-1, 0, 1}. (Sử dụng một hằng số etanhư thế này nếu dữ liệu không cố định và bạn muốn theo dõi các thay đổi theo thời gian; nếu không, đối với các nguồn tĩnh, hãy sử dụng một thứ gì đó như eta = 1 / nđể hội tụ, nsố lượng mẫu được nhìn thấy cho đến nay là bao nhiêu.)

Ngoài ra, tôi đã sửa đổi công cụ ước lượng trung bình để làm cho nó hoạt động cho các lượng tử tùy ý. Nói chung, một hàm lượng tử cho bạn biết giá trị chia dữ liệu thành hai phân số: p1 - p. Sau đây ước tính giá trị này tăng dần:

quantile += eta * (sgn(sample - quantile) + 2.0 * p - 1.0)

Giá trị pphải nằm trong [0, 1]. Về cơ bản, điều này làm dịch chuyển sgn()đầu ra đối xứng của hàm {-1, 0, 1}sang một bên, phân chia các mẫu dữ liệu thành hai thùng có kích thước không bằng nhau (các phân số p1 - pdữ liệu nhỏ hơn / lớn hơn ước tính lượng tử, tương ứng). Lưu ý rằng đối với p = 0.5, điều này giảm xuống công cụ ước tính trung bình.


2
Tuyệt vời, Đây là một sửa đổi điều chỉnh 'eta' dựa trên giá trị trung bình đang chạy ... (giá trị trung bình được sử dụng làm ước tính sơ bộ của giá trị trung bình để nó hội tụ trên các giá trị lớn với cùng tốc độ mà nó hội tụ trên các giá trị nhỏ). tức là eta được điều chỉnh tự động. stackoverflow.com/questions/11482529/…
Jeff McClintock

3
Để biết một kỹ thuật tương tự, hãy xem bài báo này về phát trực tuyến tiết kiệm: arxiv.org/pdf/1407.1121v1.pdf Nó có thể ước tính bất kỳ phần tư nào và thích ứng với những thay đổi về giá trị trung bình. Nó yêu cầu bạn chỉ lưu trữ hai giá trị: ước tính cuối cùng và hướng điều chỉnh cuối cùng (+1 hoặc -1). Thuật toán này rất dễ thực hiện. Tôi thấy rằng lỗi nằm trong khoảng 5% khoảng 97% thời gian.
Paul Chernoch

9

Đây là một thuật toán đơn giản cho dữ liệu lượng tử hóa (vài tháng sau):

""" median1.py: moving median 1d for quantized, e.g. 8-bit data

Method: cache the median, so that wider windows are faster.
    The code is simple -- no heaps, no trees.

Keywords: median filter, moving median, running median, numpy, scipy

See Perreault + Hebert, Median Filtering in Constant Time, 2007,
    http://nomis80.org/ctmf.html: nice 6-page paper and C code,
    mainly for 2d images

Example:
    y = medians( x, window=window, nlevel=nlevel )
    uses:
    med = Median1( nlevel, window, counts=np.bincount( x[0:window] ))
    med.addsub( +, - )  -- see the picture in Perreault
    m = med.median()  -- using cached m, summ

How it works:
    picture nlevel=8, window=3 -- 3 1s in an array of 8 counters:
        counts: . 1 . . 1 . 1 .
        sums:   0 1 1 1 2 2 3 3
                        ^ sums[3] < 2 <= sums[4] <=> median 4
        addsub( 0, 1 )  m, summ stay the same
        addsub( 5, 1 )  slide right
        addsub( 5, 6 )  slide left

Updating `counts` in an `addsub` is trivial, updating `sums` is not.
But we can cache the previous median `m` and the sum to m `summ`.
The less often the median changes, the faster;
so fewer levels or *wider* windows are faster.
(Like any cache, run time varies a lot, depending on the input.)

See also:
    scipy.signal.medfilt -- runtime roughly ~ window size
    http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c

"""

from __future__ import division
import numpy as np  # bincount, pad0

__date__ = "2009-10-27 oct"
__author_email__ = "denis-bz-py at t-online dot de"


#...............................................................................
class Median1:
    """ moving median 1d for quantized, e.g. 8-bit data """

    def __init__( s, nlevel, window, counts ):
        s.nlevel = nlevel  # >= len(counts)
        s.window = window  # == sum(counts)
        s.half = (window // 2) + 1  # odd or even
        s.setcounts( counts )

    def median( s ):
        """ step up or down until sum cnt to m-1 < half <= sum to m """
        if s.summ - s.cnt[s.m] < s.half <= s.summ:
            return s.m
        j, sumj = s.m, s.summ
        if sumj <= s.half:
            while j < s.nlevel - 1:
                j += 1
                sumj += s.cnt[j]
                # print "j sumj:", j, sumj
                if sumj - s.cnt[j] < s.half <= sumj:  break
        else:
            while j > 0:
                sumj -= s.cnt[j]
                j -= 1
                # print "j sumj:", j, sumj
                if sumj - s.cnt[j] < s.half <= sumj:  break
        s.m, s.summ = j, sumj
        return s.m

    def addsub( s, add, sub ):
        s.cnt[add] += 1
        s.cnt[sub] -= 1
        assert s.cnt[sub] >= 0, (add, sub)
        if add <= s.m:
            s.summ += 1
        if sub <= s.m:
            s.summ -= 1

    def setcounts( s, counts ):
        assert len(counts) <= s.nlevel, (len(counts), s.nlevel)
        if len(counts) < s.nlevel:
            counts = pad0__( counts, s.nlevel )  # numpy array / list
        sumcounts = sum(counts)
        assert sumcounts == s.window, (sumcounts, s.window)
        s.cnt = counts
        s.slowmedian()

    def slowmedian( s ):
        j, sumj = -1, 0
        while sumj < s.half:
            j += 1
            sumj += s.cnt[j]
        s.m, s.summ = j, sumj

    def __str__( s ):
        return ("median %d: " % s.m) + \
            "".join([ (" ." if c == 0 else "%2d" % c) for c in s.cnt ])

#...............................................................................
def medianfilter( x, window, nlevel=256 ):
    """ moving medians, y[j] = median( x[j:j+window] )
        -> a shorter list, len(y) = len(x) - window + 1
    """
    assert len(x) >= window, (len(x), window)
    # np.clip( x, 0, nlevel-1, out=x )
        # cf http://scipy.org/Cookbook/Rebinning
    cnt = np.bincount( x[0:window] )
    med = Median1( nlevel=nlevel, window=window, counts=cnt )
    y = (len(x) - window + 1) * [0]
    y[0] = med.median()
    for j in xrange( len(x) - window ):
        med.addsub( x[j+window], x[j] )
        y[j+1] = med.median()
    return y  # list
    # return np.array( y )

def pad0__( x, tolen ):
    """ pad x with 0 s, numpy array or list """
    n = tolen - len(x)
    if n > 0:
        try:
            x = np.r_[ x, np.zeros( n, dtype=x[0].dtype )]
        except NameError:
            x += n * [0]
    return x

#...............................................................................
if __name__ == "__main__":
    Len = 10000
    window = 3
    nlevel = 256
    period = 100

    np.set_printoptions( 2, threshold=100, edgeitems=10 )
    # print medians( np.arange(3), 3 )

    sinwave = (np.sin( 2 * np.pi * np.arange(Len) / period )
        + 1) * (nlevel-1) / 2
    x = np.asarray( sinwave, int )
    print "x:", x
    for window in ( 3, 31, 63, 127, 255 ):
        if window > Len:  continue
        print "medianfilter: Len=%d window=%d nlevel=%d:" % (Len, window, nlevel)
            y = medianfilter( x, window=window, nlevel=nlevel )
        print np.array( y )

# end median1.py

4

Trung vị cuộn có thể được tìm thấy bằng cách duy trì hai phân vùng số.

Để duy trì phân vùng, sử dụng Min Heap và Max Heap.

Max Heap sẽ chứa các số nhỏ hơn bằng số trung vị.

Min Heap sẽ chứa các số lớn hơn bằng số trung vị.

Ràng buộc cân bằng: nếu tổng số phần tử là chẵn thì cả hai đống phải có các phần tử bằng nhau.

nếu tổng số phần tử là lẻ thì Max Heap sẽ có nhiều phần tử hơn Min Heap.

Phần tử trung vị: Nếu Cả hai phân vùng có số phần tử bằng nhau thì phần tử trung vị sẽ là một nửa tổng của phần tử max từ phân vùng đầu tiên và phần tử min từ phân vùng thứ hai.

Nếu không trung vị sẽ là phần tử tối đa từ phân vùng đầu tiên.

Thuật toán-
1- Lấy hai Heap (1 Min Heap và 1 Max Heap)
   Max Heap sẽ chứa nửa số phần tử đầu tiên
   Min Heap sẽ chứa nửa sau số phần tử

2- So sánh số mới từ luồng với đầu Max Heap, 
   nếu nó nhỏ hơn hoặc bằng thì hãy thêm số đó trong đống tối đa. 
   Nếu không, hãy thêm số trong Min Heap.

3- nếu min Heap có nhiều phần tử hơn Max Heap 
   sau đó loại bỏ phần tử trên cùng của Min Heap và thêm vào Max Heap.
   nếu đống tối đa có nhiều phần tử hơn trong đống tối thiểu 
   sau đó loại bỏ phần tử trên cùng của Max Heap và thêm vào Min Heap.

4- Nếu Cả hai đống có số phần tử bằng nhau thì
   trung vị sẽ là một nửa tổng của phần tử max từ Max Heap và phần tử min từ Min Heap.
   Nếu không, trung vị sẽ là phần tử tối đa từ phân vùng đầu tiên.
public class Solution {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        RunningMedianHeaps s = new RunningMedianHeaps();
        int n = in.nextInt();
        for(int a_i=0; a_i < n; a_i++){
            printMedian(s,in.nextInt());
        }
        in.close();       
    }

    public static void printMedian(RunningMedianHeaps s, int nextNum){
            s.addNumberInHeap(nextNum);
            System.out.printf("%.1f\n",s.getMedian());
    }
}

class RunningMedianHeaps{
    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(Comparator.reverseOrder());

    public double getMedian() {

        int size = minHeap.size() + maxHeap.size();     
        if(size % 2 == 0)
            return (maxHeap.peek()+minHeap.peek())/2.0;
        return maxHeap.peek()*1.0;
    }

    private void balanceHeaps() {
        if(maxHeap.size() < minHeap.size())
        {
            maxHeap.add(minHeap.poll());
        }   
        else if(maxHeap.size() > 1+minHeap.size())
        {
            minHeap.add(maxHeap.poll());
        }
    }

    public void addNumberInHeap(int num) {
        if(maxHeap.size()==0 || num <= maxHeap.peek())
        {
            maxHeap.add(num);
        }
        else
        {
            minHeap.add(num);
        }
        balanceHeaps();
    }
}

Tôi không rõ câu trả lời Java thứ ba mang lại lợi ích bao nhiêu cho câu hỏi C. Bạn nên đặt một câu hỏi mới và sau đó cung cấp câu trả lời Java của bạn cho câu hỏi đó.
jww 19/02/17

logic chết sau khi đọc điều này 'sau đó loại bỏ phần tử trên cùng của Min Heap và thêm vào Min Heap.' Ít nhất hãy có phép lịch sự để đọc thuật ngữ trước khi đăng
Cyclotron3x3

4
Thuật toán này không dành cho trung vị cuộn mà là trung vị của số lượng phần tử ngày càng tăng. Đối với đường trung bình cuộn, người ta cũng phải xóa một phần tử khỏi đống, phần tử này cần được tìm thấy trước.
Walter

2

Có thể đáng để chỉ ra rằng có một trường hợp đặc biệt có giải pháp đơn giản chính xác: khi tất cả các giá trị trong luồng là số nguyên trong một phạm vi xác định (tương đối) nhỏ. Ví dụ: giả sử tất cả chúng phải nằm trong khoảng từ 0 đến 1023. Trong trường hợp này, chỉ cần xác định một mảng gồm 1024 phần tử và một số lượng, đồng thời xóa tất cả các giá trị này. Đối với mỗi giá trị trong luồng tăng thùng tương ứng và số lượng. Sau khi luồng kết thúc, hãy tìm thùng chứa số / 2 giá trị cao nhất - dễ dàng hoàn thành bằng cách thêm các thùng liên tiếp bắt đầu từ 0. Sử dụng cùng một phương pháp, có thể tìm thấy giá trị của thứ tự xếp hạng tùy ý. (Sẽ có một sự phức tạp nhỏ nếu phát hiện độ bão hòa của thùng và "nâng cấp" kích thước của thùng lưu trữ lên loại lớn hơn trong quá trình chạy.)

Trường hợp đặc biệt này có vẻ giả tạo, nhưng trong thực tế nó rất phổ biến. Nó cũng có thể được áp dụng như một phép gần đúng cho các số thực nếu chúng nằm trong một phạm vi và mức độ chính xác "đủ tốt" được biết đến. Điều này sẽ phù hợp với khá nhiều tập hợp các phép đo trên một nhóm các đối tượng "thế giới thực". Ví dụ: chiều cao hoặc cân nặng của một nhóm người. Không phải là một bộ đủ lớn? Nó sẽ hoạt động tốt đối với chiều dài hoặc trọng lượng của tất cả (cá thể) vi khuẩn trên hành tinh - giả sử ai đó có thể cung cấp dữ liệu!

Có vẻ như tôi đã đọc nhầm bản gốc - có vẻ như nó muốn có dải phân cách cửa sổ trượt thay vì chỉ là dải phân cách của một con suối rất dài. Cách tiếp cận này vẫn hoạt động cho điều đó. Tải N giá trị luồng đầu tiên cho cửa sổ ban đầu, sau đó cho giá trị luồng thứ N + 1, tăng bin tương ứng trong khi giảm bin tương ứng với giá trị luồng thứ 0. Trong trường hợp này, cần giữ lại N giá trị cuối cùng để cho phép giảm, điều này có thể được thực hiện một cách hiệu quả bằng cách giải quyết theo chu kỳ một mảng có kích thước N. Vì vị trí của trung vị chỉ có thể thay đổi -2, -1,0,1 , 2 trên mỗi bước của cửa sổ trượt, không cần thiết phải tính tổng tất cả các thùng cho đến dải phân cách trên mỗi bước, chỉ cần điều chỉnh "con trỏ trung vị" tùy thuộc vào (các) thùng bên nào đã được sửa đổi. Ví dụ, nếu cả giá trị mới và giá trị bị xóa đều nằm dưới giá trị trung bình hiện tại thì nó không thay đổi (offset = 0). Phương thức bị phá vỡ khi N trở nên quá lớn để có thể lưu giữ thuận tiện trong bộ nhớ.


1

Nếu bạn có khả năng tham chiếu các giá trị như một hàm của điểm trong thời gian, bạn có thể lấy mẫu các giá trị thay thế, áp dụng khởi động hệ thống để tạo ra giá trị trung bình được khởi động trong khoảng tin cậy. Điều này có thể cho phép bạn tính giá trị trung bình gần đúng với hiệu quả cao hơn so với việc liên tục sắp xếp các giá trị nhận vào thành cấu trúc dữ liệu.


1

Đối với những người cần trung gian chạy trong Java ... PriorityQueue là bạn của bạn. O (log N) chèn, O (1) trung vị hiện tại, và O (N) loại bỏ. Nếu bạn biết phân phối dữ liệu của mình, bạn có thể làm tốt hơn nhiều điều này.

public class RunningMedian {
  // Two priority queues, one of reversed order.
  PriorityQueue<Integer> lower = new PriorityQueue<Integer>(10,
          new Comparator<Integer>() {
              public int compare(Integer arg0, Integer arg1) {
                  return (arg0 < arg1) ? 1 : arg0 == arg1 ? 0 : -1;
              }
          }), higher = new PriorityQueue<Integer>();

  public void insert(Integer n) {
      if (lower.isEmpty() && higher.isEmpty())
          lower.add(n);
      else {
          if (n <= lower.peek())
              lower.add(n);
          else
              higher.add(n);
          rebalance();
      }
  }

  void rebalance() {
      if (lower.size() < higher.size() - 1)
          lower.add(higher.remove());
      else if (higher.size() < lower.size() - 1)
          higher.add(lower.remove());
  }

  public Integer getMedian() {
      if (lower.isEmpty() && higher.isEmpty())
          return null;
      else if (lower.size() == higher.size())
          return (lower.peek() + higher.peek()) / 2;
      else
          return (lower.size() < higher.size()) ? higher.peek() : lower
                  .peek();
  }

  public void remove(Integer n) {
      if (lower.remove(n) || higher.remove(n))
          rebalance();
  }
}

c ++ có cây thống kê thứ tự từ gnu trong phần mở rộng cho thư viện chuẩn. Xem bài viết của tôi dưới đây.
Leo Goodstadt

Tôi nghĩ rằng mã của bạn không được đặt ở đây một cách chính xác. Có một số phần chưa hoàn chỉnh ở đó như: }), higher = new PriorityQueue<Integer>();hoặc new PriorityQueue<Integer>(10,. Tôi không thể chạy mã.
Hengameh

@Hengameh Java kết thúc câu lệnh bằng dấu chấm phẩy - ngắt dòng không quan trọng chút nào. Bạn phải đã sao chép nó không chính xác.
Ma-thi-ơ Đọc

Bạn nên đặt một câu hỏi mới và sau đó cung cấp câu trả lời Java của bạn cho câu hỏi đó.
jww 19/02/17

0

Đây là một cái có thể được sử dụng khi đầu ra chính xác không quan trọng (cho mục đích hiển thị, v.v.) Bạn cần tổng số tiền và số người cuối cùng, cộng với giá trị mới.

{
totalcount++;
newmedian=lastmedian+(newvalue>lastmedian?1:-1)*(lastmedian==0?newvalue: lastmedian/totalcount*2);
}

Tạo ra kết quả khá chính xác cho những thứ như page_display_time.

Quy tắc: luồng đầu vào cần phải trôi chảy theo thứ tự thời gian hiển thị trang, số lượng lớn (> 30, v.v.) và có trung vị khác 0.

Ví dụ: thời gian tải trang, 800 mục, 10ms ... 3000ms, trung bình 90ms, trung bình thực: 11ms

Sau 30 lần đầu vào, lỗi trung bình thường <= 20% (9ms..12ms) và ngày càng ít hơn. Sau 800 đầu vào, lỗi là + -2%.

Một nhà tư tưởng khác có giải pháp tương tự ở đây: Bộ lọc trung vị Triển khai siêu hiệu quả


-1

Đây là triển khai java

package MedianOfIntegerStream;

import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;


public class MedianOfIntegerStream {

    public Set<Integer> rightMinSet;
    public Set<Integer> leftMaxSet;
    public int numOfElements;

    public MedianOfIntegerStream() {
        rightMinSet = new TreeSet<Integer>();
        leftMaxSet = new TreeSet<Integer>(new DescendingComparator());
        numOfElements = 0;
    }

    public void addNumberToStream(Integer num) {
        leftMaxSet.add(num);

        Iterator<Integer> iterMax = leftMaxSet.iterator();
        Iterator<Integer> iterMin = rightMinSet.iterator();
        int maxEl = iterMax.next();
        int minEl = 0;
        if (iterMin.hasNext()) {
            minEl = iterMin.next();
        }

        if (numOfElements % 2 == 0) {
            if (numOfElements == 0) {
                numOfElements++;
                return;
            } else if (maxEl > minEl) {
                iterMax.remove();

                if (minEl != 0) {
                    iterMin.remove();
                }
                leftMaxSet.add(minEl);
                rightMinSet.add(maxEl);
            }
        } else {

            if (maxEl != 0) {
                iterMax.remove();
            }

            rightMinSet.add(maxEl);
        }
        numOfElements++;
    }

    public Double getMedian() {
        if (numOfElements % 2 != 0)
            return new Double(leftMaxSet.iterator().next());
        else
            return (leftMaxSet.iterator().next() + rightMinSet.iterator().next()) / 2.0;
    }

    private class DescendingComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }

    public static void main(String[] args) {
        MedianOfIntegerStream streamMedian = new MedianOfIntegerStream();

        streamMedian.addNumberToStream(1);
        System.out.println(streamMedian.getMedian()); // should be 1

        streamMedian.addNumberToStream(5);
        streamMedian.addNumberToStream(10);
        streamMedian.addNumberToStream(12);
        streamMedian.addNumberToStream(2);
        System.out.println(streamMedian.getMedian()); // should be 5

        streamMedian.addNumberToStream(3);
        streamMedian.addNumberToStream(8);
        streamMedian.addNumberToStream(9);
        System.out.println(streamMedian.getMedian()); // should be 6.5
    }
}

Bạn nên đặt một câu hỏi mới và sau đó cung cấp câu trả lời Java của bạn cho câu hỏi đó.
jww 19/02/17

-4

Nếu bạn chỉ yêu cầu giá trị trung bình được làm mịn, một cách nhanh chóng / dễ dàng là nhân giá trị mới nhất với x và giá trị trung bình với (1-x) rồi cộng chúng. Sau đó giá trị này trở thành mức trung bình mới.

chỉnh sửa: Không phải những gì người dùng yêu cầu và không hợp lệ về mặt thống kê nhưng đủ tốt cho nhiều mục đích sử dụng.
Tôi sẽ để nó ở đây (bất chấp số phiếu phản đối) để tìm kiếm!


2
Điều này tính toán giá trị trung bình. Anh ấy muốn trung phong. Ngoài ra, anh ta đang tính toán giá trị trung bình của một cửa sổ trượt, không phải của toàn bộ tập hợp.
A. Levy

1
Công cụ này tính toán mức trung bình đang hoạt động của một cửa sổ giá trị với hằng số giảm dần phụ thuộc vào X - nó rất hữu ích khi hiệu suất quan trọng và bạn không thể bận tâm khi thực hiện bộ lọc kalman. Tôi đưa nó vào để tìm kiếm có thể tìm thấy nó.
Martin Beckett

Đây là điều tôi cũng ngay lập tức nghĩ đến, đã triển khai một bộ lọc như một bộ lọc thông thấp rất cơ bản và rẻ cho một ứng dụng âm thanh.
James Morris
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.