Masalah Ender yang bahagia

32

Masalah happy ending (sebenarnya sebuah teorema) menyatakan hal itu

Setiap set lima titik di pesawat dalam posisi umum memiliki subset dari empat titik yang membentuk simpul dari segiempat cembung.

Masalahnya dinamai oleh Paul Erd ketika dua ahli matematika yang pertama kali menangani masalah tersebut, Ester Klein dan George Szekeres, bertunangan dan kemudian menikah.

Klarifikasi:

  • Posisi umum di sini berarti bahwa tidak ada tiga titik yang collinear.
  • Segi empat yang dibentuk oleh empat simpul akan selalu dianggap non-berpotongan, terlepas dari urutan poin. Sebagai contoh, mengingat empat poin [1 1], [1 2], [2 1], [2 2]yang segiempat dimaksudkan adalah persegi, bukan kupu-kupu:

    masukkan deskripsi gambar di sini

  • Quadrilateral non-berpotongan adalah cembung jika tidak ada sudut interior melebihi 180 derajat; atau setara jika kedua diagonal terletak di dalam segiempat.

Tantangan

Diberikan 5 poin dengan koordinat bilangan bulat positif, menghasilkan 4 poin yang membentuk segiempat cembung.

Aturan

Jika ada beberapa solusi (yaitu, beberapa set 4 poin), Anda dapat secara konsisten memilih untuk mengeluarkan salah satu dari mereka atau semua.

Format input dan output fleksibel seperti biasa (array, daftar, daftar daftar, string dengan pemisah yang masuk akal, dll).

Golf kode, byte paling sedikit menang.

Uji kasus

  1. Memasukkan:

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

    Hanya ada satu kemungkinan output:

    [6 8] [1 10] [6 6] [5 9]
    
  2. Memasukkan:

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

    Ada lima solusi:

    [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. Memasukkan:

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

    Ada tiga solusi:

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

    Sebagai ilustrasi, berikut adalah tiga solusi untuk kasus ini:

masukkan deskripsi gambar di sini

Luis Mendo
sumber
14
Saya mengharapkan jawaban dari Martin dengan emosi positif yang diungkapkan di dalamnya.
El'endia Starman
1
Masalah happy ending tidak harus disamakan dengan masalah Happy Ender, yaitu menemukan cara untuk mencegah anggota militer dari menemukan simulasi yang mereka mainkan adalah nyata .
user253751

Jawaban:

24

CJam, 37 34 32 byte

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

Tidak yakin apakah :-Vcukup senang, tetapi seperti yang ditunjukkan K Zhang, ada =}akhirnya. :)

Ini hanya mencetak satu solusi karena menghapus duplikat akan lebih mahal.

Uji di sini.

Penjelasan

Idenya cukup sederhana. Kami menghasilkan semua segiempat yang mungkin (termasuk semua pemesanan poin) dan kemudian pilih yang cembung. Kami menguji kecemburuan dengan melihat setiap pasang tepi dan memeriksa bahwa mereka semua berbalik ke arah yang sama.

Perasaan balik dapat diperoleh dengan mudah dari produk titik. Jika Anda mengambil tiga titik berurutan pada segi empat, dan menggambar garis dari yang pertama ke yang kedua, dan yang pertama ke yang ketiga, dan kemudian memproyeksikan yang terakhir ke tegak lurus dari yang pertama ... Anda mendapatkan nomor yang tanda memberitahu Anda apakah tiga poin ini belok kiri atau kanan. (Saya mungkin harus menambahkan diagram untuk ini.) Ini "memproyeksikan ke tegak lurus" adalah suara yang cukup terlibat, tetapi benar-benar hanya berarti bahwa kita membalikkan salah satu dari dua vektor dan mengurangi komponen setelah penggandaan daripada menambahkannya. Jadi, inilah kodenya ...

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.
}=
Martin Ender
sumber
2
Tentu, bebek yang bahagia!
Luis Mendo
1
@LuisMendo Saya pikir dua karakter terakhir lebih terlihat seperti smiley =}
K Zhang
!}dapat dianggap sebagai mengedipkan mata juga
Jezzamon
2
Jon Skeet dari CodeGolf .. ini luar biasa
Alex Carlsen
8

MATLAB, 67 byte

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

Input dalam bentuk matriks 2D di mana kolomnya masing-masing X dan 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]

Semua set 4 poin yang membuat segiempat cembung ditampilkan dalam format yang sama.

Berikut ini adalah demo yang sedikit dimodifikasi untuk bekerja dengan Oktaf

Penjelasan

Solusi ini mengambil semua himpunan bagian dari 4 poin input (urutan tidak masalah). Untuk melakukan ini, kita membuat matriks identitas dan meniadakan itu: ~eye(5). Kami loop melalui kolom matriks ini dan k(indeks loop) adalah array logis yang menentukan mana dari 4 poin untuk dipertimbangkan. Kami kemudian menggunakan ini untuk mengambil 4 poin XY ini dari input ( I(k,:)).

Kami kemudian menghitung lambung cembung dari 4 poin ini ( convhull). Output dari convhulladalah indeks input yang sesuai dengan poin yang membentuk lambung cembung (dengan indeks pertama digandakan untuk menutup lambung).

Untuk segiempat cembung, keempat poin akan menjadi bagian dari lambung cembung dari poin yang sama ( nnz(convhull(points)) > 4). Jika kami mendeteksi bahwa ini adalah masalahnya, kami menampilkan poin yang digunakan untuk iterasi khusus ini.

Suever
sumber
4

Javascript (ES6), 306 293 283 byte

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)

Penjelasan :

Fungsi cmenghitung produk silang dari vektor antara 3 titik yang berdekatan dari poligon dan mengembalikan 1 jika positif dan 0 sebaliknya (catatan: produk silang tidak boleh nol karena poin tidak bisa menjadi co-linear).

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)}

Fungsi kdan jmenghasilkan semua permutasi siklik (mengabaikan membalik urutan) dari array input.

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])

Fungsi 'i' kemudian dipanggil untuk setiap permutasi siklik untuk menghitung jumlah fungsi cuntuk masing-masing 4 kembar tiga koordinat yang berdekatan. Jika semua produk silang memiliki tanda yang sama maka mereka semua akan menjadi 0 atau 1 dan total menjadi 0 (modulo 4) dan poligon cekung dan didorong ke dalam array output. Jika ada triplet yang memiliki tanda berbeda maka totalnya akan menjadi nol (modulo 4) dan poligonnya cembung.

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

Fungsi fini digunakan untuk menginisialisasi array output dan kemudian memanggil fungsi-fungsi di atas sebelum mengembalikan output.

Tes :

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(','))
    );
  }
);

Edit :

Dapat juga menangani poin co-linear menggunakan versi asli dan mengubah dua baris pertama menjadi:

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))}

Namun, karena case tersebut secara khusus dikecualikan dalam pertanyaan maka karakter tambahan tidak diperlukan.

MT0
sumber
3

Mathematica 105 96 byte

Select[#~Subsets~{4},f@#&]&memilih, dari daftar (5) poin, himpunan bagian dari 4 poin yang memuaskan f.

fpuas ketika masing-masing titik, dari 4 poin di set, kebohongan pada RegionBoundarysatu ConvexHulldari 4 poin.

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

Uji Kasus

1. Mari kita lihat 5 cangkang subset cembung (masing-masing 4 poin) dari {{6, 8}, {1, 10}, {6, 6}, {5, 9}, {8, 10}} .

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}} adalah satu-satunya solusi; masing-masing dari empat titik berfungsi sebagai simpul dari cembung cembung (dari 4 poin yang sama).

larutan


{{6, 8}, {1, 10}, {6, 6}, {8, 10}} bukan solusi; convex hull hanya memiliki 3 simpul. {6, 8} terletak di dalam lambung.

gagal1


Himpunan bagian yang tersisa juga bukan solusi:

gagal2

gagal3

gagal4


2. {{4, 8}, {1, 9}, {9, 9}, {10, 2}, {1, 6}} memiliki tiga solusi.

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}} memiliki 5 solusi.

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}}
}

Perhatikan bahwa masing-masing dari lima titik terletak pada batas cembung semua titik.

Jika salah satu poin dihilangkan, 4 poin yang tersisa masing-masing akan menjadi simpul dari lambung cembung yang dikurangi.

sol2

DavidC
sumber