【问题标题】:How to calculate the maximum median in an array如何计算数组中的最大中位数
【发布时间】:2019-09-11 03:26:51
【问题描述】:

这是一道算法题:

输入是一个具有非重复正整数的数组。找到一个中值最大的连续子数组(size > 1)。

例子:输入:[100, 1, 99, 2, 1000],输出应该是(1000 + 2) / 2 = 501的结果

我可以提出蛮力解决方案:尝试从 2 到数组大小的所有长度以找到最大中位数。但似乎太慢了。我也尝试在这个问题上使用两个指针,但不确定何时移动左右指针。

谁有更好的办法来解决这个问题?

【问题讨论】:

  • 你能举个例子吗?据我了解,取一个包含最大元素的长度为 2 的子数组就足够了,但它可能比我想象的要复杂。
  • 用蛮力算法做一些测试,似乎总是2或3的数组,不一定包含最大元素。但也可能有一些例外... 示例:Array = [4, 5, 6, 1, 9, 2, 3, 8, 0, 7],最大中位数子数组:[8, 0, 7],中位数: 7.
  • @Gilles-PhilippePaillé。刚刚添加了一个例子。你发现的东西真的很有趣。我还没有实现暴力破解,但是我尝试了很多例子,看起来大小总是小于 3。不确定是否有办法证明这一点?
  • 要明确一点,您要查找的是 median 还是 mean 值?问题提到“中位数”,但示例是计算出的“平均值”。
  • @Anders:给定两个数字,它们的平均值和中位数总是一致的。

标签: algorithm math median


【解决方案1】:

tl;dr - 我们可以证明答案的长度必须为 2 或 3,之后是检查所有可能性的线性时间。

假设输入是A,具有最大中位数的最小子数组是a。最大的中位数是单个元素或来自a 的一对元素的平均值。请注意,a 中大于中位数最大元素的每个元素只能与小于中位数最小元素的元素相邻(否则可以选择这样的一对作为子数组以形成更大的中位数)。

如果a 的任一端有一对不包含中位数元素的元素,则可以从a 中删除它而不影响中位数,这是矛盾的。

如果a 的任一端小于中位数的最小元素,则消除它会增加中位数,这是一个矛盾。

因此a 的每一端要么是中间值的一个元素,要么大于中间值的最大元素(因为它大于中间值的最小元素而不等于中间值的最大元素)。

因此,a 的每一端都是中值的一个元素,否则,我们将有一个大于中值元素的元素,该元素与中值元素相邻,形成更大的中值。

如果 a 是奇数,那么它的长度必须是 3,因为任何更大的奇数长度都可以从距离中位数最远的a 的末尾删除 2,而不改变中位数。

如果a 是偶数,那么它的长度必须为 2,因为任何更大的偶数长度由中间元素的元素预订,内部元素在小于和大于中间值之间交替,必须有一个中间元素与一个较大的元素相邻元素比中位数的另一个元素,形成一个更大的中位数。

这个证明大纲可以使用一些编辑,但无论如何,结论是包含最大中位数的最小数组的长度必须是 2 或 3。

鉴于此,在线性时间内检查每个这样的子数组。 O(n)。

【讨论】:

  • 这是简化的证明。选择一个子数组。如果它的前两个元素都不小于它的中值,则将它们保留为新的子数组。否则,将两者都删除并保留其余部分。重复直到长度为 2 或 3。
  • 准确挑选两个元素并不是必须的。您 xan 将子数组分成两半,并保留中位数较大的一半。如果它们相等,则保留任何一个。顺便说一句,同样的逻辑适用于算术平均值。
  • @n.m. [1,5,3,1] 的中位数为 2。前 2 个 elt 并不都小于中位数,删除它们后我们会得到中位数为 2 的 [3,1],但最佳的是 [5,3 ] 中位数为 4。
  • 这个过程不能保证达到最大中位数,这是一个证明具有最大中位数的最短子数组不能超过3个元素。在你的例子中, (1,5,3,1) 不是具有最大中位数的最短子数组,因为在删除 (1,5) 之后,我们剩下的 (3,1) 是具有相同中位数的较短子数组。
  • @n.m.您给出的只是一个将长数组减少为 2-3 个元素子数组的过程,但是您的过程与找到最大中位数无关,因此不能证明可以在这样的 2 中找到最大中位数-3 elt 子数组。这恰好是真的,但这并不能证明这一点。
【解决方案2】:

这是解决O(n)中问题的算法的Python实现:

import random
import statistics

n = 50
numbers = random.sample(range(n),n)

max_m = 0;
max_a = [];

for i in range(2,3):
    for j in range(0,n-i+1):
        a = numbers[j:j+i]
        m = statistics.median(a)
        if m > max_m:
            max_m = m
            max_a = a

print(numbers)
print(max_m)
print(max_a)

这是蛮力算法 (O(n^3)) 的一种变体,它只搜索长度为 2 或 3 的子数组。原因是对于每个大小为 n 的数组,都存在具有相同或改进的中位数的子数组。递归地应用这个推理,我们可以将子数组的大小减少到 2 或 3。因此,通过仅查看大小为 2 或 3 的子数组,我们可以保证获得具有最大中值的子数组。

操作如下:如果,对于一个连续的子数组(在开头或结尾),至少有一半的元素低于中位数(或低于形成中位数的两个值,如果这是这种情况),删除它们以改善或至少保留中位数。

如果在所有子数组中总是至少有一个元素高于或等于中位数而不是下位数,那么子数组的大小将达到中位数的大小。在这种情况下,这意味着补码将在中位数以下有更多元素,因此,我们可以简单地删除补码并改进(或保留)中位数。因此,我们总是可以执行操作。对于n=3,可能需要删除 2 或 3 个元素才能执行操作,这是不允许的。在这种情况下,结果就是列表本身。

【讨论】:

  • 感谢您的回答!时间复杂度应该大于 O(n^2),因为 statistics.median(a) 应该超过 O(N)。不确定是否通过矛盾证明可以证明答案的长度应始终为 2 或 3。
  • @CipherText 有一种算法可以在O(n) 的平均值中找到中位数:rcoh.me/posts/linear-time-median-finding。由于我们不需要对整个数组进行排序,因此可以进行一些优化。但是,我不确定 Python 统计中使用的实现
  • 是的。我知道快速选择平均可以是 O(N)。但是for i in range(2,n): 是 O(N),for j in range(0,n-i+1): 是另一个 O(N),m = statistics.median(a) 也是 O(N)。所以总数应该大约是O(N^3)。无论如何,我认为您对长度为 2 或 3 的想法是正确的,即使我也找不到数学方法来证明它。谢谢你的回答!
  • 哦,O(n^3) 是对的。我将编辑我的答案。
  • 我用 2 或 3 列表的证明和 O(n) 中的算法编辑了我的答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 2020-09-01
  • 2022-11-01
  • 2021-03-10
相关资源
最近更新 更多