Python Codility Frog River 一次复杂度

Posted

技术标签:

【中文标题】Python Codility Frog River 一次复杂度【英文标题】:Python Codility Frog River One time complexity 【发布时间】:2019-03-04 13:44:57 【问题描述】:

所以这是另一个可能是众所周知的 codility 平台的方法,关于青蛙过河的任务。很抱歉,如果这个问题被问得不好,这是我在这里的第一篇文章。

目标是找到青蛙可以跳到河对岸的最早时间。 例如,给定 X = 5 和数组 A 使得:

  A[0] = 1
  A[1] = 3
  A[2] = 1
  A[3] = 4
  A[4] = 2
  A[5] = 3
  A[6] = 5
  A[7] = 4

函数应该返回 6。

示例测试:(5, [1, 3, 1, 4, 2, 3, 5, 4])

完整的任务内容: https://app.codility.com/programmers/lessons/4-counting_elements/frog_river_one/

所以这是我第一个明显的方法:

def solution(X, A):
    lista = list(range(1, X + 1))
    if X < 1 or len(A) < 1:
        return -1
    found = -1
    for element in lista:
        if element in A:
            if A.index(element) > found:
                found = A.index(element)
        else: return -1
    return found

X = 5
A = [1,2,4,5,3]
solution(X,A)

这个解决方案是 100% 正确的,在性能测试中得到 0%。

所以我认为更少的行+列表理解会得到更好的分数:

def solution(X, A):
    if X < 1 or len(A) < 1:
        return -1
    try:
        found = max([ A.index(element) for element in range(1, X + 1) ])
    except ValueError:
        return -1
    return  found

X = 5
A = [1,2,4,5,3]
solution(X,A)

这个也可以工作,性能为 0%,但无论如何它更快。

我还找到了 deanalvero (https://github.com/deanalvero/codility/blob/master/python/lesson02/FrogRiverOne.py) 的解决方案:

def solution(X, A):
    # write your code in Python 2.6
    frog, leaves = 0, [False] * (X)
    for minute, leaf in enumerate(A):
        if leaf <= X:
            leaves[leaf - 1] = True
        while leaves[frog]:
            frog += 1
            if frog == X: return minute
    return -1

此解决方案在正确性和性能测试中达到 100%。

我的问题可能是因为我不太了解这个时间复杂度的事情。请告诉我最后一个解决方案比我的第二个解决方案更好吗?它在 for 循环中有一个 while 循环!它应该很慢,但事实并非如此。

【问题讨论】:

自动假设具有更多循环的算法总是较慢是一个常见的错误。此外,您甚至没有计算index 中的隐式循环(这是O(n),因为A 是一个无序数组)。 【参考方案1】:

嵌套循环的数量并不能直接告诉您有关时间复杂度的任何信息。令 n 为输入数组的长度。 while 循环的内部需要 平均 O(1) 时间,尽管其最坏情况的时间复杂度为 O(n)。快速解决方案使用布尔数组 leaves,其中在每个索引处,如果有叶子,则其值为 true,否则为 false。在整个算法期间,while 循环的内部被排除不超过 n 次。外部 for 循环也只执行 n 次。这意味着算法的时间复杂度是O(n)

【讨论】:

【参考方案2】:

关键是你的两个初始解都是二次的。它们涉及对每个父元素的 O(n) 次内部扫描(导致 O(n**2))。

快速解决方案最初似乎遭受了同样的命运,因为很明显它包含一个循环中的一个循环。但是内部的 while 循环不会对每个“叶子”进行完全扫描。看看 'frog' 被初始化的地方,你会注意到 while 循环有效地从每个叶子停止的地方开始。

【讨论】:

【参考方案3】:

答案已经被告知,但我将添加一个我认为可能有助于您理解的可选解决方案:

def save_frog(x, arr):
    # creating the steps the frog should make
    steps = set([i for i in range(1, x + 1)])

    # creating the steps the frog already did
    froggy_steps = set()

    for index, leaf in enumerate(arr):
        froggy_steps.add(leaf)
        if froggy_steps == steps:
            return index
    return -1

【讨论】:

您也可以在这种情况下测试len(froggy_steps) == x ,而不是比较两组,如果这两组的大小很大,这应该会略有改进。 解决方案似乎错误,但通过了所有测试用例。 froggy_steps 集应该只存储小于或等于 X 的值。例如如果 X = 4 和 A = [1,3,5,7,4,2],就我阅读问题而言,答案应该是 5。上述解决方案返回 -1。还是我错过了什么?【参考方案4】:

这是一个解决方案,您可以在其中获得 100% 的正确性和性能。

def solution(X, A):
    i = 0
    dict_temp = 
    while i < len(A):
        dict_temp[A[i]] = i
        if len(dict_temp) == X:
            return i
        i += 1
    return -1

【讨论】:

更改A的元素时不起作用。如X, A = 5, [1,3,1,4,10 ,9,2,3, 5, 4,7],输出为(5, 1: 2, 3: 1, 4: 3, 10: 4, 9: 5) 工作完美,但是另一个输入也失败了。 X, A = 5, [1,3,1,6,4,2,3,4] 输出 5 但是在第 5 距离内没有叶子。 我认为 if A[i] &lt;= X: 在 while 行之后是必需的。【参考方案5】:

这是我考虑数字级数总和的 100% 解决方案。

def solution(X, A):
covered = [False] * (X+1)
n = len(A)
Sx = ((1+X)*X)/2 # sum of the numeric progression
for i in range(n):
    if(not covered[A[i]]):
        Sx -= A[i]
    covered[A[i]] = True
    if (Sx==0):
        return i
return -1

【讨论】:

【参考方案6】:

来自@sphoenix 的优化解决方案,无需比较两个sets,它不是很好。

def solution(X, A):
    found = set()
    for pos, i in enumerate(A, 0):
        if i <= X:
            found.add(i)
            if len(found) == X:
                return pos
    return -1

还有一种更优化的二进制数组解决方案

def solution(X, A):
    steps, leaves = X, [False] * X
    for minute, leaf in enumerate(A, 0):
        if not leaves[leaf - 1]:
            leaves[leaf - 1] = True
            steps -= 1
            if 0 == steps:
                return minute
    return -1

最后一个更好,资源更少。 set 比二进制列表消耗更多的资源(内存和 CPU)。

【讨论】:

【参考方案7】:
def solution(X, A):
    
    # if there are not enough items in the list
    if X > len(A):
        return -1
    # else check all items
    else:
        d = 
        for i, leaf in enumerate(A):
            d[leaf] = i
            if len(d) == X:
                return i   

    # if all else fails
    return -1

【讨论】:

【参考方案8】:

我尽量使用简单的指令。

def solution(X, A):
    if (X > len(A)): # check for no answer simple
        return -1
    elif(X == 1):    # check for single element
        return 0
    else:
        std_set = i for i in range(1,X+1) # list of standard order
        this_set = set(A)                   # set of unique element in list
        if(sum(std_set) > sum(this_set)):   # check for no answer complex
            return -1
        else:
            for i in range(0, len(A) - 1):  
                if  std_set:
                    if(A[i] in std_set):
                        std_set.remove(A[i]) # remove each element in standard set
                if not std_set:              # if all removed, return last filled position
                    return(i)

我猜这段代码可能无法满足运行时要求,但它是我能想到的最简单的代码

【讨论】:

【参考方案9】:

我正在使用集合中的 OrderedDict 和前 n 个数字的总和来检查青蛙是否能够越过。

def solution(X, A):
    from collections import OrderedDict as od
    if sum(set(A))!=(X*(X+1))//2:
        return -1
    k=list(od.fromkeys(A).keys())[-1]
    for x,y in enumerate(A):
        if y==k:
            return x

【讨论】:

以上是关于Python Codility Frog River 一次复杂度的主要内容,如果未能解决你的问题,请参考以下文章

如何在颤动中播放 Rive 动画

在 Flutter 中更改 Rive 2 动画

如何在flutter中使用rive的状态机?

如何在 WPF / XAML 项目中使用 Rive 动画?

Flutter Flare, Rive,它可以用作背景吗?

关于 Codility 的 HilbertMaze 任务