如何根据列表中多个项目的结果计算一个列表?

3

我有一个列表 A=[a,b,c,d]。需要根据 A 中每个项目之间的操作计算出一个新列表 B

B= [a, b-(a), c-(a+(b-a)), d-(a+(b-a)+(c-(a+(b-a)))) ]

有没有一种Pythonic的方式来完成这个任务?List A并不总是一个由4个项组成的列表,所以解决方案需要适用于任意长度的列表。提前致谢。

5个回答

1

所有的术语都会被消除(c-(a+(b-a)) 简化为 c - bd-(a+(b-a)+(c-(a+(b-a)))) 简化为 d - c),所以这里真正的算法是每个术语等于匹配术语减去前一个术语。这样可以大大简化事情:

B = [A[0]]  # Initial term has no prior to subtract from it
B += [x - y for x, y in zip(A[1:], A)]  # All other terms computed by subtracting term n - 1 from term n

如果你想简化这个代码(忽略引用库),你可以插入一个虚拟的 0来得到第一个元素的结果,而无需显式地进行特殊处理:

from itertools import chain  # At top of file

B = [x - y for x, y in zip(A, chain([0], A))]

如果你喜欢使用map等方法进行微优化,你可以将后者替换为:

from operator import sub  # At top of file

B = [*map(sub, A, chain([0], A))]

并将所有工作推向C层(无需逐个元素执行字节码)。

你怎么知道它们会抵消?[他们可能不会] - Kelly Bundy
1
@KellyBundy:呵呵。当然,浮点数就是魔鬼(我以前已经说过了,现在还要再说一遍)。尽管如此,从简化后的逻辑数学计算结果来看,实际上比从未简化的表达式得到的结果更正确(从数学角度来看),由于C语言的双精度类型double的精度不足,未简化的表达式正在获得不同的结果。很少有情况下你会希望浮点数的缺陷使你的结果偏离数学上正确的答案,但我认为在极为奇怪的情况下或许会出现这种情况。 - ShadowRanger
“简化”版本根据类型和意图可能更“正确”。但它不仅对float,而且例如对于Counter也偏离了参考代码,请参见我的答案中的测试。甚至可以考虑一些自定义类型。问题并没有说与好数字有任何关系。整个问题中唯一出现的数字是指向列表长度的“4”。我们所知道的关于这些值的信息只是它们支持+- - Kelly Bundy

1
观察到您的列表表达式可以简化为:
B = [a, b-a, c-b, d-c]

考虑到这一点,我们可以使用列表推导式:

[y - x for x, y in zip([0] + data, data)]

例如,
data = [1, 2, 7, 6]
result = [y - x for x, y in zip([0] + data, data)]
print(result)

输出:

[1, 1, 5, -1]

你怎么知道它们会抵消?[它们可能不会]。 - Kelly Bundy

0

有两种解决方案,它们不假设事物“抵消”(因为这对于标准类型如floatCounter已经是错误的,如下所示)。

如果我正确理解了模式,第一个B值应该是第一个A值,然后每个下一个B值始终是前面所有B值的总和减去下一个A值。一种方法是:

B = []
for a in A:
    if not B:
        b = sumB = a
    else:
        b = a - sumB
        sumB = sumB + b
    B.append(b)

使用 itertools.accumulateoperator.sub 的有趣方法:

B = A[:1]
B += map(sub, A[1:], accumulate(B))

测试:

                        A = [31, 41, 59, 26]
reference                   [31, 10, 18, -33]
subtract_neighbors  correct [31, 10, 18, -33]
loop                correct [31, 10, 18, -33]
fun                 correct [31, 10, 18, -33]

                        A = [1, 1, 1e-20, 1e-20]
reference                   [1, 0, -1.0, 1e-20]
subtract_neighbors  wrong   [1, 0, -1.0, 0.0]
loop                correct [1, 0, -1.0, 1e-20]
fun                 correct [1, 0, -1.0, 1e-20]

                        A = [Counter(), Counter({None: 1}), Counter(), Counter({None: 1})]
reference                   [Counter(), Counter({None: 1}), Counter(), Counter()]
subtract_neighbors  wrong   [Counter(), Counter({None: 1}), Counter(), Counter({None: 1})]
loop                correct [Counter(), Counter({None: 1}), Counter(), Counter()]
fun                 correct [Counter(), Counter({None: 1}), Counter(), Counter()]

执行上述检查的代码(在线尝试!):

def reference(A):
    a, b, c, d = A
    return [a, b-(a), c-(a+(b-a)), d-(a+(b-a)+(c-(a+(b-a)))) ]

def subtract_neighbors(A):
    a, b, c, d = A
    return [a, b-a, c-b, d-c]

def loop(A):
    B = []
    for a in A:
        if not B:
            b = sumB = a
        else:
            b = a - sumB
            sumB = sumB + b
        B.append(b)
    return B

def fun(A):
    B = A[:1]
    B += map(sub, A[1:], accumulate(B))
    return B

from collections import Counter
from itertools import accumulate
from operator import sub

funcs = [
    reference,
    subtract_neighbors,
    loop,
    fun,
]

tests = [
    [31, 41, 59,26],
    [1, 1, 1e-20, 1e-20],
    [Counter(), Counter([None])] * 2,
]

for A in tests:
    print('                        A =', A)
    for func in funcs:
        result = func(A)
        if func is reference:
            expect = result
            correctness = '       '
        else:
            correctness = 'correct' if result == expect else 'wrong  '
        print(f'{func.__name__:19}', correctness, result)
    print()

-1

根据评论区的建议,这是最简单和最快的解决方案:

A = [5, 9, 3, 8]
B = [x - y for x, y in zip(A, [0] + A)]

这将输出:

B
[5, 4, -6, 5]

1
使用 sum() 会导致二次运行时间。 - BrokenBenchmark
@ShadowRanger 如果你在代数上小心的话,甚至不需要使用 sum()。请看我的回答。 - BrokenBenchmark
@BrokenBenchmark:哈哈,我也注意到了这个问题,但是在你发布回答之前我就开始撰写我的答案了(而且SO没在我打字时通知我有新的回答,太糟糕了),所以最终得到了基本相同的解决方案。 - ShadowRanger
@ShadowRanger 英雄所见略同!(还是愚人常有相似之处?) :) - BrokenBenchmark
顺便说一下,为了缩短运行时间,创建一个单独的变量来跟踪总和,并在for循环的每次迭代中更新它。抱歉之前没有提供解决方案。 - BrokenBenchmark
显示剩余2条评论

-1

你也可以这样做

A = [1,2,3,4]
B = [A[0]]+[A[i+1]-A[i] for i in range(len(A)-1)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接