Określanie obrotu kwadratu na podstawie listy punktów


19

W tym wyzwaniu otrzymasz listę punktów. Punkty te leżą na obwodzie wyimaginowanego kwadratu . Twoim celem jest:

  1. Jeśli to możliwe, wydrukuj obrót kwadratu, który będzie wartością z [0, 90), gdzie 0 oznacza kwadrat z liniami pionowymi i poziomymi. Obrót należy podawać w stopniach liczonych w kierunku przeciwnym do ruchu wskazówek zegara.
  2. Jeśli obrót kwadratu jest niejednoznaczny (np. Otrzymanie tylko 2 punktów), wydrukuj „Nieznany”
  3. Jeśli utworzenie kwadratu z uwzględnieniem punktów jest niemożliwe, wydrukuj „Niemożliwe”

Punkty, które otrzymasz, są gwarantowane, że są unikalne i nie mają określonej kolejności. Możesz użyć dowolnego formatu, w którym chcesz wprowadzić listę, ale w moich przykładach moje punkty będą w formacie x,y, a odstępy będą oddzielone. Liczby są liczbami zmiennoprzecinkowymi i można założyć, że mieszczą się w zakresie, który może obsługiwać Twój język. Wynik powinien być dokładny do co najmniej 3 miejsc po przecinku i założyć, że Twój język obsługuje liczby zmiennoprzecinkowe z doskonałą dokładnością.

Oto kilka przypadków testowych (większość z nich używałem liczb całkowitych do łatwej wizualizacji, ale twój program powinien obsługiwać zmiennoprzecinkowe):

Nieznany:

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

Niemożliwy:

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

Możliwe (jeśli nie zostało określone, powinno zwrócić 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 

Mogłem przegapić kilka interesujących przypadków testowych. Jeśli tak, prosimy o komentarz, aby je dodać.

To jest golf golfowy, więc wygrywa kod najkrótszy!


Czy istnieje minimalna wymagana dokładność? Jak daleko od poprawnej odpowiedzi może być wyjście, zanim zostanie uznane za nieprawidłowe?
trichoplax

@trichoplax jest tak dokładne, jak pozwala na to implementacja liczb zmiennoprzecinkowych w Twoim języku.
Nathan Merrill

Czy to oznacza, że ​​jeśli istnieją 2 możliwe podejścia i jedno daje nieco dokładniejszy wynik w twoim języku, należy zastosować najdokładniejsze podejście?
trichoplax

@trichoplax tak.
Nathan Merrill

2
@NathanMerrill Skąd będę (lub ktokolwiek) wiedzieć, czy istnieje bardziej dokładne podejście? Myślę, że bardziej sensowne byłoby po prostu wymaganie stałej minimalnej dokładności, takiej jak 4 lub 6 miejsc dziesiętnych. Chociaż nie jestem nawet pewien, czy niedokładności reprezentacji zmiennoprzecinkowej danych wejściowych uniemożliwiają wiele przykładów. Może lepiej byłoby podać dane racjonalne lub całkowite.
Martin Ender

Odpowiedzi:


6

Rev 1: Ruby, 354 bajty

dalsza gra w golfa dzięki 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'}

Rubinowy, 392 bajtów

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

Algorytm wygląda następująco:

- Wybierz dowolny punkt (pierwszy) i przenieś go do początku (odejmij współrzędne tego punktu od wszystkich punktów na liście).

- Spróbuj wszystkich obrotów kwadratu wokół początku w krokach co 0,001 stopnia, o 360 stopni.

- Dla danego obrotu, jeśli wszystkie punkty znajdują się powyżej osi y, narysuj możliwie najmniejszy kwadrat wokół wszystkich punktów, uwzględniając najniższy i najbardziej lewy punkt.

-Sprawdź, czy wszystkie punkty znajdują się na krawędzi. Odbywa się to za pomocą miękkiego obliczenia, które bierze każdy punkt, znajduje kwadratowe odległości od wszystkich krawędzi i mnoży je razem. Daje to dobre dopasowanie, a nie odpowiedź tak / nie. Interpretuje się, że rozwiązanie zostanie znalezione, jeśli ten iloczyn podzielony przez długość boczną ^ 8 jest mniejszy niż 1E-9. W praktyce jest to mniej niż stopień tolerancji.

- Najlepsze dopasowanie przyjmuje mod 90 stopni i podaje jako prawidłowy kąt.

Obecnie kod zwraca wartość niejednoznaczności, jeśli znaleziono ponad 100 rozwiązań (przy rozdzielczości 0,001 stopnia. To 0,1 stopnia tolerancji).

pierwsza w pełni działająca funkcja w programie testowym

Zostawiłem rozdzielczość na 1/10 wymaganej rozdzielczości, aby prędkość była rozsądna. Wystąpił błąd 0,01 degress na ostatnim przypadku testowym.

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

wersja golfowa, rozdzielczość zgodna ze specyfikacją, trwa około minuty na połączenie w programie testowym.

W ostatnim przypadku testowym nadal występuje nieznośny błąd 0,001 stopnia. Dalsze zwiększenie rozdzielczości prawdopodobnie go wyeliminuje.

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

Zauważ, że dla około 30% więcej kodu algorytm ten można dostosować do szybkiej pracy: oczywiste jest, że w przypadkach o skończonej liczbie rozwiązań jedna z krawędzi leży płasko wzdłuż sześcianu, więc naprawdę musimy spróbować tylko te kąty które odpowiadają każdej parze wierzchołków. Konieczne byłoby również trochę poruszenia, aby sprawdzić, czy nie ma nieskończenie wielu rozwiązań.


Naprawiłem drugi przypadek testowy, dziękuję
Nathan Merrill,

@NathanMerrill zmieniona sprawa 0,0 1,0 2,0 1,2jest nadal możliwa dla kwadratu o przekątnej 0,0 ... 2,2. Próbowałem tego, a także 0,0 1,0 2,0 1,1(to drugie jest rzeczywiście niemożliwe). Kolejna kwestia: czy uważasz, że akceptowalny lub niedopuszczalny jest fakt, że mój kod zwraca niemożliwe, a nie nieznane, gdy podany jest tylko jeden punkt? Byłbym wdzięczny za odpowiedź, zanim zacznę grać w golfa.
Level River St

Chciałem to zrobić 1,1. Nie jestem pewien, jak 1,2się tam dostałeś. To nie do przyjęcia.
Nathan Merrill,

Powinieneś być w stanie sprowadzić go do co najmniej 354 bajtów: pastebin.com/jsgwMKQF
blutorange

@blutorange dzięki za wskazówki! Jestem nowy w Ruby i mam pewne trudności z golfem. Zostawiłem wiele if..ends, ponieważ mam straszne problemy z zagnieżdżonymi operatorami trójskładnikowymi w Rubim. Widzę, że obejrzałeś to, używając &&.
Level River St

6

Perl

Cześć, oto moje skromne południe. Przypadki testowe są umieszczane w strumieniu danych na dole pliku. Algorytm rozwinął się dzięki podejściu typu try-error.
Przyznaję, że jest to podejście zasadniczo heurystyczne, ale jest naprawdę szybkie: natychmiast rozwiązuje wszystkie sprawy .
Wiem, że będą pewne błędy, ale do tej pory daje poprawne odpowiedzi na wszystkie przypadki testowe.
Wiem również, że wygrywa najkrótszy kod, ale jestem pewien, że jest to jeden z najkrótszych w najszybszym znaczeniu tego terminu.

Oto algorytm

  1. badaj kropki i dla każdego segmentu między dwoma kropkami zapisz nachylenie, długość, punkt przecięcia x, punkt przecięcia y

  2. znajdź linie proste (tj. trzy kropki lub dwa sąsiednie segmenty) i wyraźne możliwe nachylenia (powiedzmy, że są obrotami). Śledź najdłuższy segment dostępny w każdej linii.

  3. znajdź wszystkie odległości między segmentem a trzecim punktem (należy to wykorzystać w punkcie 4). Śledź minimalną niezerową odległość.

  4. dla dowolnych czterech kropek (mniej więcej prostokąta) znajdź wewnętrzne kropki

Pokaż rozwiązania:

A. Powiedz „Niemożliwe”, jeśli jest jedna lub więcej kropek wewnętrznych.

B. Jedna linia:

  • W przypadku większości kropek w jednej linii bez kropek wewnętrznych, powiedz „Możliwe”

  • W przypadku kropek zbyt blisko linii powiedz „Niemożliwe”

    C. Dwie linie:

  • Powiedz „Możliwe”, gdy istnieje tylko jeden możliwy obrót

  • Powiedz „Niemożliwe”, gdy występuje więcej niż jeden obrót

    D. Brak linii: znajdź obrót, który pasuje do jego segmentu obrotu o 90 °

  • Powiedz „Możliwe”, jeśli tylko jedna pasuje lub tyle, ile pasuje kropek.

  • Powiedz „Niemożliwe”, jeśli pasuje więcej niż jeden, a nie tyle kropek

  • Powiedz „Nieznany”, jeśli tyle pasuje do obrotu.

Oto kod (wszystkie znane błędy zostały rozwiązane)

#!/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

A oto jego ouptut

# 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

Pozdrowienia.

Matteo.


Oto pierwszy błąd: twoja skrzynka 0,0 1,0 2,0 1,1 (Niemożliwe) mówi „Możliwe. -> 0,000 deg”. Muszę naprawić
Mattsteel,

Naprawdę podoba mi się to rozwiązanie. Nie przejmuj się zbytnio kodem golfowym, nie o to tak naprawdę chodzi i niekoniecznie osoba, która dostanie nagrodę.
Nathan Merrill

Dziękuję Nathan. Dane wyjściowe pokazują znacznie więcej informacji: są one w celu debugowania i zostawiłem je celowo, aby móc je naprawić
Mattsteel

Drugi błąd: fałszywy „Niemożliwe. (Bez linii) n = 8, m = 6, r = 0 c = 6” jest zapisywany tuż po poprawnej odpowiedzi „0,1 0,2 1,0 1,3 2,0 2,3 3,1 3,2: Nieznany. (Bez linii) n = 8, m = 6, r = 0 c = 6 ".
Mattsteel,

Naprawiono dwa błędy: wszystkie przypadki działają poprawnie.
Mattsteel,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.