算法导论—中位数与顺序统计量

华电北风吹
天津大学认知计算与应用重点实验室
日期:2015/7/7

一、选择最大值或者最小值的最优算法
对于长度为n的数组,已证找最大值或者最小值比较操作下界就是n-1。所以只需要让第一个值为初始最大值或者初始最小值,用所有的值与这个值比较,更新这个值即可。

def minimum(a):
    minNum=a[0]
    for i in range(1,len(a)):
        if minNum>a[i]:
            minNum=a[i]
    return minNum
print(minimum ([1,2,3,4,5,6,7,8,9]))

二、同时选择最大值和最小值的快速算法(成对比较)
首先设置一对初始的最大值最小值(总数个数是奇数,初始最大值最小值为第一个数,若是偶数就为前两个比较),然后剩下的数两两组对,对内比较大小,然后小的与当前最小的比,大的与当前最大的比。这样的话两个数需要比较三次。如果选择分别计算最大值最小值需要2(n-1)次比较,采用成对比较的话只需要3int(n/2)次

def minmax(a):
    k=len(a)
    if k%2==0:
        if a[0]<a[1]:
            minNum,maxNum=a[0],a[1]
        else:
            minNum,maxNum=a[1],a[0]
    else:
        minNum,maxNum=a[0],a[0]
    for i in range(2-k%2,k,2):
        if a[i]>a[i+1]:
            a[i+1],a[i]=a[i],a[i+1]
        if a[i]<minNum:
            minNum=a[i]
        if a[i+1]>maxNum:
            maxNum=a[i+1]
    return minNum,maxNum
print(minmax([1,2,3,4,5,6,7,8,9]))

三、期望时间为线性时间的选择算法—随机选择
回想一下,快速排序的时候,选择一个数(随机)为基准,左右交换将小于基准的分到左边,大于基准的分到右边的思想,然后得到这个数在数组中的位置,这样我们可以根据这个位置判断我们要的数所处的位置,逐步缩小搜索范围。
基于快速排序的舍弃法,不考虑直接命中的情况的话一次平均舍弃一半的数。

import random

def randomizedPartion(a,p,r):
    k=random.randint(p, r)
    a[k],a[r]=a[r],a[k]
    value=a[r]
    i=p-1
    for j in range(p,r):
        if a[j]<value:
            i+=1
            a[i],a[j]=a[j],a[i]
    i+=1
    a[i],a[r]=a[r],a[i]
    return i

def randomizedSelect(a,p,r,i):
    q=randomizedPartion(a,p,r)
    k=q-p+1
    if k==i:
        return a[q]
    else:
        if i<k:
            return randomizedSelect(a,p,q-1,i)
        else:
            return randomizedSelect(a,q+1,r,i-k)

lst=[2,6,3,1,5,0,7,8,4,9]
k=randomizedSelect(lst,0,len(lst)-1,10)
print(k)

四、最坏时间为线性时间的选择算法—中位数的中位数划分
select算法的思想为:首先对数组中所有的数分组,然后计算每个组的中位数,然后计算所有组的中位数的中位数,然后把快速排序划分策略修改为按值划分,并且返回中位数的中位数在数组中位置。然后就可以判断舍弃了。
基于中位数的中位数一次舍弃的数有下限n/4(这个是我自己算的一个下限,下确界可能更高点)——比如要找的数小于中位数的中位数,组里面的中位数大于中位数的中位数的那些组上半部分是肯定这一轮需要舍弃的,而这个的一个下线就是n/4,这样每次最多保存上一次数据的3n/4个数据,而每一次长度为n的计算量为theta(n),根据等比数列性质,它的阶数是跟首项同阶的。所以最坏情况的计算时间为theta(n)。

def midNum(a):
    k=len(a)
    groupNum=5
    start=0
    while start<k:
        end=min(k-1,start+4)
        InsertSort(a,start,end)
        start+=5
    if k<5:
        return a[(k-1)//2]
    else:
        b=a[(groupNum//2)::groupNum]
        return midNum(b)

def InsertSort(a,start,end):
    for k in range(start+1,end+1):
        i=k
        while a[i-1]>a[i] and i>start:
            a[i-1],a[i]=a[i],a[i-1]
            i-=1
    return a

def PartionByValue(a,x):
    i=0
    j=0
    for k in range(0,len(a)):
        if a[k]<x:
            a[k],a[i]=a[i],a[k]
            i+=1
    for k in range(i,len(a)):
        if a[k]==x:
            j=k
    a[i],a[j]=a[j],a[i]
    return i

def select(a,i):
    mid=midNum(a)
    k=PartionByValue(a,mid)+1
    if i==k:
        return mid
    else:
        if i<k:
            return select(a[0:k-1],i)
        else:
            return select(a[k::],i-k)

lst=[2,6,3,1,5,0,7,8,4,9]
k=select(lst,10)
print(k)

补充说明:
等比数列求和问题
等比数列通项公式:a(m)=a(1)*q^(m-1)
等比数列求和公式:S(m)=a(1)* ( 1-q^m ) / ( 1-q )

如果a(1)=c*n
a(m+1)=q*a(m) (q<1)
可以求的S(m)=c*n* ( 1-q^m ) / ( 1-q )=theta(n)

在此博客中,问题三对应q=0.5,问题四对应q<0.75,所以都是线性时间算法

批注:阶数只是对特别大的数据的时候,阶数越低计算时间越短,数据量小的时候,线性时间的常数比例因子比较影响效果。

你可能感兴趣的:(快速排序,算法导论,顺序统计量)