问题
这个问题实际上是今天出现的。我们正计划进行一项实验,向用户展示一系列地图。每张地图上都有31个符号。每个符号也有一个标签。我们注意到,在少数情况下,标签重叠,这使得其中一个标签不可读。
我们最终以老式的方式识别问题符号 - 通过逐个查看每个地图并写下我们发现的任何问题符号的ID - 但我认为这个问题可以很容易地解决算法。花了大约一个小时来目测检查所有地图(使用一个公认的笨重的实验数据收集工具)。
我很好奇这个网站上的人们能够多快解决这个问题,而且我也对你提出的算法感兴趣。 (注意:这不是一个家庭作业问题,虽然我认为它会带来有趣的家庭作业或面试问题。)
规格
符号坐标存储在电子表格表(假设制表符分隔的文本文件)中,其中包含以下列:
MapId
(范围:1 - 24)SymbolId
(范围:1 - 744(24张地图x 31个符号/地图= 744个总符号)XCoordinate
(范围:0 - 1024)YCoordinate
(范围:0 - 768)假设所有四列都包含integers
。
目标
你能以多快的速度提出一种算法(你选择的语言):
SymbolId
违反了哪些您的答案应包含
我对算法的运行速度或内存使用效率不感兴趣。我正在寻找一个快速,肮脏但准确可靠的解决方案。
答案 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
等动态数组矩阵来表示图像中的每个像素。计算每个圆圈占用的像素,并将push
其SymbolID
计算到圆圈中每个像素的数组中。完成所有圆圈后,只需查看每个像素,如果任何像素在其数组中有多个符号,您就会知道哪些SymbolID
是违规的。
所花费的时间:约5分钟
更新根据新要求,我的初始算法可以略微修改以在每行中读取,更新上面为每个符号定义的像素SymbolID
地图并移动到下一张地图。
答案 4 :(得分:0)
如果您正在寻找加快答案速度的方法,您也可以尝试“清扫和修剪”。