哈希分治法 - 统计海量数据中出现次数最多的前10个IP

场景

这是一个 ip 地址 127.0.0.1
假设有100亿个这样的 ip 地址存在文件中
这个文件大小大约是 100GB
问题:要统计出100亿个 ip 中,重复出现次数最多的前10个

分析

100GB 几乎不可能一次加载进内存进行操作,所以必须要拆分
那么可以利用分治的思想,把规模大的问题化小,然后解决各个小的问题,最后得出结果。

实现思路

  • ipv4 地址是一个 32 位的整数,可以用 uint 保存。
  • 我先设计一个哈希函数,把100个G的文件分成10000份,每份大约是 10MB,可以加载进内存了。

例如:我设计一个简单的哈希函数是 f(ip) = ip % 10000,(ip 是个32位整数)
那么 5 % 10000 = 5,不管 5 在哪个地方 5 % 10000 的结果都是 5,这就保证了相同的 ip 会被放在同一个子文件中,方便统计,相同的元素经过同一个哈希函数,得出的哈希值是一样的。
那么我把100亿个 ip,都进行 ip % 10000 的操作,就完成了 100GB 文件分解成 10000个子文件的任务了。当然实际中哈希函数的选取很重要,尽量使得元素分布均匀,哈希冲突少的函数才是最好的。

记住,我把上面这个分解的过程叫做 Map,由一台叫 master 的计算机完成这个工作。

  • 10MB 的小文件加进内存,统计出出现次数最多的那个ip

10MB 的小文件里面存着很多 ip,他们虽然是乱序的,但是相同的 ip 会映射到同一个文件中来!
那么可以用二叉树统计出现次数,二叉树节点保存(ip, count)的信息,把所有 ip 插入到二叉树中,如果这个 ip 不存在,那么新建一个节点, count 标记 1,如果有,那么把 count++,最终遍历一遍树,就能找出 count 最大的 ip 了。

我把这个过程叫做 Reduce,由很多台叫 worker 的计算机来完成。
每个 worker 至少要找出最大的前10个 ip 返回给 master,master 最后会收集到 10000 * 10 个 ip,大约 400KB,然后再找出最大的前 10 个 ip 就可以了。
最简单的遍历10遍,每次拿个最大值出来就可以了,或者用快速排序,堆排序,归并排序等等方法,找出最大前 k 个数也行。

MapReduce

我刚刚除了介绍了一种海量数据的哈希分治算法之外,还穿插了一个谷歌的 MapReduce 分布式并行编程模型,原理就是上面说的那些了,有兴趣的可以去详细了解。

哈希函数是什么?哈希函数是把大空间的元素映射到一个小空间里。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 可以使用哈希表来统计整型序列每个整数出现次数,然后找出出现次数最多的整数及其出现次数即可。具体步骤如下: 1. 创建一个哈希表,用于记录整数出现次数。 2. 遍历整型序列,对于每个整数,如果它已经在哈希出现过,则将其出现次数加1;否则,在哈希新增一个键值对,键为该整数,值为1。 3. 遍历哈希表,找出出现次数最多的整数及其出现次数。可以使用一个变量来记录当出现次数最多的整数,以及另一个变量来记录该整数出现次数。遍历哈希表时,如果当键对应的值大于记录的出现次数,则更新记录的整数和出现次数。 4. 返回出现次数最多的整数及其出现次数。 示例代码如下: ```python def find_most_frequent(nums): freq = {} for num in nums: if num in freq: freq[num] += 1 else: freq[num] = 1 max_num = None max_freq = for num, count in freq.items(): if count > max_freq: max_num = num max_freq = count return max_num, max_freq ``` 使用示例: ```python >>> nums = [1, 2, 3, 2, 2, 4, 5, 5, 5] >>> find_most_frequent(nums) (2, 3) ``` 在上面的示例,整型序列为[1, 2, 3, 2, 2, 4, 5, 5, 5],出现次数最多的整数为2,出现了3次。 ### 回答2: 统计一个整型序列出现次数最多的整数及其出现次数是一道非常基本和常见的问题。对于这道问题,我们可以使用哈希表和数组来解决。 使用哈希表的方法: 我们可以使用哈希表来统计整数在整型序列出现次数,具体步骤如下: 1. 首先初始化一个空的哈希表,用于存储整数和对应的出现次数。 2. 遍历整型序列的每一个整数,将其作为哈希表的键,将值初始化为1,并将其存入哈希。 3. 如果当整数已经在哈希存在,就将对应的值加1。 4. 遍历完成后,我们就可以得到整型序列出现次数最多的整数以及其出现次数。 使用数组的方法: 我们也可以使用数组来统计整数在整型序列出现次数,具体步骤如下: 1. 首先初始化一个大小为整型序列的最大值的数组,用于统计每个整数在序列出现次数。 2. 遍历整型序列的每一个整数,将其作为数组下标,将对应的值加1。 3. 遍历完成后,我们就可以得到整型序列出现次数最多的整数以及其出现次数。 无论是使用哈希表还是数组来解决这道问题,我们都需要注意以下几点: 1. 在统计整数出现次数时,需要注意整数可能是负数,所以需要将其转换为非负数。 2. 在遍历整型序列时,需要注意序列可能存在重复的整数,这时需要分别对其进行计数。 ### 回答3: 统计一个整型序列出现次数最多的整数及其出现次数,可以采用哈希表来实现。首先,遍历整个序列,将每个元素作为键,在哈希进行查找,若存在,则将该键对应的值加1;若不存在,则将该键添加到哈希,并将其值设置为1。遍历完整个序列后,在哈希找到值最大的键,即为出现次数最多的整数,其对应的值即为该整数出现次数。 具体实现步骤如下: 1. 创建一个哈希表,用来记录每个整数出现次数,初始为空。 2. 遍历整个序列,对于每个元素,将其作为键在哈希进行查找。 3. 若该键已经存在于哈希,则将该键对应的值加1;若不存在,则将该键添加到哈希,并将其值设置为1。 4. 遍历结束后,在哈希查找值最大的键,即为出现次数最多的整数。 5. 返回出现次数最多的整数及其出现次数。 具体代码实现如下: ``` #include <iostream> #include <unordered_map> #include <vector> using namespace std; pair<int, int> find_most_frequent(const vector<int>& nums) { unordered_map<int, int> hash_table; for (const auto& num : nums) { if (hash_table.find(num) != hash_table.end()) { hash_table[num]++; } else { hash_table[num] = 1; } } int max_num = 0, max_count = 0; for (const auto& pair : hash_table) { if (pair.second > max_count) { max_num = pair.first; max_count = pair.second; } } return {max_num, max_count}; } int main() { vector<int> nums = {1, 2, 3, 1, 2, 3, 4, 5, 1, 2, 4, 5, 6}; auto result = find_most_frequent(nums); cout << "出现次数最多的整数为:" << result.first << endl; cout << "它的出现次数为:" << result.second << endl; return 0; } ``` 在上面的代码,我们使用了 C++11 新增的 std::unordered_map(哈希表)容器来实现,该容器提供了 O(1) 的插入、查找和删除操作,能够高效地处理本题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值