如何解释字符串反转的三种python实现的性能
我已经在一个相对非正式的级别上编程了大约两年,不需要关心我的实现的性能或运行时。现在我想通过实际计算分析算法的运行时来提高我的计算精度,而不是仅仅通过分析 绘制字符串反转算法的各种实现的运行时会产生与我预期相反的结果 我在python中实现了四种反转字符串的方法:如何解释字符串反转的三种python实现的性能,python,algorithm,performance,optimization,Python,Algorithm,Performance,Optimization,我已经在一个相对非正式的级别上编程了大约两年,不需要关心我的实现的性能或运行时。现在我想通过实际计算分析算法的运行时来提高我的计算精度,而不是仅仅通过分析 绘制字符串反转算法的各种实现的运行时会产生与我预期相反的结果 我在python中实现了四种反转字符串的方法: 本机字符串[:-1] 带串联的对数反转(使用与mergesort类似的原理) 无串联对数反转 使用循环 现在,按照运行时native
字符串[:-1]
native
这是我的性能图(注意算法(1)太快了,在这个尺度上看不到)
三,
您在这里看到的是,性能顺序(在对数尺度上)是native
,这与以下预期不一致:
- 循环:
O(n)
- 日志目录:
O(nlogn)
- 纯日志:
O(logn)
- 本地人:李>
def native_reverse(string):
return string[::-1]
串联对数反转
def naive_fastreverse(string):
strlen = len(string)
# The recursion base case
if strlen==1:
return string
mid = ""
a = strlen/2
b = a
if strlen%2==1: #i.e. strlen is odd
mid = string[strlen/2]
b+=1
# else:
# use no mid letter
# use default b
half_a = string[0:a]
half_b = string[b:strlen]
return naive_fastreverse(half_b) + mid + naive_fastreverse(half_a)
def optimized_fastreverse(string):
final = list(string)
strlen = len(string)
def computenode(start, end):
if end-start==1:
final[strlen-end] = string[end-1]
else:
computenode(start,start+(end-start)/2)
computenode(start+(end-start)/2, end)
computenode(0, strlen)
return ''.join(final)
def loopy_reverse(string):
final=list(string)
strlen=len(string)
for i in xrange(strlen):
final[strlen-i-1] = string[i]
final[i] = string[strlen-i-1]
return ''.join(final)
非串联对数反转
def naive_fastreverse(string):
strlen = len(string)
# The recursion base case
if strlen==1:
return string
mid = ""
a = strlen/2
b = a
if strlen%2==1: #i.e. strlen is odd
mid = string[strlen/2]
b+=1
# else:
# use no mid letter
# use default b
half_a = string[0:a]
half_b = string[b:strlen]
return naive_fastreverse(half_b) + mid + naive_fastreverse(half_a)
def optimized_fastreverse(string):
final = list(string)
strlen = len(string)
def computenode(start, end):
if end-start==1:
final[strlen-end] = string[end-1]
else:
computenode(start,start+(end-start)/2)
computenode(start+(end-start)/2, end)
computenode(0, strlen)
return ''.join(final)
def loopy_reverse(string):
final=list(string)
strlen=len(string)
for i in xrange(strlen):
final[strlen-i-1] = string[i]
final[i] = string[strlen-i-1]
return ''.join(final)
基于循环的反转
def naive_fastreverse(string):
strlen = len(string)
# The recursion base case
if strlen==1:
return string
mid = ""
a = strlen/2
b = a
if strlen%2==1: #i.e. strlen is odd
mid = string[strlen/2]
b+=1
# else:
# use no mid letter
# use default b
half_a = string[0:a]
half_b = string[b:strlen]
return naive_fastreverse(half_b) + mid + naive_fastreverse(half_a)
def optimized_fastreverse(string):
final = list(string)
strlen = len(string)
def computenode(start, end):
if end-start==1:
final[strlen-end] = string[end-1]
else:
computenode(start,start+(end-start)/2)
computenode(start+(end-start)/2, end)
computenode(0, strlen)
return ''.join(final)
def loopy_reverse(string):
final=list(string)
strlen=len(string)
for i in xrange(strlen):
final[strlen-i-1] = string[i]
final[i] = string[strlen-i-1]
return ''.join(final)
这是我的绘图代码:
import time
import math
import random
import string
import numpy
Num = 5
def rand_str_gen(N):
return ''.join(
random.choice(
string.ascii_uppercase + string.digits)
for _ in range(N))
randstr = "a"*(10**Num) #rand_str_gen(10**Num)
def run(reverser, reversable):
start = time.time()
reverser(reversable)
end = time.time()
return end-start
def profile(reverser):
xs = np.arange(1,Num+1,0.1)
ys = []
for i in xs:
ys.append(run(reverser,randstr[1:int(10.**i)] ))
return xs ,ys
functions = [
native_reverse,
naive_fastreverse,
optimized_fastreverse,
loopy_reverse]
reversers = {
# Makes the function names look nice
(" ".join(
map(lambda s: s.capitalize(),
func.__name__.split("_")))): profile(func)
for func in functions}
%matplotlib inline
lengths = ["$10^%d$"%i for i in range(Num+1)]
from pylab import *
fig = figure(figsize=(20,10))
ax = fig.add_subplot(1,1,1)
plots = [
ax.plot(*data, label=name)[0]
for (data, name) in zip(reversers.values(), reversers.keys())
]
legend(handles=plots,loc=2)
ax.set_title("Algorithm Runtime (ms) by String Length")
ax.set_xticklabels(lengths)
您不是在衡量算法的性能,而是衡量内存的行为。由于PC中存在多个缓存级别,因此存在不同的访问时间模式。这使得实践与理论完全背离
同时,您还对Python intepreter进行计时,这也会以未知的方式扭曲结果。您不是在衡量算法的性能,而是衡量内存的行为。由于PC中存在多个缓存级别,因此存在不同的访问时间模式。这使得实践与理论完全背离
同时,您还对Python intepreter进行计时,这也会以未知的方式扭曲结果。为什么您认为一行代码会比十几行代码慢?我澄清了顺序。它是从最小到最大的运行时间。哦,你只是希望
logpure
比logconcat
慢?我一定是看错了;抱歉。不知道你从哪里得到的:native
是订单。log纯的速度更快(较小的运行时间),就像一个旁注,你不需要在级联的对数反转实现中使用中间元素,你可以删除中间的if语句。为什么你认为一行代码比十几行慢?我澄清了排序。它是从最小到最大的运行时间。哦,你只是希望logpure
比logconcat
慢?我一定是看错了;抱歉。不知道你从哪里得到的:native
是订单。日志纯被认为是更快的(较小的运行时),就像一个旁注,你不需要在连接的对数反转实现中使用中间元素,你可以删除中间的if语句。非常感谢这个答案。那么你知道我如何测量时间来统一实践和理论吗?@theideasmith这实际上是不可能的,因为理论假设RAM模型,即恒定时间内存访问,这在现实世界的计算机中并不存在。但是,您可以通过在一行中多次重复相同的执行并忽略异常值来提高准确性。您还应该使用具有静态数据结构的编译语言。@theideasmith:另一种检查行为的方法是使用计数器进行算法使用的基本操作,而不是计时。非常感谢您的回答。那么你知道我如何测量时间来统一实践和理论吗?@theideasmith这实际上是不可能的,因为理论假设RAM模型,即恒定时间内存访问,这在现实世界的计算机中并不存在。但是,您可以通过在一行中多次重复相同的执行并忽略异常值来提高准确性。您还应该使用具有静态数据结构的编译语言。@theideasmith:检查行为的另一种方法是使用计数器执行算法使用的基本操作,而不是计时。