多个双打的词典排序

时间:2010-07-14 13:58:18

标签: algorithm numeric

考虑一类双打

class path_cost {
   double length;
   double time;
};

如果我想按字典顺序排序path_costs列表,我有一个问题。请继续阅读:)

如果我使用完全相等的等同性测试

bool operator<(const path_cost& rhs) const {
   if (length == rhs.length) return time < rhs.time;
   return length < rhs.length;
}

结果顺序可能是错误的,因为一个小的偏差(例如由于计算长度的数字不准确)可能导致长度测试失败,例如

{ 231.00000000000001, 40 } < { 231.00000000000002, 10 }

错误地持有。

如果我选择使用这样的容差

bool operator<(const path_cost& rhs) const {
   if (std::fabs(length-rhs.length)<1-e6)) return time < rhs.time;
   return length < rhs.length;
}

然后排序算法可能会失败,因为&lt; -operator不再是传递的(即,如果&lt; b和b&lt; c然后a&lt; c可能不成立)

有什么想法吗?解决方案?我已经考虑过对实际行进行分区,因此每个分区中的数字被认为是相等的,但这仍然会留下太多的情况,其中相等测试失败但不应该。

(James Curran更新,希望能解释这个问题): 鉴于数字:

  • A = {231.0000001200,10}
  • B = {231.0000000500,40}
  • C = {231.0000000100,60}

    • A.Length&amp; B.Length相差7-e7,所以我们使用时间,A&lt;乙
    • B.Length&amp; C.Length相差4-e7,所以我们使用时间,而B <0。下进行。
    • A.Length&amp; C.Length相差1.1-e6,所以我们使用长度,并且A> 1。下进行。

(Esben Mose Hansen更新) 这不仅仅是理论上的。当给定非传递排序运算符时,标准排序算法会崩溃或更糟。这正是我一直在争论的(男孩调试很有趣;))

5 个答案:

答案 0 :(得分:4)

你真的只想要一个比较功能吗?

为什么不首先按长度排序,然后将这些对分组为您认为相同的长度,然后按时间在每个组内排序?

按长度排序后,您可以应用您需要的任何启发式,以确定长度的“相等性”,以进行分组。

答案 1 :(得分:1)

我认为你无法做你想做的事。从本质上讲,你似乎在说某些情况下你想忽略a> b并假装a = b的事实。我很确定你可以构造一个证据,说明当差值小于某个值时a和b是否等价,那么a和b对于a和b的所有值都是等价的。有点像:

对于C的公差和两个数字A和B,其中不失一般性A> B然后存在D(n) = B+n*(C/10),其中0<=n<=(10*(A-B))/(C)使得D(n)在D(n-1)和D(n + 1)的容差范围内,因此等价于它们。 D(0)也是B和D((10 *(A-B))/(C))= A所以A和B可以说是等价的。

我认为解决该问题的唯一方法是使用分区方法。像是乘以10 ^ 6然后转换为int shoudl分区的东西很好但是意味着如果你有1.00001 * 10 ^ -6和0.999999 * 10 ^ -6那么它们将出现在不同的分区中,这可能是不可取的

然后问题就是查看你的数据,找出如何最好地对它进行分区,因为我对你的数据一无所知。 :)

P.S。在给定算法时,或者当它们遇到特定的无法解决的情况时算法是否实际崩溃?

答案 2 :(得分:1)

我可以想到两个解决方案。

当比较不敏感时,您可以仔细选择不会失败的排序算法。例如,快速排序不应该失败,至少如果你自己实现它。 (如果您担心快速排序的最坏情况,您可以先将列表随机化,然后对其进行排序。)

或者您可以扩展您的容差补丁,使其成为等价关系,并恢复传递性。标准union-find algorithms可以完成与等价关系的任何关系。应用union-find后,您可以使用一致值(例如平均值)替换每个等价类中的长度,然后执行您想要执行的排序。医生浮点数以防止虚假重新排序感觉有点奇怪,但它应该有效。


实际上,莫伦提出了一个很好的观点。您可以先按长度排序,然后将容差范围内的邻居链接在一起,然后在第二个键的每个组中进行子排序,而不是联合和查找。这与我的第二个建议有相同的结果,但这是一个更简单的实现。

答案 3 :(得分:0)

我不熟悉您的应用程序,但我愿意打赌,图表中各点之间距离的差异比浮点数上的舍入误差大许多个数量级。因此,如果两个条目仅因舍入误差而不同,则它们基本相同,并且它们在列表中出现的顺序没有区别。从常识的角度来看,我认为没有理由担心。

答案 4 :(得分:0)

普通的double你永远不会获得100%的精确度。您说您担心使用容差会影响程序的正确性。你有没有测试过这个?您的计划实际需要什么级别的精确度?

在大多数常见应用程序中,我发现容差1e-9就足够了。当然这一切都取决于您的应用。您可以估算所需的准确度,只需将公差设置为可接受的值即可。

即使失败,也意味着double根本不适合您的目的。这种情况极不可能发生,但如果您需要非常高精度的计算,则会出现这种情况在这种情况下,您必须使用任意精度包(例如Java中的BigDecimal或类似GMP的C)。同样,只有在没有其他方法时才选择此选项。