考虑一个矩形帆布,包含随机大小和位置的矩形。要在这些矩形之间导航,用户可以使用四个箭头:向上,向下,向左,向右。
您是否熟悉可以产生相当简单的用户体验的任何导航算法?
我遇到了一些解决方案,但它们似乎都不合适。我知道没有解决方案是“理想的”。但是,我正在寻找的算法类型是用于仅使用箭头键在桌面上的图标之间导航的类型。
答案 0 :(得分:4)
[编辑21/5/2013:正如Gene在评论中指出的那样,我的加权方案实际上并不保证每个矩形都可以从其他每个矩形到达 - 只有每个矩形都会被连接到每个方向都有一些其他矩形。]
执行此操作的一种好方法是使用maximum weighted bipartite matching。
我们想要做的是构建一个定义函数f(r,d)的表,如果它们当前处于矩形r和方向d(向上,向下,向左或向右),则返回用户将移动到的矩形。对)。我们希望这个函数有一些不错的属性,例如:
对于每个矩形,在图形中创建4个顶点:在该矩形处可以按下的每个可能键一个顶点。对于特定的矩形r,将它们称为r U ,r D ,r L 和r R 。对于每对矩形r和s,创建4条边:
此图有2个连通分量:一个包含所有U和D顶点,另一个包含所有L和R顶点。每个组分都是二分的,因为例如没有U顶点连接到另一个U顶点。事实上,我们可以分别在每个组件上运行最大加权二分匹配,尽管在分组之后,例如,具有L个顶点的U个顶点和具有R个顶点的D个顶点,只讨论在整个图形上运行它一次更容易。 / p>
根据通过该对键连接的那对矩形的感知程度,为每个边分配一个非负权重。您可以自由选择此评分函数的表单,但它应该是:
此功能试图满足顶部的要求3.
[编辑#2 24/5/2013:在下面添加了一个示例函数。]
这是满足这些属性的示例函数的C-ish伪代码。它取2个矩形的中心点和矩形1的方向(矩形2的方向始终与此方向相反):
const double MAXDISTSQUARED = /* The maximum possible squared distance */;
const double Z = /* A +ve number. Z > 1 => distance more important than angle */
// Return a weight in the range [0, 1], with higher indicating a better fit.
double getWeight(enum direction d, int x1, int y1, int x2, int y2) {
if (d == LEFT && x1 < x2 ||
d == RIGHT && x1 > x2 ||
d == UP && y1 < y2 ||
d == DOWN && y1 > y2) return 0;
// Don't need to take sqrt(); in fact it's probably better not to
double distSquared = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
double angle = abs(atan2(x1 - x2, y1 - y2)); // 0 => horiz; PI/2 => vert
if (d == UP || d == DOWN) angle = PI / 2 - angle;
return 1 - pow(distSquared / MAXDISTSQUARED, Z) * (2 * angle / PI);
}
现在运行最大加权二分匹配。这将尝试找到具有最高总权重的边缘集合,使得每个顶点(或至少尽可能多的)与所选边缘相邻,但没有顶点与多于一个边缘相邻。 (如果我们允许顶点与多个边相邻,则意味着在该矩形处按下该键会将您带到多个目标矩形,这是没有意义的。)匹配对应于按键的双向对,以便按下例如向上然后向下将回到原来的位置,自动满足最高要求2.
到目前为止,这种方法不能自动满足的唯一要求是重要的一个,即数字1:它不一定能保证每个矩形都可以到达。如果我们只使用&#34; raw&#34;作为边缘权重的质量分数,对于某些配置,这实际上可能发生,例如,如果屏幕的4个角落中都有一个矩形,中间有一个矩形,则可能无法访问中心的一个矩形。
[编辑2013年3月21日:正如Gene所说,我在新的加权方案下对我的权利1的要求是满意的,我认为这是错误的。在许多情况下,每个矩形都是可达的,但一般来说,你需要解决NP难的哈密顿循环问题来保证这一点。我会把解释留在里面,因为它会让我们有一些方法。在任何情况下,只要检测到子循环,就可以通过向上调整连接组件之间的权重来进行攻击。]
为了保证匹配算法总是返回一个匹配,其中每个矩形都是可达的,我们需要调整边缘权重,这样匹配得分就不可能高于匹配更多边缘。这可以通过将评分函数缩放到0到1之间,并将个矩形,n添加到每个边的权重来实现。这是因为完全匹配然后得分至少为4n ^ 2(即使质量得分为0,边缘本身的权重为n且其中有4n),而任何与较少边缘匹配的得分最多4(n-1)(n + 1)= 4n ^ 2 - 4,严格地说更少。
答案 1 :(得分:2)
对于一个拿锤子的人来说,一切都像钉子一样,这是不言而喻的。最短路径算法在这里是一个明显的工具,因为最短距离似乎是直观的。
然而,我们正在设计一个UI,其中逻辑距离比物理距离重要得多。
让我们尝试不同的思考。
一个限制是反复击中向上(向右,向下或向左)箭头应该最终循环遍历所有矩形。否则一些无法接触的“孤儿”很可能。使用基于物理(2d)距离的算法来实现这一点是困难的,因为2d中的最近项可能在对应于所使用的箭头对的1d投影中处于错误的方向。即点击向上箭头可以轻松选择当前下方的框。哎哟。
所以让我们采用一个非常简单的解决方案。只需对其质心的x坐标上的所有矩形进行排序。按下右箭头和左箭头按顺序循环显示矩形:右边是下一个最高x,左边是下一个最低x。包裹在屏幕边缘。
对y坐标也一样。按此顺序使用上下循环。
成功的关键(双关语)是在循环时向屏幕添加动态信息,向用户显示正在发生的事情的逻辑。这是一个提案。其他人是可能的。
在第一个垂直(向上或向下)键上,矩形上会出现一个苍白的半透明叠加层。它们以浅红色或蓝色阴影,图案与质心的y坐标交替。在整个窗口中还存在匹配颜色的水平哈希标记。两种颜色的唯一原因是提供线条和矩形之间对应的视觉指示。当前选择的矩形是非半透明的,并且散列标记比所有其他矩形更亮。当您继续按下向上或向下键时,突出显示的框会按照上述的质心y顺序更改。如果没有箭头键敲击半秒左右,叠加层就会消失。
如果击中水平键,则会出现非常相似的叠加,只有垂直散列标记和x顺序。
作为用户,我真的很喜欢这个方案。但是YMMV。
实现这一目标所需的算法和数据结构是显而易见的,微不足道的,并且可以很好地扩展。我们将努力使覆盖层看起来很好。
NB现在我已经完成了所有的绘图,我意识到在每个方框的质心处放置一个正确着色的点以显示哪条线与它相交是个好主意。一些说明性图表如下。
裸盒
正在进行向上或向下箭头的选择
正在进行的左箭头或右箭头选择
答案 2 :(得分:1)
如何构建运动图如下:
然后存储图表并仅使用它进行导航。您不希望在会话中间更改路线。
答案 3 :(得分:0)
此问题可以建模为graph
问题,algorithm of navigation
可以用作shortest path routing
。
以下是建模。
每个矩形都是图中的顶点。从每个顶点(也称为矩形),您有四个选项 - 向上,向下,向左,向右。因此,您可以达到四个不同的矩形,即此顶点将有四个邻居,您可以将这些边添加到图形中。
我不确定这是否是问题的一部分 - “可以使用特定动作(例如向上)从矩形到达多个矩形”。如果没有,上面的建模就足够了。如果是,则将所有此类顶点添加为此顶点的邻居。因此,您可能不会得到一个4正则图。否则,您将把问题建模为4个常规图。
现在,问题是how do you define your "navigation" algorithm
。如果您不想区分您的操作,即上,下,左和右都相等,那么您可以为所有边添加1的权重。
如果你决定给某个特定动作优先于其他动作,比如up
比其他动作更好,那么你可以给上移的边缘加权为1,其余边为2。想法是通过分配不同的权重,你可以区分你将要旅行的边缘。
如果您确定所有up
边不相等,即A和B之间的向上距离短于C和D之间的向上距离,那么您可以相应地在图中为边指定权重施工过程。
这是路由
现在如何找到路线 - 您可以使用dijkstra's algorithm
找到给定顶点对之间的最短路径。如果您对多个最短路径感兴趣,可以使用k-shortest path
算法查找一对节点之间的k
个最短路径,然后选择最佳路径。
请注意,您最终得到的图表不一定是有向图。如果您更喜欢有向图,则可以在构造边时为边指定方向。否则你应该很好地使用无向图,因为所有你关心的是使用边到达另一个顶点。此外,如果可以使用矩形A
中的up
来访问矩形B
,则可以使用矩形rectangle B
中的down
来访问A
。因此,如果由于其他原因不需要它们,方向确实无关紧要。如果你不喜欢我刚才做的假设,那么你需要构建有向图。
希望这有帮助。