两种线性时间选取第i小的元素的方法+ python实现(未完结)

先看第一种比较简单并且也比较容易理解的`
思路如下:
快速排序的核心过程可以直接得到我们当前选择元素在最后将所有元素排好序后当前元素所在位置。
所以我们可以考虑对快速排序进行改编,由于我们不需要知道每个元素的具体位置所以,我们可以选择性地执行快速排序的核心过程。

随机选取元素然后和第一个元素进行交换,并且最终将所有小于和大于当前选取的元素的其他元素进行重新排序,小的排在左边大的排在右边。

代码如下:

import numpy as np
import random

A = [random.randint(1, 100) for i in range(10)]

def randomizedPartition(A, p, r):
    i = np.random.randint(p, r+1)
    print(i,A[i])
    A[r], A[i]= A[i], A[r]
    x = A[r]
    i = p-1
    for j in range(p,r):
        if A[j]<=x:
            i+=1
            A[i], A[j]=A[j], A[i]

    A[i+1], A[r] = A[r], A[i+1]
    print(A)
    return i+1

randomizedPartition(A, 0,len(A)-1)

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

    pass

print(randomizedSelect(A,0, len(A)-1, int(input('请输入你要的第i位'))))

运行结果如图:


4 59
[30, 59, 100, 89, 65, 67, 94, 86, 68, 64]
请输入你要的第i位5
9 64
[30, 59, 64, 89, 65, 67, 94, 86, 68, 100]
4 65
[30, 59, 64, 65, 100, 67, 94, 86, 68, 89]
9 89
[30, 59, 64, 65, 67, 86, 68, 89, 94, 100]
6 68
[30, 59, 64, 65, 67, 68, 86, 89, 94, 100]
67

Process finished with exit code 0

另一种升级版本的方法,思路:我们不断获取中位数,比上面的随机选取数要靠谱很多,并且我们还可以改革数据存储结构,让获取中位数更加简单一些
我们尽量把数变成奇数行尽量方的一堆数

例如假设数组一共有100个元素那么我们可以把它变形成11行10列其中最后一列只有第一个行有元素

我们先通过快速排序的核心方法取出每一列的中位数,放在每一列的中间然后将小的元素放在中间元素的上面,大的放在下面。

之后我们把所有的列通过中位数的大小从小到大排序,小的放左边,大的放右边

这时候我们发现在整个数组最中间的就是这个数组真正的中位数,中位数左方肯定都是不比它大的数,右边肯定都是不比它小的数。我们这样分类,每次将目标缩小到原来的四分之3(由1/4和1/2组成)

我们第一次对根号n个列找中位数通过快速排序核心算法每个最多花费根号n的时间
所以

T(n) = T(n/2)+ T(n/4)+ O(n)

由主定理推导 a=1 b=4 fn = n

属于
fn比在这里插入图片描述大的时候fn为主导,复杂度为fn即n线性时间就可以完成

下面的代码实现了将A中的所有列的中位数的中位数放在最中间,并且小的放在左边大的放在右边

# 这个算法我失败了我的心有点乱

import numpy as np
import random


maxNum = 10000
aimLength = random.randint(100, 10000)
print(aimLength)
# 我们要获取的第aimI小的元素位序
aimI = random.randint(0, aimLength)


rowNum = int(aimLength**0.5)


A = [random.randint(1, maxNum) for i in range(aimLength)]

# 判断A之后要分成多少行
if rowNum%2 ==1:
    pass
else:
    rowNum +=1

# 下面我们进行判断我们准备把数组化成二维但是可能出现排不满的情况,所以进行添加,用足够大的数进行添加。
if aimLength%rowNum ==0:
    pass
else:
    while True:
        aimLength+=1
        A.append(maxNum)
        if aimLength%rowNum ==0:
            break


A = np.array(A).reshape((rowNum, int(aimLength/rowNum)))
print(A)
# 完成对初始数组A的处理


# resultCol =

def randomizedPartition(A, p, r):
    i = np.random.randint(p, r+1)
    A[r], A[i]= A[i], A[r]
    x = A[r]
    i = p-1
    for j in range(p,r):
        if A[j]<=x:
            i+=1
            A[i], A[j]=A[j], A[i]

    A[i+1], A[r] = A[r], A[i+1]
    return i+1

# 对所有数据进行操作
def randomizedListPartition(currentrow, p, r):

    global A
    i = np.random.randint(p, r+1)
    print(A)
    # 进行交换
    A[:, [i, r]] = A[:, [r, i]]
    print(A)
    x = A[currentrow, r]
    i = p-1
    for j in range(p,r):
        if A[currentrow,j]<=x:
            i+=1
            A[:, [i, j]] = A[:, [j, i]]


    A[:, [i+1, r]] = A[:, [r, i+1]]
    return i+1


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



# 和上面唯一的不同是我们是对所有数据进行位置替换
def randomizedListSelect(A,currentrow,  p, r, i):

    if p==r:
        return A[currentrow][p]
    q=randomizedListPartition(currentrow, p ,r)
    print('93*****')
    print(A)
    k=q-p+1
    if i==k:
        return A[currentrow][q]
    elif i<k:
        return randomizedListSelect(A,currentrow,p,q-1,i)
    else:
        return randomizedListSelect(A,currentrow,q+1,r,i-k)



def colMidPartition(aimLength, rowNum):
    global A
    currentResultArray = []

    for i in range(int(aimLength/rowNum)):
        currentList = []
        for temp in A[:, i:i+1].tolist():
            currentList.append(temp[0])
        randomizedSelect(currentList, 0, len(currentList)-1, int(len(currentList)/2))
        currentResultArray.append(currentList)

    A = np.array(currentResultArray).T
    print('A', A)

# 和上面的方法不同的是这个是对整个A进行操作 比较每个位的中位数  返回 的结果中 比 每个中位数的 中位数小的列 在左大的列 在右
def colMidListPartition(aimLength, rowNum):
    global A
    # 列的数量
    colNum = int(aimLength / rowNum)
    # 我们的目的就是求第colNum/2 小的元素 它就是所有列中位数的中位数
    int(colNum/2)
    currentrow = int(rowNum/2)

    # 返回的就是整个A的所有中位数的中位数
    currentResult = randomizedListSelect(A, currentrow, 0, colNum-1, int(colNum/2))
    print('sadasdasd')
    print(A[currentrow,])




    pass

colMidPartition(aimLength, rowNum)


colMidListPartition(aimLength, rowNum)


# def getAimINum(aimI,aimLength,rowNum):
#     global A








运行结果如下:

4476
[[ 2570   788  1211 ...  7760  9688  1732]
 [ 8841  9728  4773 ...  4501  9534  7511]
 [ 9961  4225  7874 ...  5886  4074  9658]
 ...
 [  988  5257   424 ...  8832  5714  2550]
 [ 9960  8824  4501 ...  4604  8593  8048]
 [ 4397  8708  4380 ... 10000 10000 10000]]
A [[  857   788  1211 ...  1189   564  1732]
 [  278    33   618 ...  1302   460  1886]
 [ 1795   795   150 ...  1846   701  1483]
 ...
 [ 9263  6386  9099 ...  8832  8840  8475]
 [ 9960  8824  9403 ...  6385  8593  9994]
 [ 9612  9353  7985 ...  9398  9951 10000]]
[[  857   788  1211 ...  1189   564  1732]
 [  278    33   618 ...  1302   460  1886]
 [ 1795   795   150 ...  1846   701  1483]
 ...
 [ 9263  6386  9099 ...  8832  8840  8475]
 [ 9960  8824  9403 ...  6385  8593  9994]
 [ 9612  9353  7985 ...  9398  9951 10000]]
[[ 857  788 1211 ... 1189  564  349]
 [ 278   33  618 ... 1302  460 2384]
 [1795  795  150 ... 1846  701   38]
 ...
 [9263 6386 9099 ... 8832 8840 7381]
 [9960 8824 9403 ... 6385 8593 9356]
 [9612 9353 7985 ... 9398 9951 8488]]
93*****
[[ 857 1569  357 ... 1189  564 3638]
 [ 278  482  557 ... 1302  460  476]
 [1795 1202  643 ... 1846  701 2688]
 ...
 [9263 9149 9954 ... 8832 8840 7929]
 [9960 9222 7390 ... 6385 8593 8463]
 [9612 9621 8311 ... 9398 9951 9046]]
[[ 857 1569  357 ... 1189  564 3638]
 [ 278  482  557 ... 1302  460  476]
 [1795 1202  643 ... 1846  701 2688]
 ...
 [9263 9149 9954 ... 8832 8840 7929]
 [9960 9222 7390 ... 6385 8593 8463]
 [9612 9621 8311 ... 9398 9951 9046]]
[[ 857 1569  357 ... 1189  564  856]
 [ 278  482  557 ... 1302  460  989]
 [1795 1202  643 ... 1846  701 1046]
 ...
 [9263 9149 9954 ... 8832 8840 8458]
 [9960 9222 7390 ... 6385 8593 8329]
 [9612 9621 8311 ... 9398 9951 9023]]
93*****
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
93*****
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
93*****
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
93*****
[[ 857 1569  357 ...  292 1389 3382]
 [ 278  482  557 ...  384 1765 4168]
 [1795 1202  643 ...  171 3162 2644]
 ...
 [9263 9149 9954 ... 9652 9814 9132]
 [9960 9222 7390 ... 8223 9539 5882]
 [9612 9621 8311 ... 9805 9065 7695]]
sadasdasd
[4296 4327 3955 4438 4044 3895 4515 4235 4277 3888 3669 4199 4532 4761
 4920 4573 4629 4903 4725 4888 4893 4783 4836 4597 4815 4579 4905 4928
 4995 4938 4998 4999 5044 5060 5177 5164 5185 5091 5054 5168 5127 5100
 5241 5262 5364 5657 5403 5369 5462 5613 5571 5312 5462 5598 5458 5657
 5763 6732 5821 6002 5758 6125 5769 6120 6026 5764 8575]

Process finished with exit code 0

之后我们考虑对上面的代码反复调用从而实现需求,

# 这个算法我失败了我的心有点乱

import numpy as np
import random


maxNum = 10000
aimLength = random.randint(100, maxNum)
print(aimLength)
# 我们要获取的第aimI小的元素位序
aimI = random.randint(0, aimLength)
print('我们要的是第',aimI,'小的元素')
A = [random.randint(1, maxNum) for i in range(aimLength)]
rowNum = 0


def makeUpListaAndOtherParams():
    global rowNum
    global aimLength
    global A
    rowNum = int(aimLength ** 0.5)
    # 判断A之后要分成多少行
    if rowNum % 2 == 1:
        pass
    else:
        rowNum += 1
    # 下面我们进行判断我们准备把数组化成二维但是可能出现排不满的情况,所以进行添加,用足够大的数进行添加。
    if aimLength % rowNum == 0:
        pass
    else:
        while True:
            aimLength += 1
            A.append(maxNum)
            if aimLength % rowNum == 0:
                break
    A = np.array(A).reshape((rowNum, int(aimLength / rowNum)))
    print(A)
    # 完成对初始数组A的处理

resultCol = 0  # 额。。。。这里指的是顺序而不是位序是位序加一
resultRow = 0
# 获取我们要的i到底是在第几行第几列 并且赋值给resultCol, resultRow
def getResultColAndRow(aimI, aimLength, rowNum):
    global resultCol
    global resultRow

    if aimI*rowNum % aimLength==0:   # 如果整除那就证明aimI是落在某行的最后面的一个
        resultCol = int(aimI*rowNum/aimLength)
        resultRow = int(aimLength/rowNum)
    else:
        resultCol = int(aimI * rowNum / aimLength)+1
        resultRow = int(aimI%(int(aimLength/rowNum)))


# 并且我们通过coreCode执行之后比中位数大的元素都会出现在 最中间的所有原色的中位数的右边和下边,这时  我们的aimI要是会出现在右下方我们就应
def coreCode(aimLength, rowNum):

    def randomizedPartition(A, p, r):
        i = np.random.randint(p, r + 1)
        A[r], A[i] = A[i], A[r]
        x = A[r]
        i = p - 1
        for j in range(p, r):
            if A[j] <= x:
                i += 1
                A[i], A[j] = A[j], A[i]

        A[i + 1], A[r] = A[r], A[i + 1]
        return i + 1

    # 对所有数据进行操作
    def randomizedListPartition(currentrow, p, r):

        global A
        i = np.random.randint(p, r + 1)
        print(A)
        # 进行交换
        A[:, [i, r]] = A[:, [r, i]]
        print(A)
        x = A[currentrow, r]
        i = p - 1
        for j in range(p, r):
            if A[currentrow, j] <= x:
                i += 1
                A[:, [i, j]] = A[:, [j, i]]

        A[:, [i + 1, r]] = A[:, [r, i + 1]]
        return i + 1

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

    # 和上面唯一的不同是我们是对所有数据进行位置替换
    def randomizedListSelect(A, currentrow, p, r, i):

        if p == r:
            return A[currentrow][p]
        q = randomizedListPartition(currentrow, p, r)
        print('93*****')
        print(A)
        k = q - p + 1
        if i == k:
            return A[currentrow][q]
        elif i < k:
            return randomizedListSelect(A, currentrow, p, q - 1, i)
        else:
            return randomizedListSelect(A, currentrow, q + 1, r, i - k)

    def colMidPartition(aimLength, rowNum):
        global A
        currentResultArray = []

        for i in range(int(aimLength / rowNum)):
            currentList = []
            for temp in A[:, i:i + 1].tolist():
                currentList.append(temp[0])
            randomizedSelect(currentList, 0, len(currentList) - 1, int(len(currentList) / 2))
            currentResultArray.append(currentList)

        A = np.array(currentResultArray).T
        print('A', A)

    # 和上面的方法不同的是这个是对整个A进行操作 比较每个位的中位数  返回 的结果中 比 每个中位数的 中位数小的列 在左大的列 在右
    def colMidListPartition(aimLength, rowNum):
        global A
        # 列的数量
        colNum = int(aimLength / rowNum)
        # 我们的目的就是求第colNum/2 小的元素 它就是所有列中位数的中位数
        int(colNum / 2)
        currentrow = int(rowNum / 2)


        # 返回的就是整个A的所有中位数的中位数
        currentResult = randomizedListSelect(A, currentrow, 0, colNum - 1, int(colNum / 2))
        print('sadasdasd')
        print(A[currentrow,])
        # 返回的是顺序号而不是位序号
        return currentrow+1,int(colNum / 2) , currentResult, int(colNum/2) + (currentrow)*(rowNum-1)
        pass

    colMidPartition(aimLength, rowNum)
    return colMidListPartition(aimLength, rowNum)

def getAimINum(aimLength,rowNum):

    global A
    global resultCol
    global resultRow
    global aimI

    # 初始化参数
    makeUpListaAndOtherParams()
    # 获取resultCol和resultRow
    getResultColAndRow(aimI, aimLength, rowNum)
    currentrow, currentcol, currentResult, currentAim= coreCode(aimLength, rowNum)
    if resultCol == currentcol and resultRow == currentrow:
        print('这个数是:', currentResult)
        return currentResult
    else:
        # 我们在这里对参数进行修改之后再次调用自身方法
        if aimI > currentAim:
            # 我们要的是更大的数 说明肯定比当前中位数的中位数小的元素都可以不要
           # 

            pass




        pass




getAimINum(aimI,aimLength,rowNum)








  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值