Xác định góc quay của hình vuông cho một danh sách các điểm


19

Trong thử thách này, bạn sẽ được cung cấp một danh sách các điểm. Những điểm này nằm trên chu vi của một hình vuông tưởng tượng . Mục tiêu của bạn là:

  1. Nếu có thể, hãy in ra góc xoay của hình vuông, đó sẽ là một giá trị từ [0, 90) trong đó 0 đại diện cho hình vuông có các đường thẳng đứng và nằm ngang. Vòng quay sẽ được tính theo độ tính ngược chiều kim đồng hồ.
  2. Nếu góc quay của hình vuông không rõ ràng (chẳng hạn như chỉ được cho 2 điểm), hãy in ra "Không xác định"
  3. Nếu việc tạo một hình vuông cho các điểm là không thể, hãy in ra "Không thể"

Các điểm bạn được cung cấp được đảm bảo là duy nhất và không theo thứ tự cụ thể. Bạn có thể sử dụng bất kỳ định dạng nào bạn muốn để nhập danh sách, nhưng đối với ví dụ của tôi, điểm của tôi sẽ ở định dạng x,yvà khoảng cách được phân tách. Các số này là số có dấu phẩy động và bạn có thể cho rằng chúng nằm trong phạm vi mà ngôn ngữ của bạn có thể xử lý. Đầu ra của bạn phải chính xác đến ít nhất 3 chữ số thập phân và giả sử ngôn ngữ của bạn xử lý các số dấu phẩy động với độ chính xác hoàn hảo.

Dưới đây là một số trường hợp thử nghiệm (tôi đã thực hiện hầu hết các trường hợp này bằng cách sử dụng các số nguyên để dễ hình dung, nhưng chương trình của bạn sẽ xử lý các dấu phẩy động):

Không xác định:

0,0                      
0,0 1,0        
0,0 1,0 0,1              
0,0 1,0 0,1 1,1
0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2

Không thể nào:

0,0 1,0 2,0 3,1 4,2
0,0 1,0 2,0 1,1
0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2 2,2
2,0 0,1 2,2 0,3
0,0 2,1 0,2 2,2 -1,1

Có thể (nếu không được chỉ định, sẽ trả về 0):

0,0 1,0 2,0
0,0 0.3,0.3 0.6,0.6  (should return 45)
0,0 0.1,0.2 0.2,0.4  (should return appx 63.435 (the real value is arctan(2)))
0,0 0,1 2,1 2,2
0,1 0,2 1,0 1,4 2,0 2,4 4,1 4,3 

Tôi có thể đã bỏ lỡ một số trường hợp thử nghiệm thú vị. Nếu vậy, xin vui lòng bình luận để thêm chúng.

Đây là môn đánh gôn, vì vậy đoạn mã ngắn nhất sẽ thắng!


Có độ chính xác tối thiểu cần thiết? Làm thế nào xa câu trả lời đúng có thể là đầu ra trước khi nó được tính là sai?
trichoplax

@trichoplax chính xác như việc triển khai số dấu phẩy động trong ngôn ngữ của bạn cho phép.
Nathan Merrill

Điều này có nghĩa là nếu có 2 cách tiếp cận có thể và một cách cho kết quả chính xác hơn một chút trong ngôn ngữ của bạn, thì cách tiếp cận chính xác nhất phải được sử dụng?
trichoplax

@trichoplax có.
Nathan Merrill

2
@NathanMerrill Làm sao tôi (hoặc bất cứ ai) biết nếu một cách tiếp cận chính xác hơn tồn tại? Tôi nghĩ sẽ có ý nghĩa hơn khi chỉ yêu cầu độ chính xác tối thiểu cố định, như 4 hoặc 6 chữ số thập phân. Mặc dù tôi thậm chí không chắc chắn nếu sự không chính xác của biểu diễn dấu phẩy động của đầu vào làm cho nhiều ví dụ là không thể. Có lẽ đầu vào hợp lý hoặc số nguyên sẽ tốt hơn cho điều đó.
Martin Ender

Câu trả lời:


6

Rev 1: Ruby, 354 byte

chơi gôn hơn nữa nhờ blutorange.

->a{t=s=Math::PI/18E4
d=r=c=0
a=a.map{|e|e-a[0]}
0.upto(36E4){|i|b=a.map{|e|(e/Complex.polar(1,i*s)).rect}.transpose
m,n=b
if n.min>=f=0
l=[m.max-x=m.min,n.max].max
a.each_index{|j|f+=((l-w=n[j])*(x+l-v=m[j])*(x-v)*w)**2}
(1E-9>q=f/l**8)&&(c>0&&(i-d)%9E4%89E3>1E3?c=9E9:0;c+=1;d=i)
q<t&&(r=i)&&t=q;end}
c<101&&a[1]?c<1?'impossible':r%9E4/1.0E3:'unknown'}

Ruby, 392 byte

->(a){
s=Math::PI/18E4
t=1
d=r=c=0
a=a.map{|e|e-a[0]}
(0..36E4).each{|i|
b=a.map{|e|(e/Complex.polar(1,i*s)).rect}.transpose
m=b[0]
n=b[1]
x=m.min
if n.min>=0
l=[m.max-x,n.max].max
f=0
a.each_index{|j|f+=((l-n[j])*(x+l-m[j])*(x-m[j])*n[j])**2}
q=f/l**8
if q<1E-9
c>0&&(i-d)%9E4%89E3>1E3?(c=9E9):0
c+=1
d=i
end
if q<t
r=i
t=q
end
end
}
c>100||a.size<2?'unknown':c<1? 'impossible':r%9E4/1.0E3
}

Thuật toán như sau:

-Nhấp một điểm tùy ý (điểm đầu tiên) và di chuyển điểm đó đến điểm gốc (trừ tọa độ của điểm này khỏi tất cả các điểm trong danh sách.)

Hãy thử tất cả các góc quay của hình vuông về gốc tọa độ tăng 0,001 độ, qua 360 độ.

-Đối với một phép quay cho trước, nếu tất cả các điểm nằm trên trục y, hãy vẽ hình vuông nhỏ nhất có thể xung quanh tất cả các điểm, kết hợp điểm thấp nhất và ngoài cùng bên trái.

-Kiểm tra nếu tất cả các điểm nằm trên cạnh. Điều này được thực hiện với một phép tính mềm lấy từng điểm, tìm khoảng cách bình phương từ tất cả các cạnh và nhân chúng lại với nhau. Điều này cho một sự phù hợp tốt hơn là một câu trả lời có / không. Nó được giải thích rằng một giải pháp được tìm thấy nếu sản phẩm này chia cho độ dài ^ 8 nhỏ hơn 1E-9. Trong thực tế điều này là ít hơn một mức độ khoan dung.

-Phân hợp tốt nhất được lấy mod 90 độ và báo cáo là góc chính xác.

Hiện tại mã trả về giá trị không rõ ràng nếu tìm thấy hơn 100 giải pháp (ở độ phân giải 0,001 độ. Đó là 0,1 độ dung sai.)

chức năng làm việc đầy đủ đầu tiên, trong chương trình thử nghiệm

Tôi để độ phân giải ở mức 1/10 của độ phân giải cần thiết để làm cho tốc độ hợp lý. Có một lỗi suy giảm 0,01 trên trường hợp thử nghiệm cuối cùng.

g=->(a){
 s=Math::PI/18000
 t=1
 d=r=-1
 c=0
 a=a.map{|e| e-a[0]} 

 (0..36000).each{|i| 
    b=a.map{|e|(e/Complex.polar(1,i*s)).rect}.transpose

    m=b[0]
    n=b[1]
    x=m.min

    if n.min>=0

       l=[m.max-x,n.max].max
       f=0
       a.each_index{|j|f+=((l-n[j])*(x+l-m[j])*(x-m[j])*n[j])**2}
       q=f/l**8

       if q<1E-9

         j=(i-d)%9000
         c>0&&j>100&&j<8900?(c=9E9):0 
         c+=1
         d=i
       end  

       if q<t
         r=i
         t=q
       end

     end    
  }

 print "t=",t,"   r=",r,"     c=",c,"    d=",d,"\n"
 p c>100||a.size<2?'unknown':c<1? 'impossible':r%9000/100.0   
}


#ambiguous
#g.call([Complex(0,0)])
#g.call([Complex(0,0),Complex(1,0)])
#g.call([Complex(0,0),Complex(1,0),Complex(0,1)])
#g.call([Complex(0,0),Complex(1,0),Complex(0,1),Complex(1,1)])
#g.call([Complex(0,1),Complex(0,2),Complex(1,0),Complex(1,3),Complex(2,0),Complex(2,3),Complex(3,1),Complex(3,2)])

#impossible
#g.call([Complex(0,0),Complex(1,0),Complex(2,0),Complex(3,1),Complex(4,2)])
#g.call([Complex(0,0),Complex(1,0),Complex(2,0),Complex(1,1)])
#g.call([Complex(0,1),Complex(0,2),Complex(1,0),Complex(1,3),Complex(2,0),Complex(2,3),Complex(3,1),Complex(3,2),Complex(2,2)])
#g.call([Complex(2,0),Complex(0,1),Complex(2,2),Complex(0,3)])
#g.call([Complex(0,0),Complex(2,1),Complex(0,2),Complex(2,2),Complex(-1,1)])

#possible
g.call([Complex(0,0),Complex(1,0),Complex(2,0)])
g.call([Complex(0,0),Complex(0.3,0.3),Complex(0.6,0.6)]) #(should return 45)
g.call([Complex(0,0),Complex(0.1,0.2),Complex(0.2,0.4)]) #(should return appx 63.435 (the real value is arctan(2)))
g.call([Complex(0,0),Complex(0,1),Complex(2,1),Complex(2,2)])
g.call([Complex(0,1),Complex(0,2),Complex(1,0),Complex(1,4),Complex(2,0),Complex(2,4),Complex(4,1),Complex(4,3)])

phiên bản golf, độ phân giải tuân thủ thông số kỹ thuật, mất khoảng một phút cho mỗi cuộc gọi, trong chương trình thử nghiệm.

Vẫn còn một lỗi đáng tiếc 0,001 độ trong trường hợp thử nghiệm cuối cùng. Tăng độ phân giải hơn nữa có thể sẽ loại bỏ nó.

g=->(a){                                                            #take an array of complex numbers as input
  s=Math::PI/18E4                                                   #step size PI/180000
  t=1                                                               #best fit found so far
  d=r=c=0                                                           #angles of (d) last valid result, (r) best fit; c= hit counter
  a=a.map{|e|e-a[0]}                                                #move shape so that first point coincides with origin
  (0..36E4).each{|i|                                                #0..360000
    b=a.map{|e|(e/Complex.polar(1,i*s)).rect}.transpose             #rotate each element by dividing by unit vector of angle i*s, convert to array... 
    m=b[0]                                                          #...transpose array [[x1,y1]..[xn,yn]] to [[x1..xn],[y1..yn]]...
    n=b[1]                                                          #...and assign to variables m and n 
    x=m.min                                                         #find leftmost point
    if n.min>=0                                                     #if all points are above x axis
       l=[m.max-x,n.max].max                                        #find the sidelength of smallest square in which they will fit
       f=0                                                          #f= accumulator for errors. For each point
       a.each_index{|j|f+=((l-n[j])*(x+l-m[j])*(x-m[j])*n[j])**2}   #...add to f the product of the squared distances from each side of the smallest square containing all points
       q=f/l**8                                                     #q= f normalized with respect to the sidelength.
       if q<1E-9                                                    #consider a hit if <1E-9
         c>0&&(i-d)%9E4%89E3>1E3?(c=9E9):0                          #if at least one point is already found, and the difference between this hit and the last exceeds+/-1 deg (mod 90), set c to a high value
         c+=1                                                       #increment hit count by 1 (this catches infinitely varible cases)
         d=i                                                        #store the current hit in d
       end  
       if q<t                                                       #if current fit is better than previous one
        r=i                                                         #store the new angle
        t=q                                                         #and revise t to the new best fit.
       end             
    end
  }
  c>100||a.size<2?'unknown':c<1? 'impossible':r%9E4/1.0E3           #calculate and return value, taking special care of case where single point given.
}
#ambiguous
puts g.call([Complex(0,0)])
puts g.call([Complex(0,0),Complex(1,0)])
puts g.call([Complex(0,0),Complex(1,0),Complex(0,1)])
puts g.call([Complex(0,0),Complex(1,0),Complex(0,1),Complex(1,1)])
puts g.call([Complex(0,1),Complex(0,2),Complex(1,0),Complex(1,3),Complex(2,0),Complex(2,3),Complex(3,1),Complex(3,2)])

#impossible
puts g.call([Complex(0,0),Complex(1,0),Complex(2,0),Complex(3,1),Complex(4,2)])
puts g.call([Complex(0,0),Complex(1,0),Complex(2,0),Complex(1,1)])
puts g.call([Complex(0,1),Complex(0,2),Complex(1,0),Complex(1,3),Complex(2,0),Complex(2,3),Complex(3,1),Complex(3,2),Complex(2,2)])
puts g.call([Complex(2,0),Complex(0,1),Complex(2,2),Complex(0,3)])
puts g.call([Complex(0,0),Complex(2,1),Complex(0,2),Complex(2,2),Complex(-1,1)])

#possible
puts g.call([Complex(0,0),Complex(1,0),Complex(2,0)])
puts g.call([Complex(0,0),Complex(0.3,0.3),Complex(0.6,0.6)]) #(should return 45)
puts g.call([Complex(0,0),Complex(0.1,0.2),Complex(0.2,0.4)]) #(should return appx 63.435 (the real value is arctan(2)))
puts g.call([Complex(0,0),Complex(0,1),Complex(2,1),Complex(2,2)])
puts g.call([Complex(0,1),Complex(0,2),Complex(1,0),Complex(1,4),Complex(2,0),Complex(2,4),Complex(4,1),Complex(4,3)])

Lưu ý rằng để có thêm 30% mã, thuật toán này có thể được điều chỉnh để hoạt động nhanh: rõ ràng là trong trường hợp có số lượng giải pháp hữu hạn, một trong các cạnh nằm dọc theo một khối lập phương, vì vậy tất cả những gì chúng ta thực sự phải thử là những góc đó tương ứng với từng cặp đỉnh. Cũng cần phải làm một chút ngọ nguậy để kiểm tra xem không có vô số giải pháp.


Tôi đã sửa trường hợp thử nghiệm thứ hai, cảm ơn bạn
Nathan Merrill

@NathanMerrill trường hợp sửa đổi 0,0 1,0 2,0 1,2vẫn có thể cho một hình vuông đường chéo 0,0 ... 2,2. Tôi đã thử điều đó và cũng vậy 0,0 1,0 2,0 1,1(điều sau thực sự là không thể.) Một điểm khác: bạn có cho rằng có thể chấp nhận hoặc không thể chấp nhận rằng mã của tôi trả về không thể thay vì không biết khi chỉ đưa ra một điểm duy nhất? Tôi đánh giá cao câu trả lời trước khi tôi bắt đầu chơi gôn.
Cấp độ sông St

Ý tôi là làm 1,1. Không chắc làm thế nào 1,2có được ở đó. Nó không được chấp nhận.
Nathan Merrill

Bạn sẽ có thể giảm xuống ít nhất 354 byte như thế này: pastebin.com/jsgwMKQF
blutorange 11/07/2015

@blutorange cảm ơn vì lời khuyên! Tôi mới chơi Ruby và gặp một số khó khăn khi chơi gôn. Tôi đã để lại rất nhiều if..ends vì tôi gặp rắc rối khủng khiếp với các toán tử ternary lồng nhau trong Ruby. Tôi thấy bạn có vòng đó bằng cách sử dụng &&.
Cấp sông St

6

Perl

Xin chào, đây là soution khiêm tốn của tôi. Các trường hợp thử nghiệm được đặt trong luồng DATA ở dưới cùng của tệp. Thuật toán đã phát triển theo cách tiếp cận thử lỗi.
Tôi thừa nhận rằng đó là một cách tiếp cận heuristic rộng rãi, nhưng nó thực sự rất nhanh: nó giải quyết tất cả các trường hợp ngay lập tức .
Tôi biết sẽ có một số lỗi, nhưng cho đến nay nó trả lời đúng cho tất cả các trường hợp thử nghiệm.
Tôi cũng biết rằng mã ngắn nhất sẽ thắng, nhưng tôi chắc chắn đây là một trong những mã ngắn nhất theo nghĩa nhanh nhất của thuật ngữ này.

Đây là thuật toán

  1. kiểm tra các dấu chấm và cho từng đoạn giữa hai điểm ghi độ dốc, chiều dài, chặn x, chặn y

  2. tìm các đường thẳng (nghĩa là ba dấu chấm hoặc hai đoạn liền kề) và các sườn có thể khác biệt (giả sử chúng là các phép quay). Theo dõi phân khúc dài nhất có sẵn trong mỗi dòng.

  3. tìm tất cả các khoảng cách giữa một đoạn và điểm thứ ba (điều này nên được sử dụng cho điểm 4). Theo dõi khoảng cách tối thiểu khác không.

  4. đối với bốn dấu chấm bất kỳ (khó khăn là một hình chữ nhật) tìm các dấu chấm bên trong

Hiển thị giải pháp:

A. Nói "Không thể" nếu có một hoặc nhiều dấu chấm bên trong.

B. Một dòng:

  • Trong trường hợp hầu hết các dấu chấm trong một dòng không có dấu chấm bên trong, hãy nói "Có thể"

  • Trong trường hợp các dấu chấm quá gần với dòng, hãy nói "Không thể"

    C. Hai dòng:

  • Nói "Có thể" khi chỉ có một vòng quay có thể

  • Nói "Không thể" khi có nhiều hơn một vòng quay

    D. Không có dòng: tìm xoay phù hợp với phân đoạn xoay 90 ° của nó

  • Nói "Có thể" nếu chỉ có một điểm phù hợp hoặc nhiều điểm phù hợp.

  • Nói "Không thể" nếu có nhiều hơn một dấu chấm và không nhiều bằng dấu chấm

  • Nói "Không xác định" nếu có nhiều vòng xoay phù hợp.

Đây là mã (tất cả các lỗi đã biết được giải quyết)

#!/usr/bin/perl
use strict ;
use warnings ;
my $PI = 4*atan2( 1, 1 ) ;
my $EPS = 0.000001 ;
while ( <DATA> ) {
    if ( /^\s*#/ ) { print ; next } # print comments
    chomp ;
    my @dot = split /\s+/ ;
    my $n = scalar @dot || next ; # skip empty lines

    # too few dots
    if ( $n < 3 ) {
        print "@dot : Unknown.\n" ;
        next
    }

    my %slop = () ; # segment --> its slope
    my %leng = () ; # segment --> its length
    my %x0   = () ; # segment --> its line's x-intercept
    my %y0   = () ; # segment --> its line's y-intercept
    my %side = () ; # slope   --> list of segments (with duplicates)

    # 1. examine dots
    for my $p (@dot) {
        my ($px,$py) = split /,/, $p ;
        for my $q (@dot) {
            next if $p eq $q ;
            next if defined ( $slop{ "$q $p" } ) ;
            my $segment_name = "$p $q" ;
            my ($qx,$qy) = split /,/, $q ;
            my $dx = $px - $qx ;
            my $dy = $py - $qy ;
            my $slope = "inf" ; $slope = $dy / $dx if abs($dx) > 0 ;
            my $sd = $dx*$dx+$dy*$dy ;
            my $x0 = ( $slope eq 'inf' ? $px : "nan" ) ;
            my $y0 = ( abs($slope) > 0 ? $px : "nan" ) ;
            $x0 = $qx - $qy / $slope if abs($slope) > 0 ;
            $y0 = $qy - $qx * $slope if $slope ne "inf" ;
            push @{ $side{ $slope } }, $segment_name ;
            $slop{ $segment_name } = $slope ;
            $leng{ $segment_name } = sqrt( $sd ) ;
            $x0{ $segment_name } = $x0 ;
            $y0{ $segment_name } = $y0 ;
        }
    }

    # 2. find straight lines and distinct possible slopes (rotation)
    my %line = () ;     # slope --> segment name
    my %rotation = () ; # slope --> slope itself
    my $a_rotation ;
    for my $slope ( keys %side ) {
        my %distinct = () ;
        for my $segment_name ( @{ $side{ $slope } } ) {
            $distinct{ $segment_name } = $slope ; 
            my $rot = $slope eq 'inf' ? '0' : abs( $slope < 0 ? 1/$slope : $slope ) ;
            $rotation{ $rot } = $rot ;
            $a_rotation = $rot ;
        }
        for my $a_segm ( keys %distinct ) {
            for my $b_segm ( keys %distinct ) {
                next if $a_segm eq $b_segm ;
                # the two segment has to be adjacent
                my ($a1,$a2) = split / /, $a_segm;
                my ($b1,$b2) = split / /, $b_segm;
                next unless $a1 eq $b1 || $a1 eq $b2 || $a2 eq $b1 || $a2 eq $b2 ;
                # the two segment has to have same intercepts
                my $x0a = $x0{ $a_segm } ;
                my $x0b = $x0{ $b_segm } ;
                my $y0a = $y0{ $a_segm } ;
                my $y0b = $y0{ $b_segm } ;
                next unless $x0a eq $x0b && $y0a eq $y0b ;
                # keep the longest segment
                my $a_len = 0 ;
                $a_len = $leng{ $line{ $slope } } if defined( $line{ $slope } ) && defined( $leng{ $line{ $slope } } ) ;
                for my $segm ("$a1 $b1", "$a1 $b2", "$a2 $b1", "$a2 $b2",
                              "$b1 $a1", "$b2 $a1", "$b1 $a2", "$b2 $a2" ) {
                    next unless defined ( $leng{ $segm } ) ;
                    if ( $a_len < $leng{ $segm } ) {
                        $a_len = $leng{ $segm } ;
                        $line{ $slope } = $segm ;
                    }
                }
            }
        }
    }

    # 3. find distance between a segment and a third point
    my %distance = () ;            # segment-point --> distance
    my %distance_mani = () ;       # distance --> array of segment-point
    my %min_distance = () ;        # segment --> min distance to other dots
    for my $segment_name ( keys %slop ) {
        my $a = $slop{ $segment_name } ;
        my $b = -1 ;
        my $c = $y0{ $segment_name } ;
        my $z = $x0{ $segment_name } ;
        for my $p (@dot) {
            next if $segment_name =~ /$p/ ; # skip dots that are in the segment
            my ($px,$py) = split /,/, $p ;
            my $d = 0 ;
            if ( $a ne 'inf' ) {
                my $num = ($b * $py) + ($a * $px) + $c ;
                my $den = sqrt( $a*$a + $b*$b ) ;
                $d = abs( $num ) / $den ;
            }
            else {
                $d = abs( $px - $z );
            }
            $distance{ "$segment_name $p" } = $d ;
            push @{ $distance_mani{ $d } }, "$segment_name $p" ;
            if ( $d > 0 ) {
                $min_distance{ $segment_name } = $d if !defined ( $min_distance{ $segment_name } ) or $d < $min_distance{ $segment_name }
            }
        }
    }

    # 4. find inner dots: pick 4 dots to form a well shaped pseudo-rectangle
    #    and check for any other dot that is too close to all the 4 sides.
    my $fail = 0 ;
    RECTANGLE:
    for my $a ( @dot ) {
        for my $b ( @dot ) {
            next if $a eq $b ;
            my ($ax,$ay) = split /,/, $a ;
            my ($bx,$by) = split /,/, $b ;
            next if $ax > $bx || $ay > $by ;
            for my $c ( @dot ) {
                next if $c eq $a or $c eq $b ;
                my ($cx,$cy) = split /,/, $c ;
                next if $bx < $cx || $by > $cy ;
                for my $d ( @dot ) {
                    next if $d eq $a or $d eq $b or $d eq $c ;
                    my ($dx,$dy) = split /,/, $d ;
                    next if $cx < $dx || $cy < $dy  ;
                    next if $dx > $ax || $dy < $ay  ;
                    for my $e ( @dot ) {
                        next if $e eq $a or $e eq $b or $e eq $c or $e eq $d ;

                        my $abe = $distance{ "$a $b $e" } || $distance{ "$b $a $e" } || next ;
                        my $bce = $distance{ "$b $c $e" } || $distance{ "$c $b $e" } || next ;
                        my $cde = $distance{ "$c $d $e" } || $distance{ "$d $c $e" } || next ;
                        my $dae = $distance{ "$d $a $e" } || $distance{ "$a $d $e" } || next ;

                        my $abd = $distance{ "$a $b $d" } || $distance{ "$b $a $d" } || next ;
                        my $abc = $distance{ "$a $b $c" } || $distance{ "$b $a $c" } || next ;
                        my $bca = $distance{ "$b $c $a" } || $distance{ "$c $b $a" } || next ;
                        my $bcd = $distance{ "$b $c $d" } || $distance{ "$c $b $d" } || next ;
                        my $cdb = $distance{ "$c $d $b" } || $distance{ "$d $c $b" } || next ;
                        my $cda = $distance{ "$c $d $a" } || $distance{ "$d $c $a" } || next ;
                        my $dac = $distance{ "$d $a $c" } || $distance{ "$a $d $c" } || next ; 
                        my $dab = $distance{ "$d $a $b" } || $distance{ "$a $d $b" } || next ; 

                        if ( $abd > $abe && $abc > $abe && 
                             $bca > $bce && $bcd > $bce &&
                             $cdb > $cde && $cda > $cde &&
                             $dac > $dae && $dab > $dae) {
                            ## print "     $a $b $c $d --> $e\n";
                            $fail ++ ;
                            last RECTANGLE ;
                        }
                    }
                }
            }
        }
    }
    if ( $fail ) {
        print "@dot : Impossible.\n" ;
        next # DATA 
    }

    my $m = scalar keys %rotation ; # how many distinct slopes
    my $r = scalar keys %line ; # how many lines i.e. >3 dots in a straight line

    print "@dot : " ;
    # most of dots lie in single line without inner dots
    if ( $r == 1 ) {
        $a_rotation = (keys %line)[0] ;
        my $a_segment = $line{ $a_rotation } ;
        my $a_dist = $min_distance{ $a_segment } || 0 ;
        if ( $a_dist && $a_dist < $leng{ $a_segment } ) {
            print "Impossible.\n"  ;
        }
        else {
            print "Possible. --> " . sprintf("%.3f deg", 180 / $PI * atan2( $a_rotation, 1 ) ) . "\n" ;
        }
        next # DATA
    }
    # two lines
    if ( $r == 2 ) {
        print "Impossible.\n" if $m > 1 ;
        print "Possible. --> " .
            sprintf("%.3f deg", 180 / $PI * atan2( $a_rotation, 1 ) ) . "\n" if $m == 1 ;  # never?
        next ; # DATA
    }
    # no lines
    if ( $r == 0 ) {
        # match between segment rotation and other side
        my $count = 0 ;
        my $numeros = 0 ;
        for my $slope ( keys %rotation ) {
            my $rot = $slope eq '0' ? 'inf' : -1/$slope ;
            if ( exists $side{ $rot } ) {
                $count++ ;
                my $u = scalar @{ $side{ $rot } } ;
                if ( $numeros < $u ) {
                    $numeros = $u ;
                    $a_rotation = $slope ;
                }
            }
        }
        print "Possible. --> " .
            sprintf("%.3f deg", 180 / $PI * atan2( $a_rotation, 1 ) ) . "\n" if $count < 2 or $count == $n ;
        print "Unknown.\n"    if $count == $m ;
        print "Impossible.\n"    if $count > 2 && $count != $n && $count != $m;
        next # DATA
    }
    # there are lines
    print "lines $r " ;
    my $shorter = 0 ;
    my $longer = 0 ;
    for my $slope ( keys %line ) {
        for my $dis ( keys %distance_mani ) {
            $shorter++ ;
            $longer++ ;
        }
    }
    print "ACK! WHAT IS THIS CASE! n=$n, m=$m, r=$r\n" ;
    1 ;
}

1;

__DATA__
# Unknown:

0,0
0,0 1,0
0,0 1,0 0,1
0,0 1,0 0,1 1,1
0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2

# Impossible:

0,0 1,0 2,0 3,1 4,2
0,0 1,0 2,0 1,1
0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2 2,2
2,0 0,1 2,2 0,3
0,0 2,1 0,2 2,2 -1,1

# Possible (if not designated, should return 0):

0,0 1,0 2,0 1,2
0,0 1,0 2,0 0.5,2.1

0,0 1,0 2,0
0,0 1,0 2,0 1,2
0,0 0.3,0.3 0.6,0.6
0,0 0.1,0.2 0.2,0.4
0,0 0,1 2,1 2,2
0,1 0,2 1,0 1,4 2,0 2,4 4,1 4,3

Và đây là ouptut của nó

# Unknown:
0,0 : Unknown.
0,0 1,0 : Unknown.
0,0 1,0 0,1 : Unknown.
0,0 1,0 0,1 1,1 : Unknown.
0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2 : Unknown.
# Impossible:
0,0 1,0 2,0 3,1 4,2 : Impossible.
0,0 1,0 2,0 1,1 : Impossible.
0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2 2,2 : Impossible.
2,0 0,1 2,2 0,3 : Impossible.
0,0 2,1 0,2 2,2 -1,1 : Impossible.
# Possible (if not designated, should return 0):
0,0 1,0 2,0 1,2 : Possible. --> 0.000 deg
0,0 1,0 2,0 0.5,2.1 : Possible. --> 0.000 deg
0,0 1,0 2,0 : Possible. --> 0.000 deg
0,0 1,0 2,0 1,2 : Possible. --> 0.000 deg
0,0 0.3,0.3 0.6,0.6 : Possible. --> 45.000 deg
0,0 0.1,0.2 0.2,0.4 : Possible. --> 63.435 deg
0,0 0,1 2,1 2,2 : Possible. --> 0.000 deg
0,1 0,2 1,0 1,4 2,0 2,4 4,1 4,3 : Possible. --> 0.000 deg

Trân trọng.

Matteo.


Đây là lỗi đầu tiên: trường hợp của bạn 0,0 1,0 2,0 1,1 (Không thể) được nói là "Có thể. -> 0,000 deg" theo kịch bản của tôi. Tôi phải sửa chữa
Mattsteel

Tôi thực sự thích giải pháp này. Đừng lo lắng về môn đánh gôn quá nhiều, đó không phải là thử thách thực sự và đó không nhất thiết là người sẽ nhận được tiền thưởng.
Nathan Merrill

Cảm ơn bạn. Đầu ra cho thấy nhiều thông tin hơn: đây là mục đích gỡ lỗi và tôi đã cố tình để chúng có thể sửa chữa
Mattsteel

Lỗi thứ hai: một lỗi "Không thể. (Không có dòng) n = 8, m = 6, r = 0 c = 6" được viết ngay sau câu trả lời đúng "0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2: Không xác định. (Không có dòng) n = 8, m = 6, r = 0 c = 6 ".
Mattsteel

Hai lỗi đã được sửa: tất cả các trường hợp đều chạy tốt.
Mattsteel
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.