简体   繁体   English

Pythonic方法检查列表中的不连续元素数量是否达到给定值

[英]Pythonic way of checking if indefinite # of consec elements in list sum to given value

Having trouble figuring out a nice way to get this task done. 无法找到完成此任务的好方法。

Say i have a list of triangular numbers up to 1000 -> [0,1,3,6,10,15,..]etc 假设我有一个最多1000的三角形数字列表 - > [0,1,3,6,10,15,..]等

Given a number, I want to return the consecutive elements in that list that sum to that number. 给定一个数字,我想返回该列表中与该数字相加的连续元素。

ie

64 --> [15,21,28]
225 --> [105,120]
371 --> [36, 45, 55, 66, 78, 91]

if there's no consecutive numbers that add up to it, return an empty list. 如果没有连续的数字加起来,则返回一个空列表。

882 --> [ ] 

Note that the length of consecutive elements can be any number - 3,2,6 in the examples above. 请注意,连续元素的长度可以是任何数字 - 在上面的示例中为3,2,6。

The brute force way would iteratively check every possible consecutive pairing possibility for each element. 蛮力方式将迭代地检查每个元素的每个可能的连续配对可能性。 (start at 0, look at the sum of [0,1], look at the sum of [0,1,3], etc until the sum is greater than the target number). (从0开始,查看[0,1]的总和,查看[0,1,3]之和等,直到总和大于目标数)。 But that's probably O(n*2) or maybe worse. 但那可能是O(n * 2)或者可能更糟。 Any way to do it better? 有什么方法可以做得更好吗?

UPDATE: Ok, so a friend of mine figured out a solution that works at O(n) (I think) and is pretty intuitively easy to follow. 更新:好的,所以我的一个朋友找到了一个在O(n)工作的解决方案(我认为)并且非常直观易于理解。 This might be similar (or the same) to Gabriel's answer, but it was just difficult for me to follow and I like that this solution is understandable even from a basic perspective. 这可能与Gabriel的答案相似(或相同),但我很难跟上,我喜欢这个解决方案即使从基本的角度来看也是可以理解的。 this is an interesting question, so I'll share her answer: 这是一个有趣的问题,所以我将分享她的答案:

def findConsec(input1 = 7735):

    list1 = range(1, 1001)
    newlist = [reduce(lambda x,y: x+y,list1[0:i]) for i in list1]

    curr = 0
    end = 2
    num = sum(newlist[curr:end])

    while num != input1:

        if num < input1:
            num += newlist[end]
            end += 1

        elif num > input1:
            num -= newlist[curr]
            curr += 1

        if curr == end:
            return []

    if num == input1:
        return newlist[curr:end]

A 3-iteration max solution Another solution would be to start from close where your number would be and walk forward from one position behind. 3次迭代最大解决方案另一种解决方案是从关闭位置开始,然后从后面的一个位置向前走。 For any number in the triangular list vec , their value can be defined by their index as: 对于三角形列表vec中的任何数字,它们的值可以通过它们的索引定义为:

vec[i] = sum(range(0,i+1))

The division between the looking-for sum value and the length of the group is the average of the group and, hence, lies within it, but may as well not exist in it. 查找总和值与组长度之间的区别是组的平均值,因此位于组内,但也可能不存在。 Therefore, you can set the starting point for finding a group of n numbers whose sum matches a value val as the integer part of the division between them. 因此,您可以设置起始点,以查找一组n个数字,其总和与值val匹配,作为它们之间除法的整数部分。 As it may not be in the list, the position would be that which minimizes their difference. 因为它可能不在列表中,所以该位置将最小化它们的差异。

# vec as np.ndarray -> the triangular or whatever-type series
# val as int -> sum of n elements you are looking after
# n as int -> number of elements to be summed
import numpy as np

def seq_index(vec,n,val):
    index0 = np.argmin(abs(vec-(val/n)))-n/2-1 # covers odd and even n values
    intsum = 0 # sum of which to keep track
    count = 0 # counter
    seq = [] # indices of vec that sum up to val

    while count<=2: # walking forward from the initial guess of where the group begins or prior to it
        intsum = sum(vec[(index0+count):(index0+count+n)])
        if intsum == val:
            seq.append(range(index0+count,index0+count+n))
        count += 1
    return seq

# Example

vec = []

for i in range(0,100):
    vec.append(sum(range(0,i))) # build your triangular series from i = 0 (0) to i = 99 (whose sum equals 4950)

vec = np.array(vec) # convert to numpy to make it easier to query ranges

# looking for a value that belong to the interval 0-4590
indices = seq_index(vec,3,4)
# print indices
print indices[0]
print vec[indices]
print sum(vec[indices])

Returns 返回

print indices[0] -> [1, 2, 3]
print vec[indices] -> [0 1 3]
print sum(vec[indices]) -> 4 (which we were looking for)

This seems like an algorithm question rather than a question on how to do it in python. 这似乎是一个算法问题而不是如何在python中执行它的问题。

Thinking backwards I would copy the list and use it in a similar way to the Sieve of Eratosthenes. 向后思考我会复制列表并以类似于Eratosthenes筛选的方式使用它。 I would not consider the numbers that are greater than x. 我不会考虑大于x的数字。 Then start from the greatest number and sum backwards. 然后从最大数字开始并向后总结。 Then if I get greater than x, subtract the greatest number (exclude it from the solution) and continue to sum backward. 然后,如果我得到大于x,减去最大数字(从解决方案中排除)并继续向后求和。 This seems the most efficient way to me and actually is O(n) - you never go back (or forward in this backward algorithm), except when you subtract or remove the biggest element, which doesn't need accessing the list again - just a temp var. 这对我来说似乎是最有效的方式,实际上是O(n) - 你永远不会回头(或者在这个后向算法中前进),除非你减去或删除最大的元素,不需要再次访问列表 - 只是温度变量

To answer Dunes question: 回答沙丘问题:

Yes, there is a reason - to subtracts the next largest in case of no-solution that sums larger. 是的,有一个原因 - 在没有解决方案的情况下减去下一个最大值。 Going from the first element, hit a no-solution would require access to the list again or to the temporary solution list to subtract a set of elements that sum greater than the next element to sum. 从第一个元素开始,点击无解决方案将需要再次访问列表或临时解决方案列表以减去一个总和大于下一个元素的元素集合。 You risk to increase the complexity by accessing more elements. 您可能会通过访问更多元素来增加复杂性。

To improve efficiency in the cases where an eventual solution is at the beginning of the sequence you can search for the smaller and larger pair using binary search. 为了提高最终解决方案位于序列开头的情况下的效率,您可以使用二进制搜索来搜索越来越小的对。 Once a pair of 2 elements, smaller than x is found then you can sum the pair and if it sums larger than x you go left, otherwise you go right. 一旦找到一对小于x的2个元素,那么你可以对该对进行求和,如果它总和大于x,则向左移动,否则你就向右移动。 This search has logarithmic complexity in theory. 该搜索在理论上具有对数复杂性。 In practice complexity is not what it is in theory and you can do whatever you like :) 在实践中复杂性不是理论上的,你可以做任何你喜欢的事情:)

You should pick the first three elements, sum them and do and then you keep subtracting the first of the three and add the next element in the list and see if the sum add up to whatever number you want. 你应该选择前三个元素,对它们求和,然后你继续减去三个元素中的第一个,然后在列表中添加下一个元素,看看总和是否加起来你想要的任何数字。 That would be O(n). 那将是O(n)。

# vec as np.ndarray

import numpy as np    

itsum = sum(list[0:2]) # the sum you want to iterate and check its value
sequence = [[] if itsum == whatever else [range(0,3)]] # indices of the list that add up to whatever (creation)
for i in range(3,len(vec)):
    itsum -= vec[i-3]
    itsum += vec[i]
    if itsum == whatever:
        sequence.append(range(i-2,i+1)) # list of sequences that add up to whatever

The solution you provide in the question isn't truly O(n) time complexity -- the way you compute your triangle numbers makes the computation O(n 2 ). 您在问题中提供的解决方案并非真正的O(n)时间复杂度 - 计算三角形数字的方式使得计算O(n 2 )。 The list comprehension throws away the previous work that want into calculating the last triangle number. 列表理解抛弃了以前需要计算最后三角形数的工作。 That is: tn i = tn i-1 + i (where tn is a triangle number). 即:tn i = tn i-1 + i(其中tn是三角形数)。 Since you also, store the triangle numbers in a list, your space complexity is not constant, but related to the size of the number you are looking for. 由于您也将三角形数字存储在列表中,因此空间复杂度不是恒定的,而是与您要查找的数字的大小相关。 Below is an identical algorithm, but is O(n) time complexity and O(1) space complexity (written for python 3). 下面是一个相同的算法,但是O(n)时间复杂度和O(1)空间复杂度(为python 3编写)。

# for python 2, replace things like `highest = next(high)` with `highest = high.next()`
from itertools import count, takewhile, accumulate

def find(to_find):
    # next(low) == lowest number in total
    # next(high) == highest number not in total
    low = accumulate(count(1)) # generator of triangle numbers
    high = accumulate(count(1))
    total = highest = next(high)
    # highest = highest number in the sequence that sums to total

    # definitely can't find solution if the highest number in the sum is greater than to_find
    while highest <= to_find:
        # found a solution
        if total == to_find:
            # keep taking numbers from the low iterator until we find the highest number in the sum
            return list(takewhile(lambda x: x <= highest, low))
        elif total < to_find:
            # add the next highest triangle number not in the sum
            highest = next(high)
            total += highest
        else: # if total > to_find
            # subtract the lowest triangle number in the sum
            total -= next(low)

    return []

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

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