[ Algorytmika?? ] Analizowanie ście?żki

0

Witam .
Powiedziałem sobie kiedyś , że już nigdy nie wejdę do tego działu ( niektórzy wiedzą o co chodzi ) , jednak trzeba kiedyś to zrobić :) .
Oto mój problem :

Zadanie polega na przeanalizowaniu ścieżki przez nas narysowanej .
Załóżmy , że mamy żółwia , sterujemy nim , a on pozostawia po sobie ślady . Gdy zakończy podróż my analizujemy drogę jaką przebył .
Możliwe są 4 rodzaje ścieżki :
zamknięta ( przecinająca się lub nie ) oraz otwarta ( przecinająca się lub nie ) . Ścieżka jest zamknięta , gdy kończymy tam gdzie zaczęliśmy . W przypadku , gdy ścieżka jest zamknięta i się nie przecina żółw wytoczył pewne pole . Naszym zadaniem jest obliczenie tego pola . Zakładamy dla ułatwienia , że ścieżka ma grubość : 1 . Napisałem program , który to robi ( jest tutaj ) .
Działa na takiej samej zasadzie jak Narzędzie Wiadra Z Wodą w programach graficznych . Oznaczam w ten sposób to co jest poza ścieżką ( polem ) i zliczam to co nie zostało zaznaczone ( ani przez sciezke , ani przez "Wiadro Z Farbą" ) . Program dosnonale radzi sobie z tym przypadkiem ( zaczynamy w lewym górnym rogu i idziemy w prawo ) :
user image
z którym nie może sobie poradzić program , który zaczął by zaznaczać "od środka" ( mam nadzieje ze ktos wie o co chodzi :) ) .
Za to mój program z kolei wysypie się w tym przypadku :
user image
w którym tamten by dobrze zadziałał . Sam już nie wiem co o tym myśleć . Na upartego można założyć , że ten pojedyńczy kwadracik ma należeć do tego pola , a z drugiej jest on "po zewnętrznej" . Jak zrobić , żeby w obydwu przypadkach pole było dobrze liczone ??

Może się okazać , że ten problem jest bez sensu , jeśli tak wybaczcie ;) tak to jest jak sie nad czymś długo siedzi :) .

0

L := 0;
for w := 0 to OstatniWiersz do
{
/* odczytaj jeden wiersz */
z := False;
for pole := 0 to ostatnie do
{
if not z then //Jeżeli nie rozpoczęliśmy zliczania
{
if pole is sciezka then
{
o := True; //Obszar się rozpoczął
z := True; //Zaczynamy zliczanie
L++; //Zwiększamy pole
}
}
else //Jeżeli już liczymy
{
if pole is sciezka then
{
if o then //Jeżeli rozpoczeło się zliczanie obszaru (czyli 1 trafiliśmy na ścieżkę a jeszcze nie trafiliśmy na pusty obszar wew. ścieżki)
L++;
else
{
z := False; //Jeżeli trafiliśmy na środek obszaru, to wyłączyliśmy o, więc to jest już ścieżka zamykająca
L++;
}
}
else
{
o := False; //Jeżeli pole nie jest ścieżką, to jesteśmy w środku obszaru i trzeba wyłączyć wskaźnik początku obszaru.
L++;
}
}
}

0

tak sobie pomyslalem, ze moznaby pokusic sie o metode analizy polegajaca na analizowaniu calych wierszy i kolumn obrazu z wykorzystaniem faktu ze wewnetrzne pole jest ograniczone konturem w taki sposob ze w pojedyncej lini napotykamy na kontur dwa razy. To co pomiedzy to interesujace nas pole wewnetrzne.przy czym jako wystapienie kontuu uwazam jedno badz wiecej pol konturowych.

dopisane:
wlasnie tak jak to Dryo zrobil

0

Dryo , nic nie rozumiem :) , C by się przydało .
Czy twój sposób jest odporny na ten drugi obrazek obrócony o 90 stopni w lewo ??

0

Być powinien. Nie wiedziałem jak to napisać. Więc słownie postaram się.
Odczytujesz po 1 wierszu. Przechodzisz od lewej do prawej aż trafisz na ścieżkę.
Jeżeli trafisz na ścieżkę to dajesz jakiś znacznik i przechodzisz dalej, aż trafisz drugi raz na ścieżkę. W momencie jak drugi raz na nią trafisz, to kończysz zliczanie i wyłaczasz znacznik. Oczywiście, aby także zliczało linie poziome potrzebny jest jeszcze jeden znacznik. To co wcześniej napisałem to właśnie taki pseudokod. Wystarczy go przerobić na dowolny język.

0

Kumpel wpadł na praktycznie taki sam pomysł kiedyś , wydaje mi się jednak , że dla drugiego przypadku obróconego o 90 stopni w lewo źle liczy .
4 wiersz od góry ( po obróceniu , licząc od 1 ) :
trafiamy na sciezke , liczymy pole ... , trafiamy na sciezke i wylaczamy znacznik , ale zaraz trafiamy na nia jeszcze raz wlaczamy znacznik , zaczynamy liczyc - i tu jest blad , tego pola wlasnie nie powinno sie liczyc ! . dobrze mówię , czy sie gdzieś rypnąłem ??

0

Wszystko się zgadza. Właśnie dlatego w kodzie jest dotatkowy znacznik wyłączający takie przypadki :)
Nie bardzo wiedziałem jak to słowni opisać, dlatego jest w pseudokodzie (znacznik o jest za to odpowiedzialny). Szkoda, że nie mogę tego pokazać przy tobie. Może jest tutaj jakiś program do rysowania na cudzym ekranie? ;-)
/* dopisane */
OK. Już widzę ten błąd. Trzeba po prostu inaczej ten znacznik wyłączać. Troszkę później, jeżeli następny za będzie pusty.

/* dopisane2 */

Mój kod jest zły. Nie jest możliwym analiza wyłacznie jednego wiersza.
Jeżeli mamy taki układ:
soossos
s- ścieżka
o - pusty obszar
To nie możemy określić, czy to ss to jest poziomą linią jednej ścieżki, czy są to dwie pionow, a co za tym idzie czy ostatnie o jest w obszarz czy nie. Szukam dalej...

0

To nie możemy określić, czy to ss to jest poziomą linią jednej ścieżki, czy są to dwie pionow, a co za tym idzie czy ostatnie o jest w obszarz czy nie. Szukam dalej...

Możemy to określić tylko pamiętając jak biegła ścieżka ( np pamiętając jej "punkty" kluczowe ) . To jednak nie jest takie proste na jakie się wydaje .

0

Dryo a jakby tak trafiajac na powyzsza kombinacje, przy zalozeniu ze analizujemy od gory obrazka, "rozszczepic" analize na dwaregiony, czyli na obszar o wspolrzednych sciezki ograniczonych wspolrzedna pierwszego "s" i drugi obszar o wspolrzednych wiekszych rownych wspolrzednej drugiego "s" i postepowac dla kazdego z tych regionow jak poprzednio? to by pozwolilo zrobic "mape" obszarow w tablicy.

aha tylko jescze trzba przedtem sprawdzic czy oba powstale w ten sposob obszary maja caglosc z dotychczas analizowanymi. to znaczy czy nie zachodzi cos takiego

sooosssss
soossOOss
soossssss

O - mogloby byc falszywie uznane za obszar wewnetrzny.
wystarczy zatem sprawdzic czy punkt o wspolrzednej y takiejsamej jak O w poprzednim wierszu jest obszarem wewnetrznym

0

A nie dałoby się tak:

sssssss
sooooos
sooooos
sssssss
sssssss
sooooos
sooooos
sssssss

(z grubsza tak jak w przykładzie graficznym)
podzielić na drobniejsze kwadraciki i uzyskac zamiast lewego gornego rogu:

ooo
oss
oso

itp. i zamiast tego środka:

oooooo
ssooss
osooso

i z takiej postaci stosować "wiadro z farbą" a następnie z powrotem zamienic na takie jak było i uzyskać

sssssss
sxxxxxs
sxxxxxs
sssssss
sssssss
sxxxxxs
sxxxxxs
sssssss

gdzie x należy do pola i należy zaznaczyć. Nie wiem, czy da się to zrozumieć, ale może coś z tego wyjdzie.
P.S. Użyłem znacznika delphi, żeby pisać czcionką nie TrueType i żeby każda literka była tej samej szerokości.

//Do tego został tag [code] - m.M

0

Adam : ja sie przyznam szczerze że nie za bardzo rozumiem twój sposób . Przeanalizuj nim 1 i 2 obrazek z mojego 1 posta , przy czym ten drugi również obrócony o 90 stopni w lewo , bo w tym główny problem .

0

A to nie jest po prostu problem przynależności punktu do wielokąta? Chyba jest - polecam poszukanie od tego algorytmu (a coby nie utrudniać, to tam jest: www.algorytm.cad.pl )
A jak się uda to po prostu sprawdzamy dla wszystkich punktów i sumujemy - sposób może niezbyt ambitny, ale moze zadziała :)

0

Numi , na początku myślałem , że to rozwiąże ten problem . Jednak przemyślałem sprawe i doszedłem do wniosku , że to nigdy nie przejdzie . W naszym przykładku ścieżka ma grubość 1 . To w pełni dyskwalifikuje to rozwiązanie .
//
Gdybyśmy rozważali ten problem przy założeniu , że ścieżka nie ma szerokości , twoje rozwiązanie byłoby idealne .

0

Możesz mi wyjaśnić w czym przeszkadza grubosc sciezki ?
Jezeli tak, to zawsze mozesz przyjac grobosc 0, i od koncowego wyniku odjac dlugosc sciezki.

0

A gdyby tak... będe gdybał, więc mogę nię trafić, ale moze komus cos podpowiem... Może gdyby tak to wszystko rozciągnąć... Powiedzmy dwukrotnie w pionie i poziomie, to wtedy okazałoby się, że grubość scieżki nie rzutuje, bo pomiędzy równoległymi liniami pojawiaja się puste przestrzenie. Tylko trzebaby jakoś wcześniej zapisać kształt wielokąta (przebytej ściezki) i uzupełnić po rozciągnięciu ... Czy to coś komus da... Pociągnijcie w tę stronę... Mi się wydaje, że trafiłem. Wtedy nie powinien sie algorytm wykrzaczyc, bo okaże się, że zamknięty obszar (ten mały) należy do obszaru zewnętrznego. Kombinujcie...

Potem policzyć pole nie rozciągnietego, wg tego wyszło z analizy rozciągniętego... :) I'm almost sure, it will work.

Im dłużej nad tym myślę, tym więcej przychodzi mi pomysłów...
powiększając dwukrotnie i uzupełniając ścieżkę znowu może sie pojawić problem, że te uzupełnione będą przy sobie równoległe... poza tym mnożnik wtedy zacznie skakać po potęgach 2, a to tylko strata czasu i pamięci. Więc trzeba jakoś sie zabezpieczyć... Nie warto znów tego dwukrotnie powiekszać... Chyba lepiej jest wrócic i zwiększyc mnożnik do 3 i sprawdzić, ewentuanie potem do czterech- liniowo, ale cały czas startować (powiększać) oryginalny stan.

Nie wiem, jeśli... Ja tylko gdybam.

Nie, wiecie 2x starczy, bo żółw się tu porusza tylko w kierunkach prostopadłych.

Do postu poniżej:
Pomyśle w międzyczasie, moze Ci cos podrzuce pascal, czy c++ , czy obojetne? Właściwie dla ułatwienia... Trzeba zapamietywać tylko te punkty, w których żółwik skręcał, bo reszta to przecież proste... Może sam sobie dasz radę. Nie, nie chodz mi o żadne odejmowenie.... Rzutujesz oryginalny punkt, na rozciągnięta powierzchnię i tylko sprawdzasz, czy należy do wnętrza, czy nie. Ale liczysz cały czas oryginał. Wiesz co zara biorę się do roboty, bo na przykładzie będzie najłatwiej...

Robi... Powieksza... Ale dalej nie robiłem nie mam czasu... W każdym razie punkt (x,y) z pierwotnej tablicy nalezy do wnętrza, gdy punkt z powiekszonej (2x,2y) nalezy do wnetrza, dalej powinienes sobie poradzic... Aha to jest pod dosa

{$A-,B-,D+,E-,F-,G-,I+,L+,N-,O-,P-,Q+,R+,S+,T-,V+,X+,Y+}
{$M 16384,0,0}

{trouble.pas - answer for Trouble post}

type bytearr=array[0..65534]of byte;
     pbytearr=^bytearr;

     pointarr=array[0..32766,0..1]of byte;
     ppointarr=^pointarr;

     znak=record ch:char;at:byte end;
     ekran=array[1..25,1..80]of znak;

const
      arrx=10;   {algorytm jest tak napisany, ze bedzie dzialal niezaleznie od tych cyferek}
      arry=8;    {przyklad, wiec wiecej nie potrzeba}

      x=0;
      y=1;
      pointcount=16; {tyle zakretow}

      z:array[0..pointcount-1,0..1]of byte= {zakrety}
      ((0,0),           {zaczyna w prawo}
       (9,0),           {skeca w dol              (+pi/2)-clockwise}
       (9,2),           {sreca w lewo             (+pi/2)}
       (5,2),           {w dol                    (-pi/2)-anticlockwise}
       (5,6),           {w prawo                  (-pi/2)}
       (7,6),           {do gory                  (-pi/2)}
       (7,3),           {teraz w prawo            (+pi/2)}
       (9,3),           {a teraz w dol            (+pi/2)}
       (9,7),           {(dolny prawy rog)w lewo  (+pi/2)}
       (0,7),           {(dolny lewy rog) do gory (+pi/2)}
       (0,5),           {w prawo                  (+pi/2)}
       (3,5),           {do gory                  (-pi/2)}
       (3,2),           {w lewo                   (-pi/2)}
       (1,2),           {w dol                    (-pi/2)}
       (1,4),           {w lewo                   (+pi/2)}
       (0,4)            {do gory                + (+pi/2)}
      {(0,0)jak wstawisz jeszcze raz to nie trzeba bedzie uzywac modulo)- your choice}
                                                 {---------}
       {wszystko ok biorac pod uwage, ze (0,0) to (+pi/2) - obwod zamkniety}
      );

var   e:ekran absolute $b800:$0000; {zakadam, ze masz chociaz cga ;) }
const a:array[0..arry-1,0..arrx-1]of byte {oryginal}
      =
      ((1,1,1,1,1,1,1,1,1,1),
       (1,0,0,0,0,0,0,0,0,1),
       (1,1,1,1,0,1,1,1,1,1),
       (1,1,0,1,0,1,0,1,1,1), {<- tu sa 2 kyrtyczne momenty}
       (1,1,0,1,0,1,0,1,0,1),
       (1,1,1,1,0,1,0,1,0,1),
       (1,0,0,0,0,1,1,1,0,1), {tu 1 zeby bylo smieszniej}
       (1,1,1,1,1,1,1,1,1,1));

var   b:array[0..arry-1,0..arrx-1]of byte;     {kopia,ale wynik z przeliczen 1*oryginal}
      c:array[0..arry*2-1,0..arrx*2-1]of byte; {kopia 2*oryginal - wynik z przeliczen}

procedure setxy(x,y:byte);
assembler;
asm
  mov ah,02h
  xor bh,bh
  mov dl,[x]
  dec dl      {0-1=255 - underflow, but works, 255,255-> cursor out of screen}
  mov dh,[y]
  dec dh;
  int 10h
end;

procedure cls(at:byte;ch:char);
var j,i:byte;
begin
  for j:=1 to 25 do
    for i:=1 to 80 do
      begin
        e[j,i].ch:=ch;
        e[j,i].at:=at
      end
end;

procedure print(x,y,at:byte;s:string);
var i:byte;
begin
  asm dec [x] end;
  for i:=1 to byte(s[0])do
    begin
      e[y,x+i].ch:=s[i];
      e[y,x+i].at:=at
    end
end;

procedure putchar(x,y,at:byte;ch:char);
begin
  e[y,x].ch:=ch;
  e[y,x].at:=at
end;

procedure drawtab(sx,sy:byte;p:pbytearr;multiply:byte);
var i,j:byte;
    tx,ty:byte;
begin
  tx:=arrx*multiply;
  ty:=arry*multiply;
  for j:=0 to ty-1 do
    for i:=0 to tx-1 do
      putchar(sx+i,sy+j,7,char(249-30*p^[(tx*j)+i]))

end;

function min(a,b:integer):integer;
assembler;
asm
  mov bx,[b]
  mov ax,[a]
  cmp bx,ax
  jge @1
  mov ax,bx
  @1:
end;

function max(a,b:integer):integer; {widzisz roznice?}
assembler;
asm
  mov bx,[b]
  mov ax,[a]
  cmp ax,bx                        { <- tu ;-) }
  jge @1
  mov ax,bx
  @1:
end;

{Dryo, gdyby nie Ty... mam burdel i nie wiem, gdzie szukac tej ksiazki - asm}



procedure createtab(t:pbytearr;p:ppointarr;points,multiply:byte); {tab lenght & width n dowolne}
var i:byte;
    d:integer;    {multiplikator dla posuwu wzdluz osi y - ble, brzmi okropnie}
    k:integer;    {koniec petli}
    j:integer;    {poczatek petli}
begin
  for i:=1 to points do
    begin

      {nie, to wcale nie bylo trudne, nie, nad tym kawalkiem tylko pol dnia !! he , he}

      if p^[i mod points,x]-p^[i-1,x]<>0
        then
          begin
            d:=p^[i-1,y]*arrx*multiply;                        {ta zmienna jest wolna, wiec uzyje na pkt startowy}
            j:=(min(p^[i mod points,x],p^[i-1,x])+d)*multiply; {zeby indeks petli while rosl}
            k:=(max(p^[i mod points,x],p^[i-1,x])+d)*multiply;
            d:=1;                                              {dla posuwu poziomego- tokarki/frezarki, bleee}
          end
        else
          begin
            d:=p^[i-1,x];          {bo sa w jednej linii- ten sam x, wiec obojetne}
            j:=((min(p^[i mod points,y],p^[i-1,y])*arrx)*multiply+d)*multiply;
            k:=((max(p^[i mod points,y],p^[i-1,y])*arrx)*multiply+d)*multiply;
            d:=arrx*multiply       {dla posuwu pionowego}
          end;



      while j<=k do  {rysowanie sciezki to juz banal :}
        begin
          t^[j]:=1;
          inc(j,d)
        end
      end

end;

function isinside(sm,lg:pbytearray;px,py:byte):boolean; {sm- small, lg- large (default x2)}
begin

end;

function area:integer;
begin
end;

begin

  {debug code

  writeln(min(10,12));
  writeln(min(12,10));
  writeln(max(10,12));
  writeln(max(12,10));

  writeln(min(-10,12));
  writeln(min(12,-10));
  writeln(max(10,-12));
  writeln(max(-12,10));

  writeln(min(-10,-12));
  writeln(min(-12,-10));
  writeln(max(-10,-12));
  writeln(max(-12,-10));}

  asm
    mov ax,03h
    int 10h
  end;

  setxy(0,0); {precz! przecz z ekranu!}


  fillchar(b,sizeof(b),0);
  fillchar(c,sizeof(c),0);

  createtab(@b,@z,pointcount,1); {x1 - zrob tablice z zapamietanych pointow}
  createtab(@c,@z,pointcount,2); {x2 - z zapamietanych pointow}

  print(1,1,$0e,'as You see : No trouble ;)');

  drawtab(2,3,@a,1);
  drawtab(2,13,@b,1);
  drawtab(arrx+6,4,@c,2);

  print(1,24,$0a,'niestety musisz miec pointy, zeby to dzialalo');
  print(1,25,$0f,'[esc] - break');

  asm
    @1:
    xor ah,ah
    int 16h
    xor ax,011bh {only esc}
    jnz @1
  end
end.

Oddam Ci prawa autorskie za pivo.

0

flabra : oryginalny pomysł , jednak po krótkiej jego analizie stwierdzam , że napisanie algorytmu który by rozciągał ścieżke a następnie od powstałego wyliczonego pola odejmową odpowiednią wartość , żeby otrzymać pole właściwe , może być potężnym problemem . Chyba , że masz jakiś pomysł , bo ja na razie nie .
// co prawda mozna to wydluzyc w taki sposób ze pamietamy punkty narozne wielokąta i mnożymy każdą współrzędną razy 2 . Tylko potem problem z tym ile pola odjąć ... duży problem ...

numi i marcin : wasze posty naprowadziły mnie na ( mam nadzieje ) rozwiązanie tego problemu . Marcin ma rację , że można załozyć że grubość ścieżki == 0 ( ale nie ma racji w tym , że od pola dla ścieżki bez grubości wystarczy odjąć jej dlugość ) . Ja to widze tak :
poruszamy się żółwiem tak jak w moim programie , ze ścieżką równą 1 . Nasza plansza ma postać szachownicy , przy czym każde kwadratowe na niej pole ma wymiary 1x1 . Zapamiętujemy współrzędne wierzchołków powstałego wielokąta ( tak na prawde współrzędne kwadratowych pól na "szachownicy", zaczynamy od 0,0 ) .
Do każdej współrzędnej każdego wierzchołka dodajemy 0.5 ( czyli jesli zaczynalismy od pola o wsp 0,0 , to po zmianie zaczynalismy od punktu o wsp 0.5,0.5 ) . Teraz mamy nowe współrzędne , które wyznaczają ścieżke o zerowej grubości , która po zrzutowaniu na "szachownice" biegnie dokładnie po środku dawnej ścieżki o grubości 1 . Znajdujemy max i min wartości współrzędnych x,y i dzięki nim wyznaczamy obszar w którym mieści się nasz nowy wielokąt . Przechodzimy po kojejnych punktach w naszym obszarze , zmieniając wartości współrzędnych o 1 . Np. 0.5,0.5 , 1.5,0.5 , 2.5,0.5 , ... , 0.5,1.5 , 1.5,1.5 itd ( dzięki temu sprawdzimy punkty które znajdują się w środku każdego pola na "szachownicy" ) . Dla każdego takiego punktu sprawdzamy tą metodą : http://www.algorytm.cad.pl/Algorithms/21-30/algorithm26.html oraz tą : http://www.algorytm.cad.pl/Algorithms/21-30/algorithm25.html
czy należy do wielokata czy nie i zliczamy punkty należące . To czy punkt należy do wielokąta sprawdzamy prowadząc od niego odcinek kończący się w punkcie o wsp x-owej równej x-owi pierwszego punktu+1 i wsp y-owej równej y-min wyznaczające obszar -1 . Następnie sprawdzamy czy powstały odcinek nie przecina się z którymś z odcinków tworzących ścieżkę i w zależności od tego ile razy się przecina , stwierdzamy czy jest w środku czy nie .

Ufff . Mam nadzieje , ze to dobry sposób .

Do postu powyżej
Przemyślałem twój sposób i mi się podoba ( chodzi mi o idee rozszerzania ) . Myśle , że to bez problemu powinno się sprawdzić . Co do kodu , to niestety ( albo stety ) jestem dupa z pascala . Ale tutaj nie chodzi o kod , jak teraz mam 2 sposoby na rozwiązanie tego zagadnienia to implementacja któregoś z nich nie jest już problemem .
A co do piwa , to nie potrzebuję praw autorskich do tego programu ;) , po prostu byłem ciekawy jakie macie pomysłu na jego rozwiązanie . Jednak jak będziesz kiedyś w Krakowie to masz [browar] za zainteresowanie :) .

0

To mówiąc szczerze, to mi tak z grubsza o to chodziło, tylko rozciągałem trzy, a nie dwa razy, pozostawiając ścieżkę szerokości 1, tak "środkiem" tego rozszerzonego pola 3x3 w zależności od biegu ścieżki, zamalowanie odpowiedniego pola i odwrócenie tego co zrobiliśmy. następnie należało to zliczyć, i już. Wyślę ci rysunek, może trochę wyjaśni (niestety, niebardzo mam jak go zamieścić).

1 użytkowników online, w tym zalogowanych: 0, gości: 1