Algorithm 两组三个数字是否至少有两个相同的数字?

Algorithm 两组三个数字是否至少有两个相同的数字?,algorithm,Algorithm,我只需要写一个看似简单的函数,但当我真的这么做的时候,结果比我想象的要糟糕得多。这真的很困扰我,我觉得有更好的解决办法,但我的大脑正在疯狂地思考它,所以我转向你们这些好朋友 基本上,我有两个三角形,我想知道它们是否共用一条边。三角形由其顶点索引(即,它们的顶点只是包含实际坐标的数组的索引),因此归结为查找两组三个数字是否有两个相同的数字。即三角形(1,2,3)和(3,1,5)共享一条边,即(1,3)边。但是,三角形(1,2,3)和(1,5,6)不共享边(仅共享顶点),也不共享(1,2,3)和(4

我只需要写一个看似简单的函数,但当我真的这么做的时候,结果比我想象的要糟糕得多。这真的很困扰我,我觉得有更好的解决办法,但我的大脑正在疯狂地思考它,所以我转向你们这些好朋友

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

你会怎么写这个“两个数在同一个函数中”?您可以假设每个集合内的所有值都是不同的(即,(1,1,2)不是输入),也可以假设两个集合不相等(即,我不会比较(1,2,3)和(1,3,2),因为这两个集合是相同的三角形)。但是,不能对顺序进行任何假设,它们没有排序或类似的内容

这基本上就是我想到的(假设集合是(x0,x1,x2)和(y0,y1,y2)):

但我觉得很血腥!这么多分支和这么长的布尔语句!我觉得我错过了一个显而易见的简单解决方案,这让我发疯

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

顺便说一句,我正在写的代码是C#,但在几乎所有命令式语言中,问题都是一样的

编辑:

我总结了一个快速的基准(这是到目前为止的建议)。以下是结果(在一百万对随机三角形上运行):

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

事实上,我有点惊讶“多个哈希集”如此接近“单个哈希集”,我本以为构建一百万个哈希集的开销会比大约16毫秒大(尽管这显然不包括垃圾收集器上增加的压力),虽然它们显然都远远落后于其他方法


谢谢你的帮助

您可以这样做:

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;
我将使用:

...
{
  //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使用惰性求值方面非常出色(假设对==)没有做任何愚蠢的事情)

您使用的是什么语言?很大程度上取决于您如何表示集合。还有,它们是真的吗?(保证元素是唯一的)根据您使用的语言,只需将数字存储在实际集合中,并使用库函数计算交叉点的大小。我使用的是C#,但您回答的语言并不重要,任何语言都可以。这些集合只是名为(x0,x1,x2)和(y0,y1,y2)的变量。不,我不可能在堆上创建两个集合数据结构,然后在它们上运行交集方法并检查长度,这是一个疯狂的解决方案。奥斯卡:如果高性能是一个关键因素,你应该在问题中提到。由于您对当前方法的抱怨似乎与速度无关,但与它看起来不太好有关-这更有可能鼓励人们给出可读而非高性能的算法。在您的第二个C示例中,将| |转换为加法是否会更快,以避免因短路而导致分支?顺便说一句,我意识到我们现在谈论的是微小的微优化,但我真的需要代码尽可能快。另外,我很好奇:)这也让我好奇——我做了一个基准测试,首先在[0300]中设置了数字,时间非常相似,“| |”变体有一点优势。然而,短路很少发生。之后,我还用[0,15]中的x和y值做了一个实验,“+”变量更快(可能是因为这里短路发生得更频繁,更难预测分支)。但问题很有趣,因为这不是一个可以由编译器自动进行的优化(只需将| |转换为+)--一般来说,结果是不一样的,但在本例中是一样的,因为我们知道同一集中的输入数字是不同的。您是如何编写快捷方式变量的?如果你用A,2nd B和3rd C替换第一个If中的内容,应该是这样的:If(A)return B | C;否则,如果(B)返回C;否则返回false;
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;
...
{
  //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);
}