快乐的恩德问题


32

团圆结局的问题(实际上是一个定理)指出,

平面中一般位置上的任何五个点集都有四个点的子集,这些点形成凸四边形的顶点。

保罗·埃德斯(PaulErdős)用这个名字来命名这个问题,当时最先研究该问题的两位数学家,酯类克莱因(Ester Klein)和乔治·塞克斯(George Szekeres)订婚并随后结婚。

说明:

  • 这里的一般位置意味着没有三个点是共线的。
  • 由四个顶点形成的四边形始终被认为是不相交的,而与点的顺序无关。例如,考虑到四点[1 1][1 2][2 1][2 2]预期的四边形是正方形,没有蝴蝶结:

    在此处输入图片说明

  • 如果内角不超过180度,则不相交的四边形为凸形;如果两个对角线都位于四边形内,则等效。

挑战

给定5个具有正整数坐标的点,则输出形成凸四边形的那些点中的4个。

规则

如果有多个解决方案(即几组四个点),则可以始终选择输出其中一个或全部。

输入和输出格式像往常一样灵活(数组,列表,列表列表,带有合理分隔符的字符串等)。

代码高尔夫,最少的字节数获胜。

测试用例

  1. 输入:

    [6 8] [1 10] [6 6] [5 9] [8 10]
    

    只有一种可能的输出:

    [6 8] [1 10] [6 6] [5 9]
    
  2. 输入:

    [3 8] [7 5] [6 9] [7 8] [5 1]
    

    有五种解决方案:

    [3 8] [7 5] [6 9] [7 8]
    [3 8] [7 5] [6 9] [5 1]
    [3 8] [7 5] [7 8] [5 1]
    [3 8] [6 9] [7 8] [5 1]
    [7 5] [6 9] [7 8] [5 1]
    
  3. 输入:

    [4 8] [1 9] [9 9] [10 2] [1 6]
    

    有三种解决方案:

    [4 8] [1 9] [10 2] [1 6]
    [4 8] [9 9] [10 2] [1 6]
    [1 9] [9 9] [10 2] [1 6]
    

    为了说明这一点,下面是针对这种情况的三种解决方案:

在此处输入图片说明


14
我期待马丁给出一个带有积极情绪的答案。
El'endia Starman

1
不要把幸福的结局问题与幸福的恩德问题相混淆,后者是要找到一种方法来防止新兵发现他们正在玩的模拟是真实的
user253751'6

Answers:


24

CJam,37岁 34 32字节

{e!Wf<{2*3ew{)f.-~W%.*:-V>},!}=}

不确定是否:-V足够幸福,但是正如张K指出的那样,=}结尾处是。:)

这仅打印一种解决方案,因为删除重复项将更加昂贵。

在这里测试。

说明

这个想法很简单。我们生成所有可能的四边形(包括点的所有顺序),然​​后仅选择凸形。我们通过查看每对边缘并检查它们是否都朝相同方向旋转来测试凸度。

从点积可以很容易地获得转向感。如果您在四边形上获取三个连续的点,并从第一个点到第二个点,从第一个点到第三个点画线,然后将后者投影到前者的垂直线上,则会得到一个数字,其符号告诉您这三个点是向左还是向右。(我可能应该为此添加一个图表。)听起来“相当垂直”,但实际上只是意味着我们反转两个向量之一,并在相乘后减去分量,而不是相加。所以这是代码...

e!       e# Generate all permutations of the five input points.
Wf<      e# Discard the fifth point in each permutations, giving all
         e# possible quadrilaterals.
{        e# Select the first for which this block gives a truthy result...
  2*     e#   Double the list of points, so that it includes each cyclically
         e#   adjacent set of three points.
  3ew    e#   Get all sublists of length 3, i.e. all sets of three consecutive
         e#   points (with two duplicates).
  {      e#   Filter these sets of three points...
    )    e#     Pull off the last point.
    f.-  e#     Subtract it from the other two, giving vectors from it to
         e#     to those.
    ~    e#     Unwrap the array dumping both vectors on the stack.
    W%   e#     Reverse one of them.
    .*   e#     Element-wise multiplication.
    :-   e#     Subtract the second element from the first. This completes
         e#     the projection.
    V>   e#     Check whether it's greater than 0. This is *false* for right-
         e#     turning sets of three points.
  },     e#   If all corners are right-turning, this will result
         e#   in an empty array.
  !      e#   Logical NOT - hence, only quadrilaterals where all corners
         e#   are right-turning give something truthy.
}=

2
当然,快乐的鸭子!
Luis Mendo

1
@LuisMendo我认为最后两个字符看起来更像是一个笑脸=}
K Zhang

!}也可能被视为眨眼
Jezzamon

2
CodeGolf的Jon Skeet ..这太神奇了
Alex Carlsen

8

MATLAB,67字节

I=input('');for k=~eye(5);if nnz(convhull(I(k,:)))>4;I(k,:),end;end

输入采用2D矩阵的形式,其中列分别为X和Y:

[6 8; 1 10; 6 6; 5 9; 8 10]
[3 8; 7 5; 6 9; 7 8; 5 1]
[4 8; 1 9; 9 9; 10 2; 1 6]

创建凸四边形的所有4个点的集合均以相同格式显示。

这是一个稍作修改即可使用Octave 的演示

说明

此解决方案采用输入的4点的所有子集(顺序无关紧要)。为此,我们创建身份矩阵并将其取反:~eye(5)。我们遍历此矩阵的各列,k(循环索引)是一个逻辑数组,它指定要考虑的4个点中的哪一个。然后,我们使用它从输入(I(k,:))中获取这4个XY点。

然后,我们计算这4个点的凸包(convhull)。输出convhull是输入的索引,该索引与构成凸包的点相对应(复制第一个索引以关闭包壳)。

对于凸四边形,所有四个点将是相同点(nnz(convhull(points)) > 4)的凸包的一部分。如果我们检测到这种情况,则将显示用于此特定迭代的点。


4

使用Javascript(ES6),306个 293 283字节

c=(v,w,x)=>(w[0]-v[0])*(w[1]-x[1])-(w[1]-v[1])*(w[0]-x[0])>0?1:0
i=(v,w,x,y)=>(c(v,w,x)+c(w,x,y)+c(x,y,v)+c(y,v,w))%4==0&&r.push([v,w,x,y])
j=(v,w,x,y)=>{i(v,w,x,y);i(v,w,y,x);i(v,x,w,y)}
k=(v,w,x,y,z)=>{j(v,w,x,y);j(v,w,x,z);j(v,w,y,z);j(v,x,y,z);j(w,x,y,z)}
f=(v)=>(r=[],k(...v),r)

说明

该函数c计算多边形的3个相邻点之间的矢量叉积,如果为正,则返回1,否则返回0(注意:叉积不能为零,因为这些点不能共线)。

j=(v,w,x,y)=>{i(v,w,x,y);i(v,w,y,x);i(v,x,w,y)}
k=(v,w,x,y,z)=>{j(v,w,x,y);j(v,w,x,z);j(v,w,y,z);j(v,x,y,z);j(w,x,y,z)}

该函数kj生成输入数组的所有循环置换(忽略逆序)。

i=(v,w,x,y)=>(c(v,w,x)+c(w,x,y)+c(x,y,v)+c(y,v,w))%4==0&&r.push([v,w,x,y])

然后,对每个循环置换调用函数“ i”,以计算c相邻坐标的4个三元组中的每一个的函数之和。如果叉积都具有相同的符号,则它们将全部为0或1,并且总计为0(模4),并且多边形为凹面并被推入输出数组。如果任何一个三元组的符号不同,则总和将为非零(模4),并且多边形为凸形。

f=(v)=>(r=[],k(...v),r)

该函数f用于初始化输出数组,然后在返回输出之前调用上述函数。

测试

c=(v,w,x)=>(w[0]-v[0])*(w[1]-x[1])-(w[1]-v[1])*(w[0]-x[0])>0?1:0
i=(v,w,x,y)=>(c(v,w,x)+c(w,x,y)+c(x,y,v)+c(y,v,w))%4==0&&r.push([v,w,x,y])
j=(v,w,x,y)=>{i(v,w,x,y);i(v,w,y,x);i(v,x,w,y)}
k=(v,w,x,y,z)=>{j(v,w,x,y);j(v,w,x,z);j(v,w,y,z);j(v,x,y,z);j(w,x,y,z)}
f=(v)=>(r=[],k(...v),r)

tests = [
  [[6,8],[1,10],[6,6],[5,9],[8,10]],
  [[3,8],[7,5],[6,9],[7,8],[5,1]],
  [[4,8],[1,9],[9,9],[10,2],[1,6]]
];

tests.forEach(
  (test,i)=>{
    console.log( "Test " + (i+1) );
    f(test).forEach(
      (x)=>console.log( "  " + x.map((e)=>"("+e[0]+","+e[1]+")").join(','))
    );
  }
);

编辑

也可以使用原始版本并将前两行更改为:

t=(a,b,c)=>Math.sign((b[0]-a[0])*(b[1]-c[1])-(b[1]-a[1])*(b[0]-c[0]))
p=(a,b,c,d)=>[t(a,b,c),t(b,c,d),t(c,d,a),t(d,a,b)].filter(x=>x).reduce((p,c,i,a)=>p&c==a[0],1)
q=(a,m,n,o)=>[a[0],a[m],a[n],a[o]]
f=(a)=>{r=[];for(i=0;i<5;i++){b=a.slice();b.splice(i,1);r.push(q(b,1,2,3));r.push(q(b,1,3,2));r.push(q(b,2,1,3))}return r.filter((a)=>p(...a))}

但是,由于问题中明确排除了这种情况,因此不需要多余的字符。


3

Mathematica 105 96字节

Select[#~Subsets~{4},f@#&]&从(5)个点的列表中选择满足的4个点的子集f

f当一组中的4个点中的每个点位于4个点中的一个上时RegionBoundary,满足ConvexHull

f@p_:=Apply[And,RegionBoundary@ConvexHullMesh@p~RegionMember~#&/@p];
Select[#~Subsets~{4},f@#&]&

测试用例

1.让我们看一下{{6,8},{1,10},{6,6},{5,9},{8,10}}的5个凸包子集(每4个点) 。

Select[#~Subsets~{4},f@#&[{{6, 8}, {1, 10}, {6, 6}, {5, 9}, {8, 10}}]

{{{6,8},{1,10},{6,6},{5,9}}}


{{6,8},{1,10},{6,6},{5,9}}是唯一的解决方案;四个点中的每个点都用作凸包的顶点(相同的四个点)。

解


{{6,8},{1,10},{6,6},{8,10}}不是解决方案;凸包只有3个顶点。{6,8}位于船体内。

失败1


其余子集也不是解决方案:

失败2

失败3

失败4


2. {{4,8},{1,9},{9,9},{10,2},{1,6}}有三个解决方案。

Select[#~Subsets~{4},f@#&[{{4, 8}, {1, 9}, {9, 9}, {10, 2}, {1, 6}}]

{{
{{4,8},{1,9},{10,2},{1,6}},
{{4,8},{9,9},{10,2},{1,6 }},
{{1,9},{9,9},{10,2},{1,6}}
}


3. {{3,8},{7,5},{6,9},{7,8},{5,1}}有5个解决方案。

Select[#~Subsets~{4},f@#&[{{3, 8}, {7, 5}, {6, 9}, {7, 8}, {5, 1}}]

{{
{{3,8},{7,5},{6,9},{7,8}},
{{3,8},{7,5},{6,9},{5,1 }},
{{3、8},{7、5},{7、8},{5、1}},
{{3、8},{6、9},{7、8},{5 ,1}},
{{7,5},{6,9},{7,8},{5,1}}
}

请注意,五个点中的每个点都位于所有点的凸包的边界上。

如果删除了任何一个点,则其余的4个点将分别是精简凸包的顶点。

溶胶2

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.