如何实时计算数据流中位数

本文介绍了使用最小堆和最大堆实现的流式中位数计算器,当接收到连续输入数据时,实时更新堆结构以计算并返回当前数据流的中位数。
摘要由CSDN通过智能技术生成

排序取中位数

最容易想到的思路就是直接将所有数据进行排序,然后取排序之后的中位数

不过具体的排序思路也有几种

  • 插入排序。在新的数据到达时插入有序序列
  • 平衡二叉树。插入平衡二叉树,然后取中位数

最大堆最小堆

创建一个最小堆和最大堆,其中最小堆的最小值比最大堆的最大值还大,并使最小堆最大堆数量保持均衡,那么中位数边取乎于最小最大堆堆顶

// MinHeap is a min-heap implementation.
type MinHeap []int

// Len returns the length of the heap.
func (h MinHeap) Len() int { return len(h) }

// Less compares two elements in the heap.
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }

// Swap swaps two elements in the heap.
func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }

// Push adds an element to the heap.
func (h *MinHeap) Push(x interface{}) {
	*h = append(*h, x.(int))
}

// Pop removes and returns the smallest element from the heap.
func (h *MinHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// MaxHeap is a max-heap implementation.
type MaxHeap []int

// Len returns the length of the heap.
func (h MaxHeap) Len() int { return len(h) }

// Less compares two elements in the heap.
func (h MaxHeap) Less(i, j int) bool { return h[i] > h[j] }

// Swap swaps two elements in the heap.
func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }

// Push adds an element to the heap.
func (h *MaxHeap) Push(x interface{}) {
	*h = append(*h, x.(int))
}

// Pop removes and returns the largest element from the heap.
func (h *MaxHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// StreamingMedianCalculator calculates the median from a continuous stream of input data.
type StreamingMedianCalculator struct {
	minHeap MinHeap
	maxHeap MaxHeap
}

// AddData adds a new data point to the calculator.
func (s *StreamingMedianCalculator) AddData(data int) {
	// 加入到对应堆,以是否大于最小堆顶值为区分
	if len(s.minHeap) == 0 || data >= s.minHeap[0] {
		heap.Push(&s.minHeap, data)
	} else {
		heap.Push(&s.maxHeap, data)
	}

	// 均衡两堆数量
	if len(s.minHeap)-len(s.maxHeap) > 1 {
		heap.Push(&s.maxHeap, heap.Pop(&s.minHeap))
	} else if len(s.maxHeap)-len(s.minHeap) > 1 {
		heap.Push(&s.minHeap, heap.Pop(&s.maxHeap))
	}
}

// GetMedian returns the current median.
func (s *StreamingMedianCalculator) GetMedian() float64 {
	if len(s.minHeap) == len(s.maxHeap) {
		return float64(s.minHeap[0]+s.maxHeap[0]) / 2
	} else if len(s.minHeap) > len(s.maxHeap) {
		return float64(s.minHeap[0])
	} else {
		return float64(s.maxHeap[0])
	}
}
计算数据流中位数可以通过Flink的ProcessFunction来实现。 具体实现步骤如下: 1. 将数据流按照大小排序 2. 计算数据流的长度,如果是奇数,则中位数为第 (length+1)/2 个元素;如果是偶数,则中位数为第length/2个元素和第(length/2+1)个元素的平均值。 3. 在ProcessFunction的实现中,可以使用状态变量来保存数据流的有序列表,并计算中位数。 以下是一个简单的示例代码: ```java public class MedianFunction extends ProcessFunction<Integer, Double> { private ListState<Integer> values; @Override public void open(Configuration parameters) throws Exception { super.open(parameters); values = getRuntimeContext().getListState(new ListStateDescriptor<Integer>("values", Integer.class)); } @Override public void processElement(Integer value, Context ctx, Collector<Double> out) throws Exception { values.add(value); List<Integer> sortedValues = new ArrayList<>(); for (Integer v : values.get()) { sortedValues.add(v); } Collections.sort(sortedValues); int length = sortedValues.size(); if (length % 2 == 0) { double median = (sortedValues.get(length/2) + sortedValues.get(length/2 - 1)) / 2.0; out.collect(median); } else { double median = sortedValues.get(length/2); out.collect(median); } } } ``` 在上述代码中,我们使用了ListState来保存数据流中的元素,并在每次处理新元素时重新排序并计算中位数。注意,这只是一个简单的示例,实际应用中需要考虑更多的问题,比如数据倾斜、数据丢失等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值