人工检查员与程序员(是:检测是否有任何圆圈重叠的算法)

时间:2009-10-20 21:41:02

标签: algorithm language-agnostic

问题

这个问题实际上是今天出现的。我们正计划进行一项实验,向用户展示一系列地图。每张地图上都有31个符号。每个符号也有一个标签。我们注意到,在少数情况下,标签重叠,这使得其中一个标签不可读。

我们最终以老式的方式识别问题符号 - 通过逐个查看每个地图并写下我们发现的任何问题符号的ID - 但我认为这个问题可以很容易地解决算法。花了大约一个小时来目测检查所有地图(使用一个公认的笨重的实验数据收集工具)。

我很好奇这个网站上的人们能够多快解决这个问题,而且我也对你提出的算法感兴趣。 (注意:这不是一个家庭作业问题,虽然我认为它会带来有趣的家庭作业或面试问题。)

规格

  • 地图:24
  • 地图大小(像素):1024 X 768
  • 每张地图的符号(圆圈):31
  • 符号直径(像素):60

符号坐标存储在电子表格表(假设制表符分隔的文本文件)中,其中包含以下列:

  • MapId(范围:1 - 24)
  • SymbolId(范围:1 - 744(24张地图x 31个符号/地图= 744个总符号)
  • XCoordinate(范围:0 - 1024)
  • YCoordinate(范围:0 - 768)

假设所有四列都包含integers

目标

你能以多快的速度提出一种算法(你选择的语言):

  1. 读取包含输入数据的制表符分隔文本文件。
  2. 为每个地图确定是否有任何符号重叠。
  3. 如果任何符号重叠,则报告SymbolId违反了哪些
  4. 您的答案应包含

    1. 您的算法,必须满足所有三个目标(上图)。
    2. 你花了多长时间思考并写下它(你的荣幸)。注意:您不需要花时间阅读和理解问题,但是一旦开始考虑解决方案的想法,就开始计时。
    3. 我对算法的运行速度或内存使用效率不感兴趣。我正在寻找一个快速,肮脏但准确可靠的解决方案。

5 个答案:

答案 0 :(得分:8)

对于每张地图:

If distance(A.center, B.center) < (A.radius+B.radius)
   the circles overlap.

在你的情况下,似乎每个圆都具有相同的半径,但我允许每个圆具有不同半径的可能性,以防万一。

至于想多长时间,确切地说有点难 - 比用完整描述加载页面花费的时间少。然后我不得不做一些阅读以确认基本问题是它似乎来自标题......

编辑:如果你有很多圈子,可能值得做一些排序以消除不必要的重叠测试,但每张地图只有~30圈,这可能不值得 - 你需要一台真正古老的计算机为此需要一整秒。

抱歉,不得不离开去带孩子去滚轴曲棍球比赛。无论如何,虽然我还没有测试过,但这就是我在10-12分钟内为C ++程序制作的东西(我不得不在那里打电话,所以我没有确切的时间):

#include <vector>
#include <math.h>
#include <iostream>

struct point { 
    int id, x, y;
};

std::istream &operator>>(std::istream &is, point &p) { 
    return is >> p.id >> p.x >> p.y;
}

typedef std::vector<point> map;

double dist(point const &a, point const &b) { 
    double x=a.x-b.x;
    double y=a.y-b.y;
    return sqrt(x*x+y*y);
}

int main() { 
    std::vector<map> maps(24);
    int mapID;
    while (std::cin>> mapID) {
        point temp;
        std::cin >> temp;
        maps[mapID].push_back(temp);
    }

    for (int i=0; i<maps.size(); i++)
        for (int j=0; j<maps[j].size(); j++) 
            for (int k=j; k<maps[j].size(); k++) 
                if (dist(maps[i][j], maps[i][k]) < 60)
                    std::cout 
                        << "map " << i << "\t" 
                        << maps[i][j].id 
                        << " overlaps with " 
                        << maps[i][k].id << "\n";
    return 0;
}

我实际上没有对此进行过测试,因此可能需要5到10分钟(或该命令中的某些内容)以确保其正常工作,但我不会期望任何更长的时间。无论如何,我希望一个人能在一小时内完成。如果你已经习惯了像AWK或Perl这样的东西,我希望它能更快地完成 - 不过,说实话,这很简单,它主要归结为减少打字......

答案 1 :(得分:4)

我的第一个想法是Jerry已经发布的简单O(N 2 )算法,只有一个小例外:距离 2 更容易计算,因为它没有需要一个平方根,所以将其与(半径之和) 2 进行比较。

我的第二个想法是使用包含所有积分的quadtree来增强该解决方案,这有助于减少需要进行的比较次数。

写这个答案需要花费更长的时间来考虑这些解决方案中的任何一个 - 可能是10秒。这种问题在计算机图形学中非常普遍,我只是回想起我以前见过的东西。


在Haskell中快速编写解决方案1的实现(花了不到一分钟):

import Control.Monad
import Data.List
radius = 30
main = do
    points <- fmap (map (map read . words) . lines) getContents
    mapM_ print $ overlapping (points :: [[Int]])
overlapping list = do
    [m1, s1, x1, y1]:rest <- tails list
    [m2, s2, x2, y2] <- rest
    guard $ m1 == m2 && (x2-x1)^2 + (y2-y1)^2 < 2*radius^2
    return ((m1, s1), (m2, s2))

如果指定的问题,那就是小于3e5的比较;编写四叉树插入/搜索可能不值得。即使比较需要3000个时钟周期(显然不会),它仍然会在一秒钟内完成。

答案 2 :(得分:2)

假设您的电子表格软件中有SQL权限,我会通过查询运行它:

select 
   s1.symbolId,s2.symbolId
from 
   symbols s1 
   join 
   symbols s2 
where 
   s1.mapid=s2.mapid 
   and 
   s1.symbolid<s2.symbolid 
   and 
   ((s1.xcoordinate-s2.xcoordinate)*
   (s1.xcoordinate-s2.xcoordinate)+
   (s1.ycoordinate-s2.ycoordinate)*
   (s1.ycoordinate-s2.ycoordinate))
   <(r+r)*(r+r)

(约5分钟可能有一些错误)

答案 3 :(得分:1)

这比 O(n ^ 2)更快,其中n =符号数。使用STL vector等动态数组矩阵来表示图像中的每个像素。计算每个圆圈占用的像素,并将pushSymbolID计算到圆圈中每个像素的数组中。完成所有圆圈后,只需查看每个像素,如果任何像素在其数组中有多个符号,您就会知道哪些SymbolID是违规的。

所花费的时间:约5分钟

更新根据新要求,我的初始算法可以略微修改以在每行中读取,更新上面为每个符号定义的像素SymbolID地图并移动到下一张地图。

答案 4 :(得分:0)

如果您正在寻找加快答案速度的方法,您也可以尝试“清扫和修剪”。