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)))算法?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 QuickSort 使用 O(log(n)) 额外空间?