【问题标题】:Update certain elements in a list更新列表中的某些元素
【发布时间】:2016-02-23 12:19:52
【问题描述】:

我想创建一个包含初始点x = [x0 x1] 和扰动点y1 = [x0+h x1]y2 = [x0-h x1]y3 = [x0 x1+h] 等的列表。我已经尝试过:

xs = [x]*5;
for i in range(len(x)):
    if isinstance(h, float):
        xs[2*i+1][i] -= h;
        xs[2*i+2][i] += h;

但这似乎会在循环运行时来回更改元素。

【问题讨论】:

  • [x]*5 创建一个列表,其中所有项目都是对同一列表的引用。将其更改为 [x[:] for i in range(5)] 以创建每个列表的副本
  • 你的问题对我来说没有意义。您首先说您有一个包含单个元素的列表,我猜([x0 x1] 是否意味着 [(x0, x1)]?),然后您提出的代码尝试引用嵌套列表?
  • 您是否需要一个平面中点的函数(即具有 2 个坐标),正如您的解释所暗示的那样;或具有任意数量坐标的点,正如您的“示例”代码(有很多错误)似乎暗示的那样?
  • @RNar,感谢您的解释。它可以是任意的。 LeartS 的答案解决了我的问题,尽管我知道它充满了错误(我是 python 新手,所以我没有意识到制作这些副本只是引用了同一个列表)。
  • 这是 python 初学者(和非初学者)的常见问题。

标签: python


【解决方案1】:

如果我从您的解释和代码中正确解释了您想要做的事情:

def perturbate(x, h):
    """
    Given an initial point x = [x0, x1, ..., xn]
    Returns a list of points containing the original point and,
    for every coordinate, "perturbations" of that point created
    by adding and subtracting 'h' to the coordinate.
    """
    # start with the original point plus
    # 2 copies of it for every coordinate
    points = [x[:] for i in range(len(x)*2 + 1)]

    for coordinate_index in range(len(x)):
        points[coordinate_index*2 + 1][coordinate_index] -= h
        points[coordinate_index*2 + 2][coordinate_index] += h
    return points

这给出了:

>>> perturbate([1.0,2.0], 1.0)
[[1.0, 2.0], [0.0, 2.0], [2.0, 2.0], [1.0, 1.0], [1.0, 3.0]]

所以基本上你的代码几乎是正确的(但不是很清楚,通用命名的变量没有帮助),除了“结果”初始化:当你做[x] * 5时,其中x 是一个可变对象,您正在创建一个包含 5 次相同对象的列表。如果您更改该对象的属性,它会在所有索引中更改,因为它们引用同一个对象!

>>> a = [[0,0]] * 5
>>> a
[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
>>> a[0][0] = 1
>>> a
[[1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
>>>

此外,硬编码的5 假定为二维点。但是,如果我们假设二维点,那么为什么要使用点的 len 而不是仅仅使用更简单、更清晰的“硬编码”实现呢?

【讨论】:

  • 我给出了一个更通用代码的简单示例,这就是我有 len(x) 的原因,但它可能只是 2,你是对的。感谢您的快速回复,这非常有帮助!
【解决方案2】:

我认为,对此的公认答案是不必要的复杂,这使得推理变得困难,因此更难以调试和维护。它也更复杂,因为它不必要地预先填充了一个列表,然后必须对其进行迭代。

像上面这样稍微不那么复杂的一般解决方案如下:

def perturbate(x, h):
    perturbations = [x]  # initialize with original point first
    for i, _ in enumerate(x):   # no range(len())
        shifted_down = x[:]
        shifted_down[i] -= h
        perturbations.append(shifted_down)
        shifted_up = x[:]
        shifted_up[i] += h
        perturbations.append(shifted_up)
    return perturbations

除此之外,如果您在 2D 中工作,这个支持最多 n 维点的通用解决方案的可读性远不如仅仅拼写出来:

def perturbate(point, h):
    x, y = point
    return [[x, y], [x-h, y], [x+h, y], [x, y-h], [x, y+h]]

如果这就是你所需要的,这个两行函数比上面的任何一个怪物都更具可读性和可理解性。

记住:

【讨论】:

  • 首先,我的代码是基于他的代码,因为我想显示错误在他的代码中的位置。也因为在编辑和附加 cmets 之前问题不是很清楚,所以人们不得不从他的代码中猜测他想做什么。其次,“我的”(实际上是他的)解决方案不是 O(N^2),而是 O(n)。第三,我们都同意,对于 2D 点,硬编码解决方案比一般解决方案更具可读性和简单性,正如我在回答中所说的那样。
  • 你说OP的代码是O(N^2),这是错误的。你说不使用xrange(len),但你使用了一个只考虑索引(stackoverflow.com/questions/11901081/…)的enumerate。而且,最重要的是,您没有解决 OP 的问题,即他的代码中唯一客观错误的地方。相反,您基本上回答了代码审查。我的回答也被否决了,但它解释了(通过示例)OP 代码中的错误,提供了一个工作代码(基于 OP 的代码),并且它在 1 天前提供了相同的有用指针。去图吧。
  • 接受的答案更喜欢 enumerate 出于一般原因在此处不适用,按赞数排名最高的答案更喜欢 xrange 因为更明确。关于复杂度,如果你考虑复制一个点 O(N),那么这两种算法都是 O(N^2)。您的算法也在循环内执行x[:]!事实上,我用 10,100,1000,10000 坐标测试了两者并且都表现相同(非常糟糕!)无论如何,你是对的,你的答案,虽然我仍然觉得它并没有真正“隐式显示错误”,但没有值得反对。如果你编辑它,我可以改变投票。
  • @LeartS 关于复杂性,您是对的。我已经更改了答案,以相应地删除有关时间复杂度的部分。在继续断言我第一次是对的之前,我应该重新审视代码。除此之外,除了创建一个非常长的评论链之外,我认为继续这个论点没有任何意义。我不会删除我的答案,我也不担心它的分数。不管我愿意与否,我也无法改变我对你的投票,所以就这样吧。
  • 哦,还有一件事:为了保护我对 enumerate 的使用,这不是一个随意的选择。我专门选择它是为了避免在已经拥有[x[:] for i in range(len(x)*2 + 1)] 之后不得不做points[coordinate_index*2 + 1][coordinate_index] -= h 之类的事情。就我个人而言,我认为当您没有这些复杂的数学公式来索引每次迭代都会改变时,更容易判断发生了什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-23
  • 2018-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多