【问题标题】:Python implementation of "median of medians" algorithm“中位数”算法的 Python 实现
【发布时间】:2012-06-04 02:22:33
【问题描述】:

我已经在 python 中编写了这个中位数算法的实现,但它似乎没有输出正确的结果,而且它对我来说似乎也不是线性复杂性,知道我偏离轨道的地方吗?

def select(L):
    if len(L) < 10:
        L.sort()
        return L[int(len(L)/2)]
    S = []
    lIndex = 0
    while lIndex+5 < len(L)-1:
        S.append(L[lIndex:lIndex+5])
        lIndex += 5
    S.append(L[lIndex:])
    Meds = []
    for subList in S:
        print(subList)
    Meds.append(select(subList))
    L2 = select(Meds)
    L1 = L3 = []
    for i in L:
        if i < L2:
            L1.append(i)
        if i > L2:
            L3.append(i)
    if len(L) < len(L1):
        return select(L1)
    elif len(L) > len(L1) + 1:
        return select(L3)
    else:
        return L2

函数调用如下:

L = list(range(100))
shuffle(L)
print(select(L))

LE:对不起。 GetMed 是一个简单地对列表进行排序并在 len(list) 处返回元素的函数,它应该在那里选择,我现在修复了它,但我仍然得到错误的输出。至于缩进,代码没有错误,我看不出有什么问题:-??

LE2:我期待 50(对于当前 L),它给我的输出从 30 到 70,不多不少(还)

LE3:非常感谢你,它现在起作用了。不过我很困惑,我试图在这种方法和天真的方法之间进行比较,我只是对数组进行排序并输出结果。现在,从我目前阅读的内容来看,select 方法的时间复杂度应该是 O(n)Deterministic Selection。虽然我可能无法与 python 开发人员所做的优化竞争,但我确实期望得到比我得到的更接近的结果,例如,如果我将列表的范围更改为 10000000,则选择在 84.10837116255952 秒内输出结果,而排序和返回方法在 18.92556029528825 中进行。有什么好方法可以让这个算法更快?

【问题讨论】:

  • 请修正您的格式。缩进似乎坏了。
  • getMed() 未定义。你得到什么错误的输出,你正在执行的输出是什么?

标签: python algorithm


【解决方案1】:

1) 你的代码缩进错误,试试这个:

def select(L):
    if len(L) < 10:
        L.sort()
        return L[int(len(L)/2)]
    S = []
    lIndex = 0
    while lIndex+5 < len(L)-1:
        S.append(L[lIndex:lIndex+5])
        lIndex += 5
    S.append(L[lIndex:])
    Meds = []
    for subList in S:
        print(subList)
        Meds.append(select(subList))
    L2 = select(Meds)
    L1 = L3 = []
    for i in L:
        if i < L2:
            L1.append(i)
        if i > L2:
            L3.append(i)
    if len(L) < len(L1):
        return select(L1)
    elif len(L) > len(L1) + 1:
        return select(L3)
    else:
        return L2

2)您使用的方法不返回中位数,它只是返回一个离中位数不远的数字。要获得中位数,您需要计算有多少数字大于您的伪中位数,如果多数数较大,则使用大于伪中位数的数字重复算法,否则使用其他数字重复。

def select(L, j):
    if len(L) < 10:
        L.sort()
        return L[j]
    S = []
    lIndex = 0
    while lIndex+5 < len(L)-1:
        S.append(L[lIndex:lIndex+5])
        lIndex += 5
    S.append(L[lIndex:])
    Meds = []
    for subList in S:
        Meds.append(select(subList, int((len(subList)-1)/2)))
    med = select(Meds, int((len(Meds)-1)/2))
    L1 = []
    L2 = []
    L3 = []
    for i in L:
        if i < med:
            L1.append(i)
        elif i > med:
            L3.append(i)
        else:
            L2.append(i)
    if j < len(L1):
        return select(L1, j)
    elif j < len(L2) + len(L1):
        return L2[0]
    else:
        return select(L3, j-len(L1)-len(L2))

警告:L = M = [] 不是 L = []M = []

【讨论】:

  • 对于简单的测试用例会失败 1, 2, 3, 4, 4, 5, 6, 12, 17, 20 # 返回 5,应该返回 4.5
  • @VikhyathReddy 不,4.5不是序列的元素,怎么可能是中位数?
  • @waka-waka-waka 你看到@thomash 的评论了吗?
  • 旧帖,但@waka-waka-waka 是对的。偶数列表的中位数通常取两个中心元素的平均值,否则取较低或较高的值,就会有偏差。
  • @Jblasco 返回不在输入中的元素可能不正确,具体取决于您需要的中位数。在此示例中,有一组整数,您想返回一个可能不合适的小数,对于整数,您知道有一个具有良好属性的超集允许您返回 4 到 5 之间的值,但它不会适用于任何类型的对象。不可能有一个完美的中位数定义来保证存在性和唯一性,但我的定义对于所有实际目的来说已经足够了。
【解决方案2】:

下面是我的 PYTHON 实现。要获得更快的速度,您可能需要改用 PYPY。

关于 SPEED 的问题: 每列 5 个数字的理论速度约为 10N,因此我每列使用 15 个数字,以 2X 速度为 ~5N,而最佳速度为 ~4N。但是,对于最先进的解决方案的最佳速度,我可能是错误的。在我自己的测试中,我的程序比使用 sort() 的程序运行得稍快。当然,您的里程可能会有所不同。

假设python程序是“median.py”,运行它的例子是“python ./median.py 100”。对于速度基准,您可能需要注释掉验证代码,并使用 PYPY。

#!/bin/python
#
# TH @stackoverflow, 2016-01-20, linear time "median of medians" algorithm
#
import sys, random


items_per_column = 15


def find_i_th_smallest( A, i ):
    t = len(A)
    if(t <= items_per_column):
        # if A is a small list with less than items_per_column items, then:
        #     1. do sort on A
        #     2. return the i-th smallest item of A
        #
        return sorted(A)[i]
    else:
        # 1. partition A into columns of items_per_column items each. items_per_column is odd, say 15.
        # 2. find the median of every column
        # 3. put all medians in a new list, say, B
        #
        B = [ find_i_th_smallest(k, (len(k) - 1)/2) for k in [A[j:(j + items_per_column)] for j in range(0,len(A),items_per_column)]]

        # 4. find M, the median of B
        #
        M = find_i_th_smallest(B, (len(B) - 1)/2)

        # 5. split A into 3 parts by M, { < M }, { == M }, and { > M }
        # 6. find which above set has A's i-th smallest, recursively.
        #
        P1 = [ j for j in A if j < M ]
        if(i < len(P1)):
            return find_i_th_smallest( P1, i)
        P3 = [ j for j in A if j > M ]
        L3 = len(P3)
        if(i < (t - L3)):
            return M
        return find_i_th_smallest( P3, i - (t - L3))


# How many numbers should be randomly generated for testing?
#
number_of_numbers = int(sys.argv[1])


# create a list of random positive integers
#
L = [ random.randint(0, number_of_numbers) for i in range(0, number_of_numbers) ]


# Show the original list
#
print L


# This is for validation
#
print sorted(L)[int((len(L) - 1)/2)]


# This is the result of the "median of medians" function.
# Its result should be the same as the validation.
#
print find_i_th_smallest( L, (len(L) - 1) / 2)

【讨论】:

    猜你喜欢
    • 2021-08-27
    • 1970-01-01
    • 2018-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多