首页 > 编程语言 >一种计数算法

一种计数算法

时间:2024-01-21 16:44:07浏览次数:36  
标签:arr int range len 一种 计数 算法 随机 思路

前言

常见的一个问题: 给定一个整形数组, 统计其中有多少唯一的元素.

常见的思路有哪些呢?

  1. 元素去重并统计, 利用哈希表进行去重计数.
  2. 数组排序后统计

以上空间复杂度均与元素数量关联, 如果允许损失精度, 是否可以使用较低的空间占用来统计呢?

  • 利用布隆过滤器是一种的一种

但是, 我在这篇文章看到了一种全新的思路. 尽管并不建议在生产环境中使用, 但仍不失为一种思路.

统计思路

这种思路简单说就是: "采样". 就像是统计一个湖泊中鱼的数量, 并不会一次性将所有的鱼都捞上来. 而是先钓n 条鱼上来, 给他们都做上记号. 几天后再钓 n 条鱼上来, 看其中有多少个有记号的鱼. 从而来估算整个湖泊中鱼的总数.

这个思路是否也可以在这个问题上借鉴呢?

通常的统计方式如下:

func count(arr []int) int {
	m := make(map[int]bool)
	for _, i := range arr {
		m[i] = true
	}
	return len(m)
}

好, 现在开始利用"采样"的思路, 丢失精确度, 降低空间复杂度.

我们将一半的元素放到hash表中保存:

func count(arr []int) int {
	m := make(map[int]bool)
	for _, i := range arr {
		if rand.Intn(2) == 0 {
			m[i] = true
		}
	}
	return len(m) * 2
}

但, 如此有个问题, 元素在数组中出下的次数, 会影响其最终放入hash的概率. 次数越多, 概率越大. 这显然会影响计算的公平性.

一个非常简单的解决思路: 在随机之前, 我们将该元素从hash中先删除. 这样, 影响此元素是否在hash中出现的, 就只是其最后一次随机的结果了.

func count(arr []int) int {
	m := make(map[int]bool)
	for _, i := range arr {
		delete(m, i)
		if rand.Intn(2) == 0 {
			m[i] = true
		}
	}
	return len(m) * 2
}

当然, 现在的内存使用应该是数组长度的一半. 我们可以增加随机率来进一步降低内存占用.

func count(arr []int, p int) int {
	m := make(map[int]bool)
	for _, i := range arr {
		delete(m, i)
		if rand.Intn(p) == 0 {
			m[i] = true
		}
	}
	return len(m) * p
}

至此, 基本思路已经介绍完了, 但实际的内存占用仍然与数组大小关联. 可以看到, 随机率越高, hash中保存的元素就会越少. 我们是否可以动态的来调整呢? 让随机率随着数组长度的增加而增加.

func count(arr []int, maxSize int) int {
	p := 2
	m := make(map[int]bool)
	for _, i := range arr {
		delete(m, i)
		if rand.Intn(p) == 0 {
			m[i] = true
		}
		if len(m) >= maxSize {
			p *= 2
			// 因为随机率增大了一倍
			// 之前的元素也需要再次进行随机, 使得其满足现有的随机率
			for k, _ := range m {
				if rand.Intn(2) == 0 {
					delete(m, k)
				}
			}
		}
	}
	return len(m) * p
}

以上, 就是整个算法的全部内容了. 这是一个实现简单, 思路也简单的算法. 它给我打开了新的视野

标签:arr,int,range,len,一种,计数,算法,随机,思路
From: https://www.cnblogs.com/hujingnb/p/17978013

相关文章

  • 算法学习Day36重叠区间
    Day36重叠区间ByHQWQF2024/01/21笔记435.无重叠区间给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。注意:可以认为区间的终点总是大于它的起点。区间[1,2]和[2,3]的边界相互“接触”,但没有相互重叠。示例1:输入:[[1,2],[2,3],[3,4],[1......
  • Verdi信号平移+研发管理体系+malloc和calloc函数区别+使用__FILE__只打印文件名+使用i
    Verdi信号平移信号左移是将光标移动在双引号以内的信号名左边,然后先输入数字,可以带上单位,如[ns|n]、[ps|p],然后按<<-按键。https://blog.csdn.net/qq_40268672/article/details/132915499信号右移信号右移是数字在右边,信号在左边,用右移符号,其它不变。研发管理体系https://......
  • floyd 算法——P1119 灾后重建
    floyd算法是图论中较为简单的最短路算法,但在某些方面远超最短路范围。算法思路定义\(f[x][y]\)为\(x\)到\(y\)节点的最短路径。初始化:若存在边\((x,y)\)则\(f[x][y]\)等于边长度;若不存在,为\(+\infty\)。特别的,\(f[x][x]=0\)。我们考虑一下,\(x,y\)这两个节点通......
  • (坚持每天写算法)算法复习与学习part1基础算法part1-11——差分
    差分和前缀和是有联系的。首先给定一个原数组a:a[1],a[2],a[3],,,,,,a[n];然后我们构造一个数组b:b[1],b[2],b[3],,,,,,b[i];使得a[i]=b[1]+b[2]+b[3]+,,,,,,+b[i]也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数......
  • 排序算法与查找
    1.排序1.1冒泡排序冒泡排序,就是将相邻两个元素进行比较,如果前面那个元素和后面那个元素进行比较,如果前面元素比后者元素大,则进行交换位置。下面举例: 由图可知,共有5个元素,进行了四轮比较,假设有n个元素,则进行n-1轮比较(外部循环)。内部元素比较变化:第一轮把最大的元素给去......
  • (坚持每天写算法)算法复习和学习part1基础算法part1-9高精度乘法
    这一道题的思路和之前都是一样的,仍然是按照算式进行模拟的,这里就直接贴代码了:#include<iostream>#include<cstdio>#include<cstring>#include<vector>#include<algorithm>usingnamespacestd;//总结:vector,size,string,size,vector[i],string[i];vector&......
  • 代码随想录算法训练营第 十 一 天| 20. 有效的括号 1047. 删除字符串中的所有相邻重
    LeetCode 20.有效的括号题目链接:20.有效的括号思路:采用栈数据结构解题;遇到左括号,压右括号入栈 LeetCode 1047.删除字符串中的所有相邻重复项题目链接:1047.删除字符串中的所有相邻重复项注意:Java中队列实现类API的使用 LeetCode 150.逆波兰表达式求值题目链......
  • 代码随想录算法训练营第十天| 232.用栈实现队列 225. 用队列实现栈
    LeetCode232.用栈实现队列题目链接:232.用栈实现队列思路:用两个栈实现队列 LeetCode  225.用队列实现栈 题目链接:225.用队列实现栈 思路:一个队列对栈进行实现(实现栈中的方法) ......
  • (坚持每天都写算法)算法复习与学习part1基础算法1.8高精度乘法
    这道知识点有点特殊,我当初在学的时候是只学了高精度*高精度,然后其他的我还没有想法,今天就来学学。有大概6天没有写新博客,主要是实习面试和期末考,实习面试没有过关,姐姐朋友推荐我先去刷一下面试题,叫我重温一下之前的知识,然后去参考一下开源项目,我决定边复习边写博客,就这样......
  • 安防视频监控汇聚平台LntonAIServer算法分析森林明烟明火算法检测
    在当今社会,随着科技的飞速发展,人工智能技术已经深入到各个领域,为人们的生活带来了极大的便利。在安防领域,人工智能技术的应用更是如虎添翼,为我们的家园提供了更加安全的保护。今天,我们就来探讨一下安防视频监控汇聚平台LntonAIServer中的森林明烟明火算法检测技术。森林火灾是一种......