数据流基本问题--确定频繁元素

下面可以谈论下如何求频繁元素的一个问题。

一、问题定义

如果一个数据流,其中m为数据流的大小,。我们可以定义每个元素出现的次数为,其中为第i个元素出现的次数。容易得出:

如果给定参数k,我们想求出所有出现次数超过m/k的元素。也就是输出集合:。下面我们先从一个简单特例入手,对这个问题进行分析和解决。

二.简单情况

有个经典的过半元素查找问题,在编程之美里面也有分析(寻找发帖水王)。就是说给定一个数组,找出出现次数超过一半的元素。对于这个问题一个比较好的做法就是不断删除两个不同的元素,最终剩下的元素就是所要求的元素。使用一个变量ans作为候选。该方法时间复杂度为O(n),空间复杂度为O(1)。具体实现如下:

int majority_element(vector<int>nums) {
	int len=nums.size,count=0,ans=0;
	for(int i=0;i<len;i++){
		if(count==0){
			ans=nums[i];
			count++;
		}else{
			if(ans==nums[i]){
				count++;
			}else{
				count--;
			}
		}
	}
	return ans;
}

三、一般情况

编程之美里面在解决了这个问题之后,提出了一个扩展问题,就是说如何找出发帖总数目的1/4的ID。一般化的情况就是我们在第一节中定义的问题。那一般化的问题如何解决呢?

该问题的解决方法就是对第二节中方法的改进。第二节中k=2,使用一个变量作为候选。这里,我们使用一个大小为k-1的变量作为候选,因为至多有k-1个元素的出现次数超过m/k,否则不符合实际情况。具体实现我们可以使用一个map,map里存放元素及其出现的次数。访问到第i个元素的时候,如果这个元素在map中,则将对应出现次数加1。否则看map的大小。如果map的大小少于k-1,我们将其添加到map中,设其出现次数为1。否则我们将map里面所有的元素次数都减一,如果某元素对应的次数为0,则从map中删去该元素。具体实现代码如下。

void frequent_element(vector<int>nums,int k) {
	map<int,int>num_cnt_map;
	for(int i=0;i<nums.size();i++){
		if(num_cnt_map.find(nums[i])!=num_cnt_map.end()){
			num_cnt_map[nums[i]]=num_cnt_map[nums[i]]+1;
		}else if(num_cnt_map.size()<k-1){
			num_cnt_map[nums[i]]=1;
		}else{
			for(map<int,int>::iterator it=num_cnt_map.begin();it!=num_cnt_map.end();it++){
				num_cnt_map[it->first]=num_cnt_map[it->first]-1;
				if(num_cnt_map[it->first]==0){
					num_cnt_map.erase(it);
				}
			}
		}
	}
}

下面我们对算法进行分析。在map中的元素次数都减一这个步骤(第9行到第14行的循环),一共减去了k(map中总共k个元素,每个元素对应次数减1)。所有元素出现的总次数为m,所以该步骤至多执行m/k次。显然任意出现次数超过m/k的元素一定最后仍留在map中。但是上述算法有个缺点就是:如果出现次数超过m/k的元素非常少,那么最终map中的某些元素不一定大于m/k。最终map中元素可能也没有k-1个。这时候我们需要对原数据流再做一次扫描,从而确定候选元素的出现次数,从而得出精确解。

四、进一步分析

我们可以通过第三节中的算法来对频繁元素出现的次数进行估计。对于任意频繁元素j,其出现次数为,最终估计值就是算法执行结束后num_cnt_map[j]。则两者有如下关系:

如果我们知道算法处理结束时map中元素对应次数的和为,所以map中元素次数都减一这个步骤总共减去了,所以最多执行了次。则上面的不等式中的下界可以进一步提高:

时间: 2024-10-05 04:45:36

数据流基本问题--确定频繁元素的相关文章

数据流基本问题--确定频繁元素(二)

我们之前在数据流基本问题--确定频繁元素(一)中提到了频繁元素的一个计算问题(找出出现次数超过m/k的元素),里面的算法返回的结果里肯定包含出现次数超过m/k的元素,但是也可能包含不超过m/k的元素(false positive).对于这个缺点,必须得进行额外一次的重新扫描,以确定最终答案.我们只允许进行一次的扫描,那么该怎么去做呢?这里我们简单讨论下lossy counting算法. 其实问题是一样的,这里换个说法来叙述.我们定义两个参数,一个是支持度阈值s,一个是允许错误范围参数().如果我

数据流基本问题--独立元素计数(二)

该篇文章中问题的定义和上一篇文章<数据流基本问题--独立元素计数(一)>点击打开链接中一样.不过该篇文章里能给出-近似的算法. 一.算法的基本思想 该篇文章中的算法是上一篇文章中算法的改进.这里不再记录流中元素zeros(h(j))的最大值,而是将满足所有的元素放入到B中.如果流中有d个独立元素,那么平均会有个元素满足.所以应该是d的一个比较好的估计(|B|指的是B中元素总数目). 二.算法的具体过程 算法的伪代码如下图所示.将一个元素j添加到B中时,是以g(j)和zeros(h(j))组成一

数据流基本问题---采样问题

在数据流处理中的一个常见问题就是数据采样问题.我们希望从流中选择一个子集,以便能够对它进行查询并给出统计性上对整个流具有代表性的结果. 一.蓄水池采样 具体问题是我们要从数据流中随机抽取k个元素.如果数据流长度m事先已经知道,那这个问题就非常简单,每个元素以k/m的概率选取即可.但这个问题要求m未知,那就不太好搞了.这个问题的解法是保存一个k大小的窗口.数据流的前k个元素依次加入到窗口.对于数据流第i个元素(i>k),以k/i的概率替换窗口中的某个元素.最终窗口的元素出现概率均为k/m. 下面使

数据流基本问题--矩估计

在前面我们谈到了独立元素计数的问题.在本文中,我们将独立元素计数问题推广到更一般的问题,也就是矩估计问题.我们将先介绍矩的定义,然后介绍一个无偏的估计方法,最后介绍如何改进结果.这里还是讨论内存容量不够的情况. 一.矩估计 如果一个数据流,其中m为数据流的大小,.我们可以定义每个元素 出现的次数为,其中为第i个元素出现的次数.则流的k阶矩(k-th moment)是所有出现次数取k次方的和. 下面我们看看k的一些特殊取值. 1.如果k=0时,,所以0阶矩就是数据流中独立元素数目,我们可以使用前面

Leetcode 703题数据流中的第K大元素(Kth Largest Element in a Stream)Java语言求解

题目链接 https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/ 题目内容 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素.你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素.每次调用 KthLargest.add,返回当前数据流中第K大的元素. 示例: int k = 3; int[] arr = [4,5,8

数据流算法

在有些情况下,数据以一个或者多个流的方式到来,如果不对数据进行及时的存储和处理,数据将永远丢失.这就是我们所说的数据流问题. 既然说是数据流,说明我们只能以流的方式一个一个按顺序访问数据.也就是说没法像访问访问数组一样随机访问数据流中任意位置的元素.我们只能对数据进行少数次的整体扫描,最好情况只扫描数据一次. 一.有关结论 在数据流算法里面,有2个一般化结论为值得牢记: 1.通常情况下,获得问题的近似解比精确解要高效得多. 2.为了产生与精确解相当接近的近似解,与哈希相关的技术被证明十分有用.

FP - growth 发现频繁项集

FP - growth是一种比Apriori更高效的发现频繁项集的方法.FP是frequent pattern的简称,即常在一块儿出现的元素项的集合的模型.通过将数据集存储在一个特定的FP树上,然后发现频繁项集或者频繁项对.通常,FP-growth算法的性能比Apriori好两个数量级以上. FP树与一般的树结构类似,但它通过链接(Link)来连接相似元素,被连起来的元素项可以看成一个链表. 上图是一棵FP树,一个元素项可以在一棵FP树种出现多次,FP树的节点会存储项集的出现频率,每个项集会以路

Direct3D中 SetStreamSource 函数与数据流

Microsoft? DirectX? 8.0引入了数据流的概念,用来把数据绑定到着色器使用的输入寄存器.一个数据流是一个成员数据的数组,每个成员由一个或多个元素构成,这些元素代表单个实体,如位置.法向.颜色等等.数据流使图形芯片能并行地从多个顶点缓存执行直接内存访问(DMA)操作,同时也降低了多重纹理的开销.可以这样理解数据流: 一个顶点由n个数据流组成.  一个数据流由m个元素组成.  一个元素是[位置.颜色.法向.纹理坐标]. IDirect3DDevice9::SetStreamSour

FP-growth算法发现频繁项集(二)——发现频繁项集

上篇介绍了如何构建FP树,FP树的每条路径都满足最小支持度,我们需要做的是在一条路径上寻找到更多的关联关系. 抽取条件模式基 首先从FP树头指针表中的单个频繁元素项开始.对于每一个元素项,获得其对应的条件模式基(conditional pattern base),单个元素项的条件模式基也就是元素项的关键字.条件模式基是以所查找元素项为结尾的路径集合.每一条路径其实都是一条前辍路径(perfix path).简而言之,一条前缀路径是介于所査找元素项与树根节点之间的所有内容. 下图是以{s:2}或{