Avatar billede tjacob Juniormester
22. oktober 2007 - 13:23 Der er 13 kommentarer og
2 løsninger

Et lille -matematisk? problem

Dette problem handler ganske vist ikke så meget om VB.NET, men det skal bruges i et program jeg er ved at lave, så..........

Mit spørgsmål er: Hvordan finder man "øer" af ens værdi i en given 2-dimensionel matrix.

Da Eksperten har (meget) begrænsede muligheder for formatering, har jeg lagt en webside op, der beskriver problemet mere detaljeret her:

http://www.home1.stofanet.dk/5soft/v9cxjk4l/EkspSpm.html

/tjacob
Avatar billede montago Praktikant
22. oktober 2007 - 13:45 #1
jeps --- kig på www.mdk-photo.com/Blocks

kode - www.mdk-photo.com/Blocks/source.js

her looper jeg igennem rekursivt for at finde blokke som har naboer op,ned,ven,høj -- men du kan udbygge til at finde med diagonalerne...

hvordan du finder ud af om det er en 'ø' er lidt tricky...
Avatar billede montago Praktikant
22. oktober 2007 - 13:58 #2
x,y er koodinatet du starter på
this.rows og this.cols er matricens max størrelse - kanten om man vil
this.list er et array af arrays som holder de pladser som ER fundet.
jeg har lavet en prototype funktion på Array som hedder find() -- lav en funktion som kigger dit array igennem om den plads du kigger efter allerede eksistere.

så hvidt jeg husker, lægger jeg det første element i this.list inden jeg går igang, dette for at initialiere den "farve" jeg kigger efter.

    this.crawlRecurse = function( x, y ){
        if( (x+1) < this.rows )
            if( this.fromXY(x,y) == this.fromXY(x+1,y) && !this.list.find( new Array( (x+1),y) ) ){
                this.calcs++;
                this.list.push( new Array( Number(x+1),Number(y) ) )
                this.crawlRecurse(x+1,y)
            }
        if( (x-1) >= 0 )
            if( this.fromXY(x,y) == this.fromXY(x-1,y) && !this.list.find( new Array((x-1) ,y) ) ){
                this.calcs++;
                this.list.push( new Array( Number(x-1) ,Number(y) ) )
                this.crawlRecurse(x-1,y)
            }
        if( (y+1) < this.cols )
            if( this.fromXY(x,y) == this.fromXY(x,y+1) && !this.list.find( new Array(x,(y+1)) ) ){
                this.calcs++;
                this.list.push( new Array( Number(x),Number(y+1)) )
                this.crawlRecurse(x,y+1)
            }   
        if( (y-1) >= 0 )
            if( this.fromXY(x,y) == this.fromXY(x,y-1) && !this.list.find( new Array(x,(y-1)) ) ){
                this.calcs++;
                this.list.push( new Array(Number(x),Number(y-1)) )
                this.crawlRecurse(x,y-1)
            }
        else
            return
    }
Avatar billede tjacob Juniormester
22. oktober 2007 - 14:18 #3
Tak for svarene montago.

Som jeg læser det (jeg er ikke nogen ørn til Java), så finder din funktion sammenhængende blokke af samme værdi.
Det kan jeg godt bruge, men det løser ikke mit problem:

Da jeg skal finde øer, som er omkranset af blokke med samme værdi, er jeg nødt til at have diagonalerne med. Det kan din funtion så godt udvides med, men der skal skelnes mellem diagonalerne (ja, faktisk også de regulære naboer), da ikke alle har relevans.

Her er et udsnit af det eksempel jeg viser i ovenstående link -Jeg har erstattet de relevante nuller med #, og de ikke-relevante med @:

839###1@44
92#627#922
13#554#365
266#6#1628
2@3##49601
3@@4@@2800

En funktion som din (med diagonaler) ville finde alle # og @. Hvordan skelner jeg nu imellem dem? Og hvordan finder jeg ud af om det er et "lukket" område?
Avatar billede montago Praktikant
22. oktober 2007 - 14:25 #4
som sagt det er lidt tricky...

med en opgraderet version, som tjekker på diagonaler, skal du blot lave din "find" funktion om, sådan at den tjekke på 'linie-bredden' ...
fx er disse næppe med på en linie:
##
##

man skal derfor se, om det felt du tjekke på vil gøre linien "fed" eller fortsætte den

##
#? <-- false

###? <-- true

##
#
? <-- true

osv....


Jeg tror man kan bruge euklid til at finde ø'er i en matrice... med distance vektorer... det er lidt langhåret.. og du kommer nok længst ved at lave "find" algoritmen sådan at man tjekke på om man er gået i ring...
Avatar billede pidgeot Nybegynder
22. oktober 2007 - 14:25 #5
Umiddelbart forestiller jeg mig problemets løsning sådan her:

Du har et array du looper igennem alle elementer i.
Når du finder et match til en given værdi tilføjer du koordinatet til et array (eller en stak, eller hvad du nu ender med at bruge), og kalder en hjælpemetode.

Hjælpemetoden looper alle naboer igennem, dog springer den over det element vi fandt umiddelbart før det aktuelle (så går vi ikke i en uendelig løkke, og finder ikke startpositionen allerførste gang). Når den finder et match, tilføjer den koordinatet til et array hvis det er forskelligt fra startpositionen. Hvis det er det samme som startpositionen, så har du en ø.
Finder du intet match, fjerner du det aktuelle punkt og går et trin tilbage (ved at returnere).
Avatar billede montago Praktikant
22. oktober 2007 - 14:42 #6
good point... FIND algoritmen skal selvfølgelig ikke tjekke det første element hvis afstanden er større end fx 2*rod2...

( kvadratrod( ny_x*ny_x + ny_y*ny_y) > 1,414 ) == true
Avatar billede tjacob Juniormester
22. oktober 2007 - 14:43 #7
OK, jeg kan godt se en skitse til en løsning nu. Nu skal det bare kodes ;)
Det kan godt tage lidt tid, så jeg vender måske tilbage med uddybende spørgsmål.
Avatar billede montago Praktikant
22. oktober 2007 - 15:07 #8
hvis man som pidgeot, vil kigge med look-ahead og look-behind, er man næsten nødt til at oprette et nyt array hver gang man finder et "T-kryds", så kalder man rekursivt for hver ny vej man går - og den/de veje som resultere i en ring, returneres... hvilket kan blive lidt af et problem :)
Avatar billede pidgeot Nybegynder
22. oktober 2007 - 15:32 #9
Det afhænger i høj grad af implementationen.

Bruger man eks. en stak som grundlag (hvilket jeg nævner som en mulighed), undgår man flere arrays ved hjælp af korrekt brug af push og pop - man kan blot sende stakken med som en reference, og dermed undgå en hel masse arrays der skal oprettes. Da det er .NET, har man endda en Stack-klasse klar i forvejen (System.Collections.Stack), og kan blot kalde ToArray når man har et match.

Ved at gemme hvor i "naboløkken" man er nået til sammen med positionen for punktet, er man endda rigtigt godt på vej til at have lavet den om til en iterativ løsning - men det er lidt nemmere at forklare tankegangen til dette problem ved at udtrykke det rekursivt.

Man skal naturligvis kunne håndtere dubletter på en eller anden måde, men det kan jeg ikke se man kommer udenom under alle omstændigheder.

Det eneste man "mangler" ift. .NET's egen Stack er muligheden for at se det element der kom på som det første - men det kan man jo håndtere på mange måder, også uden at skulle pille ved Stack'en.
Avatar billede montago Praktikant
22. oktober 2007 - 15:50 #10
pidgeot - dvs man både skal have et lookup-array og en stak ?... medmindre man kan læse stakken som array..

forslag : ArrayList :)
så kan man jo altid lave en Slice(#) for at fjerne en gren som ikke blev til noget
Avatar billede pidgeot Nybegynder
22. oktober 2007 - 15:53 #11
Som allerede nævnt har Stack en ToArray metode der henter indholdet som et array.
Avatar billede tjacob Juniormester
23. oktober 2007 - 11:17 #12
Hej igen. Så har fået jeg lavet en funtion, den kunne sikkert være kortere og smartere, men den virker.
Jeg har dog stadig et enkelt problem mht at identificere øen, (dvs når rekursen når tilbage til startcellen). Som det er nu har jeg lavet en tæller, jeg forlanger skal være >8 (Max antal naboer=0), men det er ikke optimalt da der kan være øer så små som 4 celler:
#
#0#
#

Nogen ideer (med udgangspunkt i nedenstående)?

    Private Sub FindFirstBlock()

        Dim Block As New List(Of Point)
        Dim i, j As Integer
        For j = 0 To 9
            For i = 0 To 9
                If OrgMatrix(i, j) = 0 Then
                    FirstCell = New Point(i, j)
                    If Not Block Is Nothing Then Block.Clear()
                    Block.Add(New Point(i, j))
                    BlockFound = False
                    Count = 0
                    Call FindNeighbours(i, j, Block)
                    If BlockFound Then Exit For
                End If
            Next i
            If BlockFound Then Exit For
        Next j

    End Sub

OrgMatrix, FirstCell, Count og BlockFound er erklæret på modulniveau

Private Sub FindNeighbours(ByVal x As Integer, ByVal y As Integer, ByRef pList As List(Of Point))

        Dim iMaxX As Integer = OrgMatrix.GetUpperBound(0)
        Dim iMaxY As Integer = OrgMatrix.GetUpperBound(1)
        Dim pNewPoint As Point
        Dim bExists As Boolean = False
        Count += 1
        If x > 0 And y > 0 Then
            pNewPoint = New Point(x - 1, y - 1)
            If Not pNewPoint = FirstCell Then
                bExists = False
                If OrgMatrix(x - 1, y - 1) = 0 Then
                    ' Tjek om den er på listen i forvejen:
                    For Each aPoint As Point In pList
                        If pNewPoint = aPoint Then
                            bExists = True
                            Exit For
                        End If
                    Next aPoint
                    If Not bExists Then
                        ' Tilføj til listen ,rekurser:
                        pList.Add(New Point(x - 1, y - 1))
                        Call FindNeighbours(x - 1, y - 1, pList)
                    End If
                End If
            Else
                ' Vi er tilbage ved den første celle:
                If Count > 8 Then
                    BlockFound = True
                    Exit Sub
                End If
            End If
        End If

******Her følger 7 If-blokke magen til ovenstående, der finder de andre naboer.

End Sub


Jeg mangler så blot at identificere 'falske' celler, samt cellerne inden i øen, men det er detaljer, der sagtens kan laves.
Avatar billede tjacob Juniormester
23. oktober 2007 - 11:30 #13
Formatteringen smuttede lidt ved mit eksempel på en 4-ø:

Den skal naturligvis se sådan ud:
1#1
#0#
1#1
Avatar billede tjacob Juniormester
23. oktober 2007 - 12:00 #14
Jeg fik selv løst det sidste problem ved at læse pidgeots første kommentar:
"dog springer den over det element vi fandt umiddelbart før det aktuelle"

Så nu ser funktionen sådan ud:

(Med PreviousCell erklæret på modulniveau)

    Private Sub FindNeighbours(ByVal x As Integer, ByVal y As Integer, ByRef pList As List(Of Point))

        Dim iMaxX As Integer = OrgMatrix.GetUpperBound(0)
        Dim iMaxY As Integer = OrgMatrix.GetUpperBound(1)
        Dim pNewPoint As Point
        Dim bExists As Boolean = False
        If x > 0 And y > 0 Then
            pNewPoint = New Point(x - 1, y - 1)
            If Not pNewPoint = PreviousCell Then
                If Not pNewPoint = FirstCell Then
                    bExists = False
                    If OrgMatrix(x - 1, y - 1) = 0 Then
                        ' Tjek om den er på listen i forvejen:
                        For Each aPoint As Point In pList
                            If pNewPoint = aPoint Then
                                bExists = True
                                Exit For
                            End If
                        Next aPoint
                        If Not bExists Then
                            ' Tilføj til listen ,rekurser:
                            PreviousCell = pList.Item(pList.Count - 1)
                            pList.Add(New Point(x - 1, y - 1))
                            Call FindNeighbours(x - 1, y - 1, pList)

                        End If
                    End If
                Else
                    ' Vi er tilbage ved den første celle:
                    BlockFound = True
                    Exit Sub
                End If
            End If
        End If

******Her følger 7 If-blokke magen til ovenstående, der finder de andre naboer.

End Sub

I skal begge have tak for hjælpen, jeg kan selv komme videre herfra. Jeg har taget kraftigt udgangspunkt i montagos funktion, så jeg synes han skal have de fleste points, men pidgeot var i høj grad medvirkende, så vil du lægge et svar pidgeot?
Avatar billede pidgeot Nybegynder
23. oktober 2007 - 12:01 #15
S'gerne :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester



IT-JOB

Udviklings- og Forenklingsstyrelsen

Controller til økonomi og compliance

MAN Energy Solutions

Department Manager Edge Platform

Udviklings- og Forenklingsstyrelsen

Scrum Master