Squarefinder - Định vị tứ giác thường xuyên


27

Hãy tưởng tượng một bó các hình chữ nhật được vẽ trong mặt phẳng, mỗi hình chữ nhật có các đỉnh của nó tại tọa độ nguyên và các cạnh của nó song song với các trục:

nhập mô tả hình ảnh ở đây

Các hình chữ nhật phân vùng mặt phẳng thành một số vùng khác nhau, được tô màu đỏ và xanh bên dưới:

nhập mô tả hình ảnh ở đây

Mục tiêu của bạn là tìm ra số lượng các khu vực như vậy là hình vuông hoàn hảo. Trong ví dụ trên, có ba:

nhập mô tả hình ảnh ở đây

Lưu ý rằng hình vuông lớn ở giữa không được tính vì nó không phải là một khu vực duy nhất, mà thay vào đó được tạo thành từ một số khu vực tách rời nhỏ hơn.

Đầu vào

Bạn có thể viết một chức năng hoặc một chương trình đầy đủ cho thử thách này.

Đầu vào sẽ là các 4nsố nguyên không âm xác định nhình chữ nhật trong mặt phẳng. Mỗi hình chữ nhật được đại diện bởi hai đỉnh đối diện, ví dụ: 4 9 7 8đại diện cho hình chữ nhật với các đỉnh đối diện (4, 9)(7, 8). Lưu ý rằng hình chữ nhật này cũng có thể được biểu diễn dưới dạng 7 8 4 9hoặc 4 8 7 9.

Định dạng đầu vào chính xác là linh hoạt (ví dụ: chuỗi được phân tách bằng dấu cách, chuỗi được phân tách bằng dấu phẩy, một mảng số nguyên, danh sách các bộ tọa độ, v.v.) nhưng vui lòng hợp lý và đưa ra ví dụ về cách chạy mã của bạn trong bài đăng của bạn. Bạn không thể sắp xếp lại đầu vào.

Để đơn giản, bạn có thể giả định rằng sẽ không có hai cạnh nào bị chồng chéo - điều này bao gồm sự chồng chéo ở một đỉnh. Cụ thể, điều này ngụ ý rằng sẽ không có hai hình chữ nhật nào được chạm từ cạnh này sang góc kia hoặc góc này sang góc kia và hình chữ nhật sẽ có diện tích khác.

Đầu ra

Chương trình của bạn sẽ in hoặc trả về một số nguyên duy nhất, đó là số vùng vuông.

Chấm điểm

Đây là mã golf, vì vậy mã trong ít byte nhất sẽ thắng.


Các trường hợp thử nghiệm

Đầu vào:

0 0 5 5
6 8 10 4
14 16 11 13
19 1 18 2

Đầu ra:

4

Đây chỉ đơn giản là bốn ô vuông rời nhau:

nhập mô tả hình ảnh ở đây


Đầu vào:

2 1 3 11
1 10 5 19
6 10 11 3
8 8 15 15
13 13 9 5
15 1 19 7
17 19 19 17

Đầu ra:

3

Đây là trường hợp thử nghiệm ví dụ ở đầu bài.


Đầu vào:

0 9 15 12
6 3 18 15
9 6 12 20
13 4 17 8

Đầu ra:

7

nhập mô tả hình ảnh ở đây


Đầu vào:

5 9 11 10
5 12 11 13
6 8 7 14
9 8 10 14
13 8 14 9
13 10 14 14

Đầu ra:

14

nhập mô tả hình ảnh ở đây


Đầu vào:

0 99999 100000 0

Đầu ra:

0

Đây chỉ là một hình chữ nhật lớn.


Đầu vào:

0 99999 100000 0
2 1 142857 285714

Đầu ra:

1

Hai hình chữ nhật lớn chồng lên nhau.

Câu trả lời:


9

SQL (POSTGIS), 286 269 261 240 226 218 216

Đây là một truy vấn cho phần mở rộng PostGIS cho PostgreSQL. Tôi đã không đếm các giá trị đầu vào trong tổng số.

SELECT SUM(1)FROM(SELECT(ST_Dump(ST_Polygonize(g))).geom d FROM(SELECT ST_Union(ST_Boundary(ST_MakeEnvelope(a,b,c,d)))g FROM(VALUES
-- Coordinate input
(2, 1, 3, 11)
,(1, 10, 5, 19)
,(6, 10, 11, 3)
,(8, 8, 15, 15)
,(13, 13, 9, 5)
,(15, 1, 19, 7)
,(17, 19, 19, 17)
)i(a,b,c,d))i)a WHERE(ST_XMax(d)-ST_XMin(d))^2+(ST_YMax(d)-ST_YMin(d))^2=ST_Area(d)*2

Giải trình

Truy vấn xây dựng hình học cho mỗi cặp tọa độ. Liên kết các vòng bên ngoài để nút đúng các dòng. Biến kết quả thành đa giác và kiểm tra chiều rộng so với chiều cao và diện tích nhân đôi so với tổng bình phương của mỗi bên.

Nó sẽ chạy như một truy vấn độc lập trên bất kỳ cơ sở dữ liệu PostgreSQL nào với Phần mở rộng PostGIS.

Chỉnh sửa Tìm thấy một vài hơn.


1
... và Haskell
Trình tối ưu hóa

@optimizer Tôi nghi ngờ nó sẽ tồn tại :)
MickyT

@MickyT Điều này đã biến thành một cuộc cạnh tranh lành mạnh. :)
Zgarb

@zgarb nó có một chút :-) nhưng tôi không nghĩ mình có gì khác để đi.
MickyT

13

Con trăn 2 480 436 386 352 byte

exec u"""s=sorted;H=[];V=[]
FRIinput():
 S=2*map(s,zip(*R))
 FiI0,1,2,3:
    c=S[i][i/2];a,b=S[~i]
    FeIs(H):
     C,(A,B)=e
     if a<C<b&A<c<B:e[:]=C,(A,c);H+=[C,(c,B)],;V+=[c,(a,C)],;a=C
    V+=[c,(a,b)],;H,V=V,H
print sum(a==A==(d,D)&c==C==(b,B)&B-b==D-d&1-any(d<X[0]<D&b<y<B Fy,XIH)Fb,aIH FB,AIH Fd,cIV FD,CIV)""".translate({70:u"for ",73:u" in ",38:u" and "})

Đưa ra danh sách các cặp tọa độ thông qua STDIN theo định dạng:

[  [(x, y), (x, y)],  [(x, y), (x, y)],  ...  ]

và in kết quả ra STDOUT.


Chương trình thực tế, sau khi thay thế chuỗi, là:

s=sorted;H=[];V=[]
for R in input():
 S=2*map(s,zip(*R))
 for i in 0,1,2,3:
    c=S[i][i/2];a,b=S[~i]
    for e in s(H):
     C,(A,B)=e
     if a<C<b and A<c<B:e[:]=C,(A,c);H+=[C,(c,B)],;V+=[c,(a,C)],;a=C
    V+=[c,(a,b)],;H,V=V,H
print sum(a==A==(d,D) and c==C==(b,B) and B-b==D-d and 1-any(d<X[0]<D and b<y<B for y,X in H)for b,a in H for B,A in H for d,c in V for D,C in V)

Giải trình

Thay vì loay hoay với các đa giác phức tạp, chương trình này xử lý các phân đoạn dòng đơn giản. Đối với mỗi hình chữ nhật đầu vào, chúng tôi thêm từng cạnh trong bốn cạnh của nó vào danh sách phân khúc tập thể, riêng lẻ. Việc thêm một phân khúc vào danh sách như sau: chúng tôi kiểm tra từng phân khúc hiện có để tìm giao điểm với phân khúc mới; nếu chúng ta tìm thấy một giao lộ, chúng ta chia cả hai phân đoạn tại điểm giao nhau và tiếp tục. Để làm cho mọi thứ dễ dàng hơn, chúng tôi thực sự giữ hai danh sách phân khúc riêng biệt: một đường chân trời và một đường thẳng đứng. Do các phân đoạn không trùng nhau, các phân đoạn ngang chỉ có thể giao nhau giữa các phân đoạn dọc và ngược lại. Tốt hơn nữa, điều đó có nghĩa là tất cả các giao điểm (không xem xét các cạnh của cùng một hình chữ nhật) là "phù hợp", nghĩa là chúng ta không có giao điểm hình chữ T, do đó "cả hai bên" của mỗi phân đoạn đều thực sự được phân chia.

Khi chúng tôi đã tạo (các) danh sách phân khúc, chúng tôi bắt đầu đếm các ô vuông. Đối với mỗi kết hợp của bốn phân đoạn (đặc biệt là hai phân đoạn ngang và hai phân đoạn dọc), chúng tôi kiểm tra xem chúng có tạo thành hình vuông không. Hơn nữa, chúng tôi xác minh rằng không có đỉnh nằm trong hình vuông này (điều này có thể xảy ra nếu, ví dụ, chúng tôi có một hình vuông nhỏ bên trong một hình vuông lớn hơn.) Điều này cho chúng tôi số lượng mong muốn. Lưu ý rằng mặc dù chương trình kiểm tra mỗi kết hợp bốn lần theo các thứ tự khác nhau, thứ tự cụ thể của tọa độ phân đoạn đảm bảo rằng chúng tôi chỉ đếm mỗi ô vuông một lần.


1
Tôi khá ấn tượng bởi cách bạn giải quyết vấn đề này nhanh chóng và cách bạn tiếp cận vấn đề! Các vòng lặp làm cho tôi đi "chắc chắn có thể làm được gì đó ..."
Sp3000

@ Sp3000 Vâng. Tôi đã thử sử dụng itertoolscho các vòng lặp nhưng cuối cùng nó dài hơn. Tôi có thể cạo một vài byte bằng exec+ thay thế chuỗi, nhưng không có gì quá thú vị.
Ell

4

Haskell, 276 266 250 237 225 222 217 byte

Nó tiếp tục ngắn hơn ... và bị xáo trộn nhiều hơn.

(x#i)l=mapM id[[min x i..max x i-1],l]
(y!j)l f=and[p l==p(f[y,j])|p<-map elem$f[y..j]]
s[h,v]=sum[1|[x,j]<-h,[y,i]<-v,x<i,i-x==j-y,(y!j)h$x#i,(x!i)v$y#j]
n=s.foldr(\(x,y,i,j)->zipWith(++)[x#i$[y,j],y#j$[x,i]])[[],[]]

Đánh giá n [(0,0,5,5),(6,8,10,4),(14,16,11,13),(19,1,18,2)]cho trường hợp thử nghiệm đầu tiên. Tôi nghĩ rằng tôi đang tiến gần đến giới hạn của việc chơi thuật toán này trên Haskell.

Hàm này quá chậm (ít nhất là O (n 3 ) trong đó n là tổng chu vi của tất cả các hình chữ nhật trong đầu vào) mà tôi không thể đánh giá nó trong hai trường hợp kiểm tra cuối cùng. Khi tôi biên dịch nó với tối ưu hóa được bật và chạy nó trên phiên bản thu nhỏ 400 lần [(0,249,250,0),(2,1,357,714)]của thử nghiệm cuối cùng, nó đã hoàn thành sau hơn 12 giây. Dựa trên điều này, trường hợp thử nghiệm thực tế sẽ kết thúc sau khoảng 25 năm.

Giải thích (một phần, tôi sẽ mở rộng điều này khi có thời gian)

Trước tiên chúng tôi xây dựng hai danh sách hvnhư sau. Đối với mỗi hình chữ nhật trong đầu vào, chúng tôi chia đường viền của nó thành các đoạn có độ dài 1. Các điểm cuối phía tây của các phân đoạn ngang được lưu trữ hvà các điểm cuối phía nam của các phân đoạn dọc trong v, như các danh sách [x,y]có chiều dài 2. Các tọa độ trong vđược lưu trữ theo chiều ngược lại hình thức như [y,x]vì lý do chơi golf. Sau đó, chúng ta chỉ cần lặp trên cả hai danh sách và tìm kiếm các cạnh ngang [x,j]và cạnh dọc [i,y]như vậy x < ii-x == j-y(vì thế họ là phía tây bắc và góc đông nam của một hình vuông), và kiểm tra xem các biên giới của hình vuông là trong danh sách đúng hv, trong khi nội thất tọa độ thì không. Số lượng các trường hợp tích cực của tìm kiếm là đầu ra.


Làm tốt lắm, tôi nghĩ tôi sẽ phải thủng lưới ngay bây giờ :)
MickyT

@MickyT Đã một tuần rồi nên tôi đã chấp nhận câu trả lời của Zgarb, nhưng nếu bạn xoay sở để đánh bại nó sau đó, dấu kiểm có thể di chuyển! Thành thật mà nói, tôi rất ấn tượng về việc hai bạn đã đi được bao xa
Sp3000

@Zgarb cũng xứng đáng giành chiến thắng :-)
MickyT

@ Sp3000 cảm ơn vì một thử thách nhỏ.
MickyT

@ Sp3000 Cảm ơn! Tôi đã có rất nhiều niềm vui chơi golf này.
Zgarb
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.