【问题标题】:Find 4 points that form a square from 2D list从二维列表中找到形成正方形的 4 个点
【发布时间】:2020-03-24 13:58:39
【问题描述】:

我在下面有一个二维列表:

a = [[3, 10], [7, 11], [7, 12], [8, 11], [8, 12], [12, 8], [12, 9], [13, 8], [13, 9], [14, 6], [14, 7], [15, 8], [17, 6], [18, 6]]

有4个点可以组成一个正方形:

[7, 11], [7, 12], [8, 11], [8, 12]

或者这个:

[12, 8], [12, 9], [13, 8], [13, 9]

这是我的代码:

def find_square(a):
    i = 0
    result = []
    while(i < len(a)):
        if a[i][0] == a[i + 1][0]:
            if a[i][1] == a[i + 2][1]:
                if a[i + 2][0] == a[i + 3][0]:
                    if a[i + 1][1] == a[i + 3][1]:
                        result.append([a[i][0] + 1, a[i][1] + 1])
                    i += 4
                else:
                    i += 3
            else:
                i += 2
        else:
            i += 1
    return result

输出:

[8, 12], [13, 9]

此代码将返回正方形的最后一个点(右下角)。我想检查是否存在 4 个点可以形成一个边为 1 的正方形并返回右下角的点。实现此代码的更好方法是什么?

假设二维列表按 x 坐标升序排序。

更新:

我发现我的代码有问题的案例:

[7, 11], [7, 12], [7, 13], [8, 11], [8, 12]

[7, 13] 的点使我的代码无法检测到正方形。

【问题讨论】:

  • 所以正方形必须由连续的点组成,而不是列表中的任意四个点?
  • @MOehm 必须由连续点组成
  • 就复杂性而言,你真的不能做得更好(这是线性的,你不能做得更好。充其量也许你可以赢得一个常数因素)。你的问题是关于实现和编写好的代码吗?
  • @gdelab 是的,我的问题是如何让我的代码看起来更好
  • @gdelab 我的代码也有问题,当有case时:[7, 11], [7, 12], [7, 13], [8, 11], [8, 12]。点[7, 13] 将位于中间,我的代码可以检测到正方形

标签: python list algorithm


【解决方案1】:

嗯,也许是这样的:

def find_square(a):
    result = []
    for (bl, tl, br, tr) in zip(a, a[1:], a[2:], a[3:]):
        if bl[0] == tl[0] and br[0] == tr[0] and \
           bl[1] == br[1] and tl[1] == tr[1] and \
           br[0] - bl[0] == tl[1] - bl[1]:
           result.append(tr)
    return result

变量的名称是bl 用于左下角,tl 用于左上角,br 用于右下角,tr 用于右上角。在if 的第一行我们检查x 坐标,在第二行检查y 坐标,在第三行我们检查它是正方形,而不是矩形。

已针对新情况更新

def find_square(a):  
    d = {}
    for p1, p2 in zip(a, a[1:]):
        if p2[0] == p1[0] and p2[1] == p1[1] + 1:
            d.setdefault(p1[1], []).append(p1[0])
    result = []
    for y, xs in d.items():
        for x1, x2 in zip(xs, xs[1:]):
            if x2 == x1 + 1:
                result.append([x2, y + 1])
    return result

解释:首先我们遍历数组并寻找点,这些点在它们上方还有另一个点。如果我们找到这样一个点,那么我们在字典d 中添加一个新值。键是垂直坐标,值将包含可能的x 坐标列表。在下一个循环中,我们将遍历每个x 坐标列表,并检查列表是否包含两个连续的x 坐标。如果是这样,那么我们找到了一个正方形。

【讨论】:

  • 你的代码实现得比我好,但是我刚刚发现的案例也有同样的问题。我用你的代码运行了这个案例[7, 11], [7, 12], [7, 13], [8, 11], [8, 12],它没有返回[8, 12]
  • 哦,你编辑了你的问题。您添加了这种情况,但在您的问题中仍然有“连续点”一词。在您的示例中,有 no 个连续点构成一个正方形。所以你要么删除“连续”这个词,要么不考虑这个例子。
  • 连续点表示781112。正方形的长度为1
  • 所以它们可以在初始列表中的任何位置,但正方形大小必须为 1 ?这改变了一切
  • 它们也可以在列表中按任何顺序排列吗?
【解决方案2】:

一种不需要点是连续的,甚至不需要以任何方式对列表进行排序的解决方案:

a = [[3, 10], [7, 11], [7, 12], [7, 13], [8, 11], [8, 12], [12, 8], [12, 9], [13, 8], [13, 9], [14, 6], [14, 7], [15, 8], [17, 6], [18, 6]]

from itertools import combinations

def is_square(points, square_side=1):
    if len(set(tuple(pt) for pt in points)) != 4:  # Some points are identical
        return False
    x_coords = sorted(set(pt[0] for pt in points))
    y_coords = sorted(set(pt[1] for pt in points))
    if not (len(x_coords) == len(y_coords) == 2):  # Points are not aligned 2 by 2 on x and on y
        return False
    # We now know we have a rectangle
    if not (x_coords[1] - x_coords[0] == y_coords[1] - y_coords[0] == square_side):
        # Not a square, or not the right size
        return False
    return True

def find_square(pts_list, square_side=1):
    result = []
    for pts in combinations(pts_list, 4):
        if is_square(pts, square_side):  # Retrieve the right point
            result.append([max(pt[0] for pt in pts), max(pt[1] for pt in pts)])
    return result

print(find_square(a, 1))

然而它在Theta(N⁴) 中,而使用列表按x 排序的事实,只有整数坐标,并且正方形大小必须为1,在最坏的情况下Theta(N²) 有一个解决方案甚至平均Theta(N)(见Alex's answer)。

【讨论】:

  • 不幸的是,这个解决方案的复杂度为 O(N^4),而对于排序输入,在最坏的情况下可以在 O(N^2) 中完成,在最好的情况下可以在 O(N) 中完成。
  • 是的,你是对的(如果索引都是整数,这似乎是这种情况)。不错的解决方案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-03
  • 1970-01-01
相关资源
最近更新 更多