简体   繁体   English

如何优化列表操作? (CodeFights)

[英]How to optimise list operations? (CodeFights)

So I have a function that performs just fine on small lists.所以我有一个在小列表上表现很好的函数。 It's function is to check if removing one element from the sequence will make the sequence a strictly increasing sequence:它的功能是检查从序列中删除一个元素是否会使序列成为严格递增的序列:

def almostIncreasingSequence(sequence):
    length = len(sequence)
    for i in range(1, length):
        newSequence = sequence[:i-1] + sequence[i:]
        if checkIfSorted(newSequence, length):
            return True
    return checkIfSorted(sequence[:length-1], length)

def checkIfSorted(sequence, length):
    for i in range(1, length - 1):
        if sequence[i-1] >= sequence[i]:
            return False
    return True

But I need it to work on lists up to 100,000 elements long.但是我需要它来处理长达 100,000 个元素的列表。 What sort of optimizations could I make for this to work faster?我可以进行哪些优化以使其更快地工作? Right now, it's abysmally slow on lists of 100,000, working through a few thousand elements a second.现在,它在 100,000 个列表上非常慢,每秒处理几千个元素。

I wrote another answer on this site answering almost the same question as you, but mine was for checking if removing at most one element from the sequence makes it strictly increasing. 在该站点上写了另一个答案,回答的问题几乎与您相同,但是我的任务是检查从序列中删除最多一个元素是否会使它严格增加。 That may be what you mean--there seems to be no practical difference. 那可能就是您的意思-似乎没有实际区别。 It seems you want my second solution, copied here. 看来您想要我的第二个解决方案,复制在这里。

def first_bad_pair(sequence, k):
    """Return the first index of a pair of elements in sequence[]
    for indices k-1, k+1, k+2, k+3, ... where the earlier element is
    not less than the later element. If no such pair exists, return -1."""
    if 0 < k < len(sequence) - 1:
        if sequence[k-1] >= sequence[k+1]:
            return k-1
    for i in range(k+1, len(sequence)-1):
        if sequence[i] >= sequence[i+1]:
            return i
    return -1

def almostIncreasingSequence(sequence):
    """Return whether it is possible to obtain a strictly increasing
    sequence by removing no more than one element from the array."""
    j = first_bad_pair(sequence, -1)
    if j == -1:
        return True  # List is increasing
    if first_bad_pair(sequence, j) == -1:
        return True  # Deleting earlier element makes increasing
    if first_bad_pair(sequence, j+1) == -1:
        return True  # Deleting later element makes increasing
    return False  # Deleting either does not make increasing

Your code is slow because it, like my first solution, makes new sequences by joining slices. 您的代码很慢,因为它像我的第一个解决方案一样,通过加入切片来创建新序列。 This copies almost the entire sequence, and doing that many times slows the code. 这几乎复制了整个序列,并且这样做会减慢代码速度。 The code above avoids that by complicating the routine that checks a sequence to see if it is strictly increasing. 上面的代码通过使检查序列以查看序列是否严格增加的例程复杂化来避免这种情况。 Check my other linked answer for more details. 检查我的其他链接的答案以获取更多详细信息。

My solution: 我的解决方案:

def is_almost_increasing(x):
    lastx = x[0]  # value to use in the next comparison
    found_one = False
    for i in range(1, len(x)):
        if x[i] <= lastx:
            if found_one:
                return False
            found_one = True
            if i > 1 and x[i] <= x[i-2]:  # i > 1 in case the first comparison failed
                break
        lastx = x[i]
    return True


print('\nThese should be True.')
print(is_almost_increasing([1]))
print(is_almost_increasing([1, 2]))
print(is_almost_increasing([1, 2, 3]))
print(is_almost_increasing([1, 3, 2]))
print(is_almost_increasing([10, 1, 2, 3, 4, 5]))
print(is_almost_increasing([0, -2, 5, 6]))
print(is_almost_increasing([1, 1]))
print(is_almost_increasing([1, 2, 3, 4, 3, 6]))
print(is_almost_increasing([1, 2, 3, 4, 99, 5, 6]))
print(is_almost_increasing([1, 2, 2, 3]))

print('\nThese should be False.')
print(is_almost_increasing([1, 3, 2, 1]))
print(is_almost_increasing([3, 2, 1]))
print(is_almost_increasing([1, 1, 1]))

This is pretty similar to, but slightly shorter than, Rory Daulton's. 这与Rory Daulton的非常相似,但略短一些。 I borrowed his test code from the link he provided, so thanks to him for that. 我从他提供的链接中借用了他的测试代码,因此感谢他。 The point is that you don't want to build a lot of secondary lists, which is inefficient. 关键是您不想建立很多次要列表,这效率很低。 To get a really big improvement in efficiency you almost always need to find a better algorithm. 为了真正实现效率的大幅提高,您几乎总是需要找到一种更好的算法。

The two complications here are (1) what to do when the first element fails the test? 这里的两个复杂性是(1)当第一个元素未通过测试时该怎么办? (2) when you find an element out of sequence, do you drop that element or the one before it? (2)当您发现某个元素不按顺序排列时,您会丢弃该元素还是该元素之前的元素? The comments address that. 评论解决了这个问题。

Okay I know this post has already an answer. 好的,我知道这篇文章已经有答案了。 But I will just like to offer my code for same problem. 但是我只想为同样的问题提供我的代码。 Suppose you have two lists lis1 and lis2 as follows:- 假设您有两个列表lis1和lis2,如下所示:

test_lis1 = [1, 2, 3, 4, 5, 2, 6]
test_lis2 = [1, 2, 3, 1, 4, 5, 1]
test_lis3 = [7, 1, 2, 3, 4, 5, 6]

Now, I guess your problem is that you want to know if this list can be converted to a strictly increasing subsequence by removing ONLY one element. 现在,我想您的问题是,您想知道是否可以通过仅删除一个元素来将此列表转换为严格增加的子序列。 If we need to remove two, Then answer is no. 如果我们需要删除两个,则答案为否。

Here is the function:- 这是功能:-

def remove_one_only(lis):
    len_lis = len(lis)
    i = 0
    j = 1
    rem_count = 0
    flag = False
    while j <= len_lis - 1:
        if lis[i] > lis[j]:
            rem_count += 1
            if rem_count == 2:
                break
            if i > 0:
                j += 1
                continue
        i = j
        j += 1
    if rem_count == 1:
        flag = True
    return flag

This function will tell you answer in yes or no if removing only one element can convert list into strictly increasing list and it runs in O(n). 如果仅删除一个元素就可以将列表转换为严格增加的列表并且以O(n)运行,则此函数将以是或否告诉您答案。 This is the fastest you can get. 这是最快的速度。

This would be the most pythonic solution I've found, but it's not the fastest.这将是我找到的最 Pythonic 的解决方案,但它不是最快的。

def almostIncreasingSequence(sequence):
  return any([all([i<j for i,j in zip(sequence[:k]+sequence[k+1:],(sequence[:k]+sequence[k+1:])[1:])]) for k in range(len(sequence))])

Looks nice, though.不过看起来不错:) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM