我正在比较maxmin算法实现的复杂性,我已经用两种方式实现了:蛮力方式和分而治之的方式。在我测试了两个算法之后,十个元素输入在1000000到10000000之间。遵循以下算法:
下面的蛮力实施:
def maxmin1(vetor):
max,min = vetor[0],vetor[0];
for elem in vetor[1:]:
if elem > max:
max = elem
if elem < min:
min = elem
return (min,max)
并在下面划分和征服实施:
def maxmin4(vetor,inicio,fim):
if ((fim - inicio) == 1):
return (vetor[inicio], vetor[inicio])
elif ((fim - inicio) == 2):
if( vetor[inicio] < vetor[inicio+1]):
return (vetor[inicio], vetor[inicio+1])
else:
return (vetor[inicio+1], vetor[inicio])
else:
(min_left,max_left) = maxmin4(vetor,inicio,(fim-inicio)/2 + inicio)
(min_right,max_right) = maxmin4(vetor,(fim-inicio)/2 + inicio,fim)
if (max_left < max_right):
max = max_right
else:
max = max_left
if (min_left < min_right):
min = min_left
else:
min = min_right
return (min,max)
和结果:
input N time algorithm 1 | time algorithm 2
1000000 | 0.1299650669 | 0.6347620487
2000000 | 0.266600132 | 1.3034451008
3000000 | 0.393116951 | 2.2436430454
4000000 | 0.5371210575 | 2.5098109245
5000000 | 0.6094739437 | 3.4496300221
6000000 | 0.8271648884 | 4.6163318157
7000000 | 1.0598180294 | 4.8950240612
8000000 | 1.053456068 | 5.1900761128
9000000 | 1.1843969822 | 5.6422820091
10000000| 1.361964941 | 6.9290060997
我不明白为什么第一个算法比第二个算法快,因为第一个算法的复杂度为2(n -1),第二个算法的复杂度为3n / 2 -2,理论上第一个比第二个慢。 。为什么会这样?
答案 0 :(得分:4)
事实证明,您的代码中似乎存在错误或分析中存在错误 - 但这并不重要。我最后会谈到它。
如果你看看你的结果,很明显两者之间的差异大约为5倍。这意味着第二个算法的复杂性并不比第一个更糟,它只是得到了一个更高的常数乘数 - 你做了相同数量的步骤,但每个步骤都要多得多。
这可能只是你测试这么窄范围的一个神器,只有10个因子。但运行你的测试有更广泛的值,如下所示:
for i in 100, 1000, 10000, 100000, 1000000, 10000000:
v = [random.random() for _ in range(i)]
t1 = timeit.timeit(lambda: maxmin1(v), number=1)
t2 = timeit.timeit(lambda: maxmin4(v, 0, len(v)), number=1)
print('{:8}: {:.8f} {:.8f} (x{:.8f})'.format(i, t1, t2, t2/t1))
......你可以看到这种模式很有用:
100: 0.00002003 0.00010014 (x5.00000000)
1000: 0.00017500 0.00080800 (x4.61716621)
10000: 0.00172400 0.00821304 (x4.76393307)
100000: 0.01630187 0.08839488 (x5.42237660)
1000000: 0.17010999 0.76053309 (x4.47083153)
10000000: 1.77093697 8.32503319 (x4.70092010)
那么,为什么第二个版本中的常量开销较高?好吧,第一个版本只是进行简单的for
迭代,两次比较,每个元素分配一次。第二个是调用函数,构建和爆炸元组,进行更多比较等等。这必然会变慢。如果你想知道为什么它正好慢5倍(或者实际上,慢15倍,如果你做2n / 3步而不是2n),你需要做一些分析,或者至少看一下字节码。但我不认为这是值得的。
故事的寓意是有一个原因2(n-1)和2n / 3-2都是O(n):当你有两个不同的复杂性类别,如O(n)和O( n ** 2),这对于大n来说总是有所不同;当你在同一个类中有两个算法时,实现中的常量(每个步骤的成本)很容易超过步数中的常量。
同时,我们如何验证2n / 3-2分析?很简单,只需添加一个全局计数器,每次调用maxmin4时都会递增一次。以下是预期和实际结果:
100: 65 127
1000: 665 1023
10000: 6665 11807
100000: 66665 131071
1000000: 666665 1048575
10000000: 6666665 11611391
但这只是意味着你做了大约2 / 3rds的步骤而不是大约1/3,所以每个步骤的不变成本是7.5倍而不是15倍。最后,这并不会真正影响分析。
答案 1 :(得分:3)
虽然分而治之的方法保证了最小的比较次数,但程序的实际复杂程度取决于程序中执行的操作总数。
在你的情况下,对大约n / 2个函数调用(函数调用的二叉树的叶节点)执行大约4或5次操作,对内部节点执行大约16次操作(计算所有赋值,算术运算) ,比较和元组结构)。总计约有10n个操作。
在第一个程序中,操作总数基本上是2.x * n(其中x取决于执行的赋值数)。
这与程序1中程序2中相对简单的操作相结合,导致在两个程序中观察到因子5。
此外,分而治之算法的比较数应为3n / 2,而不是2n / 3.
答案 2 :(得分:1)
我会非常惊讶永远看到Python递归比Python迭代运行得更快。尝试这种maxmin实现,一次取两个值。
def minmax(seq):
def byTwos(seq):
# yield values from sequence two at a time
# if an odd number of values, just return
# the last value twice (won't hurt minmax
# evaluation)
seq = iter(seq)
while 1:
last = next(seq)
yield last,next(seq,last)
seqByTwos = byTwos(seq)
# initialize minval and maxval
a,b = next(seqByTwos,(None,None))
if a < b:
minval,maxval = a,b
else:
minval,maxval = b,a
# now walk the rest of the sequence
for a,b in seqByTwos:
if a < b:
if a < minval:
minval = a
if b > maxval:
maxval = b
else:
if b < minval:
minval = b
if a > maxval:
maxval = a
return minval, maxval
如果要计算比较,则传递一系列实现__lt__
和__gt__
的对象,并让这些方法更新全局计数器。