IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python算法 快速找到单一元素——分析算法效率 -> 正文阅读

[Python知识库]Python算法 快速找到单一元素——分析算法效率

需求

给定数组vec,元素个数为(2n+1)(n为正整数)。其中,n个数据出现了两次,有一个出现了一次,把这个出现一次的数记为k,举例如下:

[0,0,1,1,2,2,3] => k=3
[1,2,3,1,4,2,3] => k=4
[9,6,3,3,9,9,6,8,8] => k=9

求函数f(vec)=k

生成随机数据用来测试

import random

def get_data(num, single):
	ret = []
	for i in range(0, num):
		ret.append(i)
		ret.append(i)
	ret.append(single)
	random.shuffle(ret)
	return ret

记录时间装饰器

import time

def test(fun):
	def real(*args, **kwargs):
		b = time.time()
		fun(*args, **kwargs)
		e = time.time()
		print(e-b)
	return real

实现1

普普通通地实现,正常思路:出现一次就加进另一个列表,再出现一次就尝试删除,若不存在就添加进去,最终应该只剩下一个元素(新列表里)

@test
def solution1(lst):
	another = [] # 临时列表,增减都在里面
	for i in lst:
		try:
			del another[another.index(i)]
		except:
			another.append(i)
	return another[0]

测试数据规模:(50000个数据,13478出现了奇数次)

data = get_data(50000, 13478)
print(solution1(data))

多次测试取平均值,大约是12~13s的样子。

分析时间消耗

空间消耗还好,这次主要看效率。
首先是try-except结构需要很大的时间开销,其间解释器内部进行了巨大地操作。
然后影响更大的是index方法,它会从头遍历越来越大的列表并定位(这也是为什么总规模扩大一倍时间却扩大很多的原因)。随着元素的增加,如果没有及时出现对子把它消除,列表会越来越大,索引需要遍历过的就越来越长,时间消耗越来越多。
最后,del和append都有一定的消耗。

改进

避免如上所述的大型时间消耗,更改思路如下:
先把列表排序,那么出现偶数次的数肯定在一起,就像这样:

[1,2,3,1,2,3,4] => [1,1,2,2,3,3,4]

这么说,只需要判断lst[n]是否等于lst[n+1](n=2k,k∈Z)
于是生成一个0,2,4,6,8,…的生成器(range函数即可,把setp改成2),把i带进去比较上述二式值。

@test
def solution1_better(lst):
	lst.sort()
	for i in range(0, len(lst), 2):
		try:
			if lst[i] != lst [i+1]:
				return lst[i]
		except:
			return lst[i]

值得关注的点在于,这里还是要用循环里的try-except,因为如果范围给的是500,要找的数是499,i+1的索引就会越界。但是相比之下无关紧要,except的检查相关大段消耗只会在try失败的时候触发,在虚拟机内部是不需要每次循环都回溯栈、各种校验的。
测试,平均0.025s,只用了solution1时间的1.9‰,可见大幅提高。

最终算法——实现2

在lst的范围足够大的时候,上述算法都无法应对——第一种时间复杂度几何增长,第二种线性增长,都不是很理想。
下面介绍重点:位运算法。利用异或(XOR)运算的周期性,两次连续XOR同一个数,值不变,即

a ^ b = c
c ^ b = a

这个性质正好用在这个算法中。出现两次连续异或就能抵消,而且位运算不管在哪种语言里都是最快速的解决方案。

@test
def solution2(lst):
	result = 0
	for i in lst:
		result ^= i
	return result

相比之下很短小,但十分有用。
测试结果表明,当数据集跟如上两种算法相当时,或者说完全一样,solution2几乎不耗时(e-b -> 0)当然结果一定正确且能满足各种特殊情况。

加大规模,将solution1_bettersolution2控制变量比较(solution1原算法太慢了无意义)。测试代码段如下:

data = get_data(1e6, 1e6-19)

print(solution1_better(data))
print(solution2(data))

目标数用最大值减去较小值是为了避免solution1_better算法过早结束,它是顺向遍历。

多次测量下,前者平均1.56s,后者稳定在0.14s,相差近十倍。

总结

二进制位运算在相同处理器的执行下效率是最高的,使用最少的时钟周期。所以在需要大规模运算的高效算法,又对时间要求特别紧张的时候,善于运用位运算能达到极佳的效果。

源码已上传至gitee

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:45:38  更:2022-03-21 20:46:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 19:31:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码