Codility峰的O(N * log(log(N)))算法?

Posted

技术标签:

【中文标题】Codility峰的O(N * log(log(N)))算法?【英文标题】:O(N*log(log(N))) algorithm for Codility's Peaks? 【发布时间】:2014-10-01 20:38:03 【问题描述】:

任务说明在这里:https://codility.com/demo/take-sample-test/peaks 也在这里:Codility Peaks Complexity

首先,我尝试自己解决这个问题,但只能想出我认为是蛮力解决方案的方法。但是,它的得分为 100/100:https://codility.com/demo/results/demoRNWC3P-X4U

这显然让我完全不满意。 ;) 为N 的每个因子调用外部循环(或为每个峰值,以较小者为准),为每个峰值调用内部循环(只需检查每个块中是否有峰值)。也许那是O(N^2),也许更好一点(因为它在时间限制内通过了测试)但我几乎可以肯定它不是O(N*log(log(N)))

然后我尝试搜索O(N*log(log(N))) 解决方案,但其他人似乎都有与我非常相似的解决方案。

那么有人对O(N*log(log(N)))(或更好的)解决方案有想法吗?

另外,如果有人能告诉我我的解决方案有多复杂,我将不胜感激。

【问题讨论】:

不登录就看不到问题,能否复现问题本身的问题描述? Afaik,它受版权保护,所以不应该在这里复制?但是,一位用户已经这样做了,我添加了一个指向他的问题的链接(带有任务描述)。 【参考方案1】:

您的代码是 O(n d(n)),其中 d(n) 是 n 的除数。在 [1, 100000] 上,d(n) 在 83160 = 2^3 3^3 5 7 11 处最大化,有 126 个除数。 According to Wikipedia,对于每个 epsilon>0,d(n) 为 o(n^epsilon),因此它的增长相当缓慢。

要获得 O(n log log n) 解决方案,请构建一个部分和数组,告诉您每个点还剩下多少个峰值。然后您可以判断 O(1) 时间间隔内是否存在峰值。然后检查除数 d 需要 O(n/d) 时间。在所有除数 d 上加 n/d 与在所有除数 d 上加 d 相同,根据同一个 Wikipedia 页面,结果是 O(n log log n)。

【讨论】:

除了最后一部分,我都明白了。您是什么意思“在所有除数 d 上加 n/d 与在所有除数 d 上加起来 d 相同”? @NPS:在由 i(d) = n/d 给出的 n 的除数集上有一个对合。 (对合是从一个集合到自身的双射,当应用两次时,什么都不做。)所以 sum(d 除以 n) n/d = sum(d 除以 n) n/i(d) = sum(d 除以 n ) d.【参考方案2】:

我已经按照 tmyklebu(谢谢!)建议的风格实现了一个解决方案,它应该是 n.log(log(n))。 Codility 不再测试这个问题的“性能”(!),但 python 解决方案的准确度为 100%。

顺便说一句,如果您一直在学习 Codility 课程,您会记得在 Lesson 8: Prime and composite numbers 中,谐波数运算的总和将产生 O(log(n)) 复杂度。我们有一个简化的集合,因为我们只关注因子分母。 Lesson 9: Sieve of Eratosthenes 展示了素数倒数之和如何为 O(log(log(n))) 并声称“证明是不平凡的”。除数倒数的总和与素数倒数的总和不同,但我建议它也属于“非平凡”证明类别!

def solution(data):

    length = len(data)

    # array ends can't be peaks, len < 3 must return 0    
    if len < 3:
        return 0

    peaks = [0] * length

    # compute a list of 'peaks to the left' in O(n) time
    for index in range(2, length):
        peaks[index] = peaks[index - 1]

        # check if there was a peak to the left, add it to the count
        if data[index - 1] > data[index - 2] and data[index - 1] > data[index]:
            peaks[index] += 1

    # candidate is the block size we're going to test
    for candidate in range(3, length + 1):

        # skip if not a factor
        if length % candidate != 0:
            continue

        # test at each point n / block
        valid = True
        index = candidate
        while index != length:

            # if no peak in this block, break
            if peaks[index] == peaks[index - candidate]:
                valid = False
                break

            index += candidate

        # one additional check since peaks[length] is outside of array    
        if index == length and peaks[index - 1] == peaks[index - candidate]:
            valid = False

        if valid:
            return length / candidate

    return 0

【讨论】:

如果条件意味着没有峰值,您是如何得出结论的? if peaks[index] == peaks[index - Candidate]: valid = False break 另外,你能否解释一下第二个 if 条件? if index == length and peaks[index - 1] == peaks[index - Candidate]: valid = False 回答你的第一个问题:看看“峰”包含什么。它是迄今为止发现的峰值的累积计数(在当前索引的左侧)。如果比较数组中的两个点并且它们具有相同的值,则没有找到新的峰值。 在回答你的第二个问题时,数组的结尾('while' 循环的最后一次迭代应该是什么是特殊的。规则规定数据数组的结尾不能被视为峰值。根据我们的设置方式,peaks[length] 将不在数组中,因此我们需要查看 peaks[length-1]。 为了更好地掌握正在发生的事情,您可以尝试一个小数组并在纸上完成。或者看看@rafalio 的例子,挑几个关键点:***.com/questions/20886486/codility-peaks-complexity

以上是关于Codility峰的O(N * log(log(N)))算法?的主要内容,如果未能解决你的问题,请参考以下文章

什么是 O(log* N)?

大 O - O(log(n)) 代码示例

为啥 QuickSort 使用 O(log(n)) 额外空间?

f = Ω(log n) 和 g = O(n) 是 g(n) = O(f (n))

几种排序算法

数组中的大多数元素分治 O(N.log(N))