实验内容
给定任意几组数据,利用舍伍德型选择算法,找出数组中的中值并输出(若数 组为奇数个则输出中值,若数组为偶数个则输出第 n/2 小元素)。
实验原理
设 A 是一个确定性算法,当它的输入实例为 x 时所需的计算时间记为 tA(x)。 设 Xn 是算法 A 的输入规模为 n 的实例的全体,则当问题的输入规模为 n 时, 算法 A 所需的平均时间为 1。这显然不能排除存在 x∈Xn 使得的 2 可能性。希望获得一个随机化算法 B,使得对问题的输入规模为 n 的每一个实例均有 3。
这就是舍伍德算法设计的基本思想。当 s(n)与 tA(n)相比可忽略时,舍伍德算法可获得很好的平均性能。 对于选择问题而言,用拟中位数作为划分基准可以保证在最坏的情况下用线性时间完成选择。如果只简单地用待划分数组的第一个元素作为划分基准,则算 法的平均性能较好,而在最坏的情况下需要 0(n2)计算时间。舍伍德选择算法 则随机地选择一个数组元素作为划分基准,这样既保证算法的线性时间平均性能,又避免了计算拟中位数的麻烦。
实验步骤
① 先判断是否需要进行随机划分即( kϵ(1, n)? n>1?);
② 产生随机数 j,选择划分基准,将 a[j]与 a[l]交换;
③ 以划分基准为轴做元素交换,使得一侧数组小于基准值,另一侧数组值大于 基准值;
④ 判断基准值是否就是所需选择的数,若是,则输出;若不是对子数组重复步骤②③④
代码实现
import time
import random
def look_for(L, left, right):
if (left >= right):
return L[left]
m = random.randint(left, right) #产生随机索引号
n = L[left]
L[left] = L[m]
L[m] = n #完成随机值与left交换
i = left
j = right
key = L[left]
while i < j:
while i < j and key <= L[j]:
j -= 1
L[i] = L[j] # 从右往左找到一个比key小的,将其赋值给基准值
while i < j and L[i] <= key:
i += 1
L[j] = L[i] # 从左往右找到一个比key大的,将其赋值给基准值
L[i] = key
# 已完成左侧比key小,右侧比key大
#判断i是否为数组的中间索引
if (len(L) % 2 == 0 and i == int(len(L) / 2) - 1):#偶数的中间索引
return key
elif (len(L) % 2 != 0 and i == int(len(L) / 2)):#奇数的中间索引
return key
if (i > int(len(L) / 2) - 1):#如果i大于中间索引则递归数组左边部分
return look_for(L, left, i - 1)
else: #反之递归数组右边部分
return look_for(L, i + 1, right)
def main():
from random import sample
result = sample([x for x in range(-1000, 1000)], 11)
print(result)
start = time.time()
mid = look_for(result, 0, len(result)-1)
end = time.time()
print("共耗时:\n" + str(end - start) + " s\n")
print("中间值为:" + str(mid))
if __name__ == '__main__':
main()
时间复杂度分析
根据look_for函数可以得到递归式:T(n)=T(n/2)+cn,运用主定理法可以得到时间复杂度为:
T(n) = O(n)。
运行结果
[-338, 938, 442, -838, 85, -476, -668, -722, -505, -607, 374]
共耗时:
0.0 s
中间值为:-476
[22, 673, 801, -316, -343, 845, 297, -412, -754, 901, 196]
共耗时:
0.0 s
中间值为:196
[417, -429, 522, 754, -563, -183, -683, 459, -719, -769, 714]
共耗时:
0.0 s
中间值为:-183