【问题标题】:how to calculate the minimum unfairness sum of a list如何计算列表的最小不公平和
【发布时间】:2020-12-25 16:09:48
【问题描述】:

我试图将问题陈述总结如下::

给定nk 和一个数组(列表)arr,其中n = len(arr)kset (1, n) inclusive 中的integer

对于数组(或列表)myList,不公平和定义为myList 中所有可能对(每个组合有 2 个元素)之间的绝对差值的 sum

解释:如果mylist = [1, 2, 5, 5, 6],则最小不公平和或MUS。请注意,元素在列表中的index 被认为是唯一的,而不是它们的值

MUS = |1-2| + |1-5| + |1-5| + |1-6| + |2-5| + |2-5| + |2-6| + |5-5| + |5-6| + |5-6|

如果你真的需要看问题陈述,那就是HERE

我的目标

给定n, k, arr(如上所述),从所有可能的子数组的不公平总和中找到Minimum Unfairness Sum,每个len(sub array) = k [这是让我们的生活变得轻松的好东西,我相信:)]

我尝试过的

嗯,这里有很多要补充的,所以我会尽量简短。

我的第一种方法是我使用itertools.combinations 来获取所有可能的组合并使用statistics.variance 检查它的spread of data(是的,我知道我真是一团糟)。
在你看到下面的代码之前,你认为这些方差和不公平总和是否完全相关(我知道它们是密切相关的),即minimum variance 的子数组必须是MUS 的子数组??

您只需检查LetMeDoIt(n, k, arr) 函数。如果需要MCVE,请查看下方第二个代码sn-p。

from itertools import combinations as cmb
from statistics import variance as varn

def LetMeDoIt(n, k, arr):
    v = []
    s = []
    subs = [list(x) for x in list(cmb(arr, k))]  # getting all sub arrays from arr in a list

    i = 0
    for sub in subs:
        if i != 0:
            var = varn(sub)  # the variance thingy
            if float(var) < float(min(v)):
                v.remove(v[0])
                v.append(var)
                s.remove(s[0])
                s.append(sub)
            else:
                pass

        elif i == 0:
            var = varn(sub)
            v.append(var)
            s.append(sub)
            i = 1

    final = []
    f = list(cmb(s[0], 2))  # getting list of all pairs (after determining sub array with least MUS)
    
    for r in f:
        final.append(abs(r[0]-r[1]))  # calculating the MUS in my messy way

    return sum(final)

上面的代码对n&lt;30 工作正常,但除此之外还引发了MemoryError。 在 Python 聊天中,Kevin 建议我尝试generator,即memory efficient(确实如此),但由于生成器也会动态生成这些组合,因为我们iterate 在它们之上,它应该花费超过 140 小时( :/) 对于 n=50,估计 k=8。

我在 SO HERE 上发布了与问题相同的问题(您可能想看看以正确理解我 - 它有讨论和融合的答案,这让我采用了我的第二种方法 - 一个更好的方法(我应该说fusion 的方法 xD))。

第二种方法

from itertools import combinations as cmb

def myvar(arr):   # a function to calculate variance
    l = len(arr)
    m = sum(arr)/l
    return sum((i-m)**2 for i in arr)/l

def LetMeDoIt(n, k, arr):
    sorted_list = sorted(arr)  # i think sorting the array makes it easy to get the sub array with MUS quickly
    variance = None
    min_variance_sub = None
    
    for i in range(n - k + 1):
        sub = sorted_list[i:i+k]
        var = myvar(sub)
        if variance is None or var<variance:
            variance = var
            min_variance_sub=sub
            
    final = []
    f = list(cmb(min_variance_sub, 2))  # again getting all possible pairs in my messy way

    for r in f:
        final.append(abs(r[0] - r[1]))

    return sum(final)

def MainApp():
    n = int(input())
    k = int(input())

    arr = list(int(input()) for _ in range(n))

    result = LetMeDoIt(n, k, arr)

    print(result)    

if __name__ == '__main__':
    MainApp()

这段代码非常适合n up to 1000(可能更多),但由于time out而终止(5秒是在线判断的限制:/)对于n超过10000(最大的测试用例有n=100000 )。

=====

您将如何解决这个问题以在给定的时间限制(5 秒)内处理所有测试用例? (问题在algorithm & dynamic programming 下列出)

(您可以参考一下

  1. successful submissions(py3, py2, C++, java) 关于这个问题的其他候选人 - 这样你就可以 向我和未来的访客解释这种方法
  2. an editorial 由问题制定者解释如何解决问题
  3. a solution code 由问题制定者本人 (py2, C++)。
  4. Input data (test cases) and expected output

编辑1 ::

对于这个问题的未来访客,我到目前为止的结论是,
varianceunfairness sumperfectly 不相关(它们与 strongly 相关)这意味着在许多整数列表中,带有 minimum variance 的列表并不总是带有 @ 的列表987654370@。如果你想知道为什么,我实际上在数学堆栈交换HERE 上提出了一个单独的问题,其中一位数学家为我证明了它xD(值得一看,因为它出乎意料)

就整个问题而言,您可以阅读下面 archer 和 Attersson 的答案(仍在尝试找出一种天真的方法来执行此操作 - 不过现在应该不远了)


感谢您的任何帮助或建议:)

【问题讨论】:

  • 我对这个(有趣的)问题有复杂的感觉,因为这是一个hackerrank挑战,而在StackOverflow上寻求帮助违背了挑战的目的......
  • "if mylist = [1, 2, 5, 5, 6], then [...] MUS = |1-2| + |1-5| + |1-5| + |1-6| + |2-5| + |2-5| + |2-6| + |5-5| + |5-5|" 你确定你没有在最后错过+ |5-6| + |5-6| 吗?
  • @Stef 哦,这是一个错字:/ 感谢您指出这一点:)。已编辑。
  • 请注意“子数组”被认为是a contiguous section of the array。您的意思是“子集”吗?
  • 您链接到的社论指出,“在这个问题中,我们得到一个包含 N 个数字的列表,从中选择 K 个数字,以使不公平和最小化。”没有提到 K 数字必须是连续的。 K 个连续的数字是从 sorted 数组中选择的,这意味着它们在原始数组中不一定是连续的。在问题陈述中澄清这一点对您很重要。

标签: python arrays algorithm dynamic-programming


【解决方案1】:

您必须处理您的列表 SORTED 并且只检查具有连续元素的子列表。这是因为默认情况下,任何包含至少一个不连续元素的子列表都将具有更高的不公平性总和。

例如,如果列表是

[1,3,7,10,20,35,100,250,2000,5000] 并且要检查长度为 3 的子列表,则解决方案必须是 [1,3,7] [3,7,10] 之一] [7,10,20] 等 任何其他子列表,例如 [1,3,10] 将具有更高的不公平性总和,因为 10>7 因此它与其余元素的所有差异都将大于 7 [1,7,10](左侧不连续)与 1 相同

鉴于此,您只需检查长度为 k 的连续子列表,从而显着减少执行时间

关于编码,这样的事情应该可以工作:

def myvar(array):
    return sum([abs(i[0]-i[1]) for i in itertools.combinations(array,2)])  
  
def minsum(n, k, arr):
        res=1000000000000000000000 #alternatively make it equal with first subarray
        for i in range(n-k):
            res=min(res, myvar(l[i:i+k]))
        return res
    

【讨论】:

  • 关于您建议的代码,在开始时定义变量res - 这与动态编程有关吗(我知道它是什么,我不知道该怎么做xD)?你对variance vs MUS thing(他们的共同关系)持什么立场?
  • res 是最小 sub 的初始值。我们必须在最初设置一个非常大的值,它会逐渐被循环内的较低值替换。当循环结束时 res 将是我们正在寻找的最低不公平总和。如果最终 res 有可能非常大,您可以最初设置 res=myvar(l[0:k]) 而不是 1000000000000,以便它始终返回正确的结果
  • 我明白了。实际上,我将设置一个随机值,至少可以节省一些宝贵的微秒时间;)(如果我是真的)。​​
  • 我在我的代码中添加了一个 myvar 函数。检查整个代码是否正常工作
  • 它可以工作,但由于 nMy Code... 请看一下。如果您有兴趣,可以查看三个不同的成功提交HERE - 因此,要么向我和未来的访问者解释他们的方法,要么基于这些方法提出其他方法。仅供参考,这个动态编程问题的提到的雾凇复杂度应该是n log n
【解决方案2】:

我看到这个问题仍然没有完整的答案。我会写一个正确算法的轨迹,它将通过判断。为了尊重 Hackerrank 挑战的目的,我不会编写代码。因为我们有可行的解决方案。

  1. 必须对原始数组进行排序。复杂度为 O(NlogN)

  2. 此时您可以检查连续的子数组,因为不连续的子数组会导致更差(或相等,但不是更好)的“不公平和”。这在弓箭手的回答中也有解释

  3. 最后一个检查通道,找到最小的“不公平和”可以在 O(N) 中完成。您需要为每个连续的 k 长子数组计算 US。错误是在 O(k) 中完成每一步都重新计算这个,这使这段代码的复杂度达到了 O(k*N)。正如您发布的社论所示,它可以在 O(1) 中完成,包括数学公式。它需要在步骤 1 之后对累积数组进行先前的初始化(在 O(N) 中完成,空间复杂度也为 O(N))。

它可以工作,但由于 n

(来自 cmets 关于弓箭手的问题)

为了解释第 3 步,考虑 k = 100。您正在滚动 N 长数组,第一次迭代,您必须照常计算从元素 0 到 99 的子数组的 US,需要 100 次。下一步需要您为与前一个元素仅相差 1 到 100 的子数组计算相同的值。然后是 2 到 101 等。 如果有帮助,就把它想象成一条蛇。删除一个块并添加一个块。 无需执行整个 O(k) 滚动。只需按照社论中的说明计算数学,您将在 O(1) 中完成。

因此,由于第一次排序,最终的复杂度将渐近为 O(NlogN)。

【讨论】:

  • 是的没问题:-) 绝对没有downvote 或任何东西。我仍然必须在界限内指定和帮助:)(并查看问题分数,这很不错)
  • 呵呵呵呵。你的立场是绝对正确的:) .. 不管怎样,我实际上在math.stackexchange HERE 上添加了我的问题的一部分variance vs minimum unfairness sum,令我惊讶的是,我得到了一个意想不到的答案。
  • 非常有趣,写得很好!所以不幸的是,使用方差的想法是行不通的。但另一方面通过应用“蛇”优化,计算不公平和没有问题
  • 我试图避免转述.. 我试图参考最后的 O(1*(N-k)) 段落。添加一个,删除一个,当您滚动查找具有最小不公平性总和的子数组时。在我的回答中,我写道“把它想象成一条蛇”
  • @Atterson 看起来我以某种方式创建了一个聊天室(所以强迫我这样做,我发誓;p)。实际上我试图实现你提到的方法,我完全理解“蛇”类比,我理解以前的方法出现超时错误的原因,我理解我应该如何继续解决算法(感谢你的解释的答案 - 我现在了解动态编程的基础知识以降低时间复杂度)。我即将弄清楚社论中解释的数学,整个过程中只有一步我似乎无法弄清楚。
猜你喜欢
  • 2014-04-23
  • 2014-11-20
  • 2019-08-12
  • 2016-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多