两组三个数字至少有两个共同的数字吗?

时间:2017-07-26 13:46:01

标签: algorithm

我只需要编写一个看起来很简单的函数,但是当我真正做到这一点时,它的结果比我想象的还要粗糙。这真的让我烦恼,我觉得这是一个更好的解决方案,但是我的大脑正在疯狂地想着它,因此我转向你们好人。

基本上,我有两个三角形,我想知道它们是否有共同的边缘。三角形由它们的顶点索引(即它们的顶点只是包含实际坐标的数组的索引),所以它归结为查找两组三个数字是否有两个共同的数字。即三角形(1,2,3)和(3,1,5)共享一条边,即(1,3)边。但是,三角形(1,2,3)和(1,5,6)不共享边(只有顶点),(1,2,3)和(4,5,6)也不共享。

你怎么写这个"两个共同功能的数字"?您可以假设每个集合中的所有值都是不同的(即(1,1,2)不会是输入)并且您还可以假设两个集合彼此不相等(即I' m不打算比较(1,2,3)和(1,3,2),因为这两个是相同的三角形)。但是,没有关于订单的假设,它们没有排序或类似的东西。

这基本上就是我提出的(假设这些集是(x0,x1,x2)和(y0,y1,y2)):

// If the x0 is equal to any of (y0, y1, y2), make sure at least one of (x1, x2)
// is equal to one of the y numbers
if (x0 == y0) {
    return x1 == y1 || x1 == y2 || x2 == y1 || x2 == y2;
} else if (x0 == y1) {
    return x1 == y0 || x1 == y2 || x2 == y0 || x2 == y2;
} else if (x0 == y2) {
    return x1 == y0 || x1 == y1 || x2 == y0 || x2 == y1;
} else {

    // if x0 is not equal to any of (y0, y1, y2), then x1 and x2 both have
    // to be equal to two of the y numbers. 
    return (x1 == y0 && x2 == y1) 
        || (x1 == y0 && x2 == y2)
        || (x1 == y1 && x2 == y0)
        || (x1 == y1 && x2 == y2)
        || (x1 == y2 && x2 == y0)
        || (x1 == y2 && x2 == y1);
}

但对我来说感觉太棒了!这么多分支和如此长的布尔语句!我觉得我错过了一个明显的简单解决方案,而且它让我疯狂。

此外,这发生在性能非常敏感的应用程序的内部循环中,因此性能(分支,算术等)很重要。

顺便说一句,我写的代码是C#,但问题或多或少是任何命令式语言都是一样的。

编辑:

我将快速基准(此处为code)与迄今为止的建议放在一起。以下是结果(以一百万个随机三角形对运行):

Original method result:         7035, time elapsed in ms:   8.6252
qwertyman method result:        7035, time elapsed in ms:   8.2537
midjji method result:           7035, time elapsed in ms:   8.7984
Single HashSet method result:   7035, time elapsed in ms:   184.4984
Many HashSets method result:    7035, time elapsed in ms:   190.5785

这些数字在运行时保持相当一致,@ qwertyman的方法总是比原始版本或@ midjii快一点。它还具有成为最干净最好的优点,所以我会选择那个。

我真的有点惊讶于"很多HashSets"如此接近" Single HashSet",我原本以为构建一百万个HashSets会有比16毫秒更大的开销(尽管这显然不能计算垃圾收集器上增加的压力),虽然他们显然远远落后于其他方法。

感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

您可以这样做:

int n = 0;

// check if x0 is among the values in the second set
if (x0 == y0 || x0 == y1 || x0 == y2) {
    n++;
}

// check if x1 is among the values in the second set
if (x1 == y0 || x1 == y1 || x1 == y2) {
    n++;
}

// check if x2 is among the values in the second set
if (x2 == y0 || x2 == y1 || x2 == y2) {
    n++;
}

return n >= 2;

这取决于(正如你所提到的)每个集合中的数字是不同的,从而产生更简单的逻辑。

如果你使用的是C,你可以写得更短:

int n = 0;

n += x0 == y0 || x0 == y1 || x0 == y2;
n += x1 == y0 || x1 == y1 || x1 == y2;
n += x2 == y0 || x2 == y1 || x2 == y2;

return n >= 2;

答案 1 :(得分:1)

我会用:

...
{
  //ti= xi in y 
  bool t0= (x0==y0) ||(x0==y1)|| (x0==y2);
  bool t1= (x1==y0) ||(x1==y1)|| (x1==y2);
  bool t2= (x2==y0) ||(x2==y1)|| (x2==y2);

  return (t0 && t1) || (t0 && t2) || (t1 && t2);
}

主要是因为我认为它更容易阅读。

在性能方面,很可能通过正确的设置它应该一样快。编译器非常适合优化自封闭,无副作用,逻辑语句并对bool使用惰性求值(假设没有对==做任何愚蠢的事情)。