Jump to content

Recommended Posts

  • Moderators
Posted

RTFC,

Wow, I expected something complex but this is going to take a fair while to understand. I am now reading this to try and get some idea of what is going on in there.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

  Reveal hidden contents

 

Posted (edited)

@JohnOne: Yeah, best not play too much with it, or you might go blind.;)

@M23: It really works mostly like I describe in the analogy. Rolling the dice = _AskOracle(), using an exponential distribution. We always accept a lower cost, but if it's higher, then we only accept the jump if we sample below our annealing temperature (well technically, cost scaled by temperature), and the temperature itself is gradually lowered. And the name annealing is apt, as the metal is cooled slowly to give the atoms the opportunity to settle into the crystal lattice.

Edited by RTFC
  • 1 month later...
Posted (edited)

Added a 3rd example in the first post, a Sudoku Generator & Solver.

Paraphrasing the script remarks section:

This example illustrates how some types of problem can cause Simulated Annealing to get stuck in a local optimum other than the global one. To get around this, we can apply a thorough reshuffle of all non-fixed parameters, and try again. The harder the problem is, the larger the average number of required reshuffles to find the full solution (see listed examples in script).

Sudoku puzzles with very few given clues (or none) are easy to solve, because many paths exist that lead to full solutions (non-unique for number of clues < 17). Sudoku's with many clues are also easy to solve, because there are only relatively few paths left, many of which yield the full solution.

Sudoku's with (or close to) the minimum number of clues that identify a unique solution are the hardest, because many paths do exist, but most lead to a sub-optimal, incomplete solution.:think: The location of the clues also becomes increasingly important the closer we get to this minimum. Reshuffling allows us to explore this landscape from different starting points.

The new example script (#3) displays a temporary result (with timing) each time it gets stuck; when the true solution is found, it plays a sound before exiting. Note that this may take a long time.:yawn:

NB This is obviously not the fastest way to solve a Sudoku; the point of this example is to show that SimAnn can (eventually) find it, without knowing how to solve it, just by getting feedback on its current attempts, and despite the solution space itself being rather large. Furthermore, this example does not imply that all intractable problems can be solved by repeated reshuffle + retry. For example, it would be useless to attempt to quickly generate bitcoins this way.

 

EDIT: after fixing a bug in updating $totalcost, it turns out sudoku isn't a particularly good example of simulated annealing after all (it just takes too long). Until I find a better way to implement it in this context I've removed this example.

Edited by RTFC
  • 7 years later...
Posted (edited)

Hey, thanks for providing examples :) I would really like to understand, how TSP works (in AutoIt). 

 

I wanted to try to adapt it to a script of mine, but I find it difficult to follow the example and replace individual parts. Since the cities are global and everything is too connected and the variable names are also a bit hard to read for me. (I am not a very experienced hobby developer^^)

I created a script with a more complicated coordinate system using ChatGPT and a lot of time and implemented a "nearest neighbor" algo. To make it a bit better with my limited knowledge, I tried to use it at least twice, hoping that the script wouldn't do something really stupid at least at the first step.

My hope was somewhat that the "understanding" of the coordinate system is mainly in the calculation of the distance and then I can simply adjust the input (the cities) and replace the function for calculating the distance and then I would be able to progress step by step somehow. But that doesn't work with the structure of the example and since my understanding is running close to the limit, I can't really make any progress or even get started. 

Could someone perhaps help me? I think my example code and the visualization could be used to make the TSP example more comprehensible (using visible coordinates and with this kind of visualisation)... I would be really grateful ^^!

 

My code:

#include <Array.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <EditConstants.au3>

Global $iMapWidth = 60, $iMapHeight = 60, $iMapRoot = 3

; Beispielkoordinaten
Local $aInputArray = ["2:50:9", "2:50:2", "2:50:4", "1:50:3", "1:50:8", "1:49:8", "3:49:4", "3:51:3", "3:51:8", "2:51:7", "1:51:4", "2:48:8", "2:48:4", "1:48:3", "1:48:2", "1:48:1", "3:48:4", "4:49:4", "3:50:6", "4:50:4", "4:50:5", "5:51:1"]
Local $sStartCoordinate = "3:51:1"
Local $sHomeCoordinate = "2:50:8"


;~ Local $aBestRoute = getBestRoute($sStartCoordinate, $aInputArray, 5)
Local $aBestRoute = getBestRoute($sStartCoordinate, $aInputArray, 5, $sHomeCoordinate)

DrawMap($aInputArray, $sStartCoordinate, $aBestRoute, $aBestRoute[UBound($aBestRoute) - 1])

Func getBestRoute($sStartCoordinate, $aTargets, $iNumberOfTargets, $sHomeCoordinate = False)
    Local $aBestRoutes[10], $aBestRoute[0]
    Local $sEndCoordinate = $sHomeCoordinate ? $sHomeCoordinate : $sStartCoordinate
    Local $aPossibleFirstTargets = getNearestTargets($sStartCoordinate, $aTargets)
    Local $aTargetsWithoutStartC = $aTargets
    Local $iIdStartCoordInTargets = _ArraySearch($aTargets, $sStartCoordinate) ; Falls ein Ziel auch der Startpunkt ist

    If $iIdStartCoordInTargets >= 0 Then _ArrayDelete($aTargetsWithoutStartC, $iIdStartCoordInTargets) ; Wenn Ziel auch Startpunkt, darf das nicht für die Suche des weiteren Weges genutzt werden

    ; Finde mit den neuen Startkoordinaten die besten Wege
    For $i = 0 To UBound($aPossibleFirstTargets) - 1
        $aBestRoutes[$i] = getShortRoute($aPossibleFirstTargets[$i], $aTargetsWithoutStartC, $iNumberOfTargets - 1, $sEndCoordinate)
    Next

    ; Lösche leere Einträge, falls es weniger als 10 Ziele gab
    For $i = UBound($aBestRoutes) - 1 To 0 Step -1
        If $aBestRoutes[$i] = "" Then
            _ArrayDelete($aBestRoutes, $i)
        EndIf
    Next

    ; Finde die beste Route
    Local $iShortestDistance = -1
    For $i = 0 To UBound($aBestRoutes) - 1
        Local $aTemp = $aBestRoutes[$i]
        $aTemp[1] += getDistance($sStartCoordinate, $aTemp[2])
        Local $iTotalDistance = $aTemp[1]

        ; Vergleiche die Gesamtentfernung mit der bisher kürzesten Route
        If $iShortestDistance = -1 Or $iTotalDistance < $iShortestDistance Then
            $iShortestDistance = $iTotalDistance
            $aBestRoute = $aTemp ; Aktuelle Route als beste Route speichern
        EndIf
    Next

    _ArrayInsert($aBestRoute, 2, $sStartCoordinate)

    Return $aBestRoute ; Die kürzeste Route wird zurückgegeben
EndFunc

; Finde die bis zu 10 nächsten Koordinaten, die alle potenziell gute Start-Koordinaten für die weitere Suche wären
Func getNearestTargets($sStartCoordinate, $aTargets)
    Local $aNearestTargets[0]
    Local $iMaxTargets = UBound($aTargets) > 10 ? 10 : UBound($aTargets)

    For $i = 1 To $iMaxTargets
        Local $iShortestDistance = -1
        Local $sNearestTarget = ""

        For $j = 0 To UBound($aTargets) - 1
            Local $iDistance = getDistance($sStartCoordinate, $aTargets[$j])

            If $iShortestDistance = -1 Or $iDistance < $iShortestDistance Then
                $iShortestDistance = $iDistance
                $sNearestTarget = $aTargets[$j]
            EndIf
        Next

        If $sNearestTarget <> "" Then
            _ArrayAdd($aNearestTargets, $sNearestTarget)
            _ArrayDelete($aTargets, _ArraySearch($aTargets, $sNearestTarget))
        EndIf
    Next

    Return $aNearestTargets
EndFunc

Func getShortRoute($sStartCoordinate, $aInputArray, $iNumberOfTargets, $sEndCoordinate)
    Local $aVisited[0] ; Leeres Array für besuchte Ziele
    Local $iTotalDistance = 0
    Local $sCurrentCoordinate = $sStartCoordinate
    Local $iVisitedTargets = 0

    _ArrayAdd($aVisited, $sStartCoordinate) ; Ist visited, weil das hier ja mit NearestTargets arbeitet, also schon das erste Ziel nach der echten Startkoordinate ist

    Local $iIdOfStartCoordinateInInput = _ArraySearch($aInputArray, $sStartCoordinate)
    If $iIdOfStartCoordinateInInput >= 0 Then _ArrayDelete($aInputArray, $iIdOfStartCoordinateInInput) ; Startkoordinate aus Array für mögliche Ziele entfernen, weil hier dann schon abgelaufen als wirklich erstes Ziel!

    For $i = 1 To $iNumberOfTargets
        Local $iShortestDistance = -1
        Local $sNextCoordinate = ""
        Local $iNextIndex = -1

        For $j = 0 To UBound($aInputArray) - 1
            If Not _ArraySearch($aVisited, $aInputArray[$j]) >= 0 Then
                Local $iDistance = getDistance($sCurrentCoordinate, $aInputArray[$j])

                If $iShortestDistance == -1 Or $iDistance < $iShortestDistance Then
                    $iShortestDistance = $iDistance
                    $sNextCoordinate = $aInputArray[$j]
                    $iNextIndex = $j
                EndIf
            EndIf
        Next

        If $iNextIndex <> -1 Then
            $iTotalDistance += $iShortestDistance
            $sCurrentCoordinate = $sNextCoordinate

            _ArrayAdd($aVisited, $sNextCoordinate)
            _ArrayDelete($aInputArray, $iNextIndex)
            $iVisitedTargets += 1
        Else
            ; Kein weiteres Ziel gefunden, brechen Sie die Schleife ab
            ExitLoop
        EndIf
    Next

    $iTotalDistance += getDistance($aVisited[UBound($aVisited) -1], $sEndCoordinate)

    ; Erstellen Sie das Ergebnisarray
    Local $aResult[2] = [$iVisitedTargets + 1, $iTotalDistance] ; +1, da die Startkoordiante nicht als besuchtes Ziel gilt
    For $i = 0 To $iVisitedTargets
        _ArrayAdd($aResult, $aVisited[$i])
    Next

    _ArrayAdd($aResult, $sEndCoordinate)

    Return $aResult
EndFunc

Func DrawMap($aInputArray, $sStartCoordinate, $aBestRoute, $sEndCoordinate)
    Local $iGridSize = 20
    Local $iGuiWidth = 900
    Local $iGuiHeight = 900
    Local $iLineSpacing = $iGuiWidth / $iGridSize ; Berechnen des Abstands zwischen den Linien

    ; GDI+ initialisieren
    _GDIPlus_Startup()

    ; GUI erstellen
    Local $hGUI = GUICreate("Map Visualization", $iGuiWidth, $iGuiHeight)
    GUISetState(@SW_SHOW)

    ; Grafikobjekt erstellen
    Local $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    Local $hBrushOrange = _GDIPlus_BrushCreateSolid(0xFFFFA500) ; Orange
    Local $hBrushRed = _GDIPlus_BrushCreateSolid(0xFFFF0000) ; Rot
    Local $hBrushBlack = _GDIPlus_BrushCreateSolid(0xFF000000) ; Schwarz
    Local $hBrushGrey = _GDIPlus_BrushCreateSolid(0xFF008000) ; Grau
    Local $hPen = _GDIPlus_PenCreate(0xFF000000, 1) ; Schwarzer Stift mit einer Breite von 1 für das Gitter

    ; Arrays mit 2D-Koordinaten erstellen
    Local $aInputArray2D[0]
    Local $aBestRoute2D[0]

    For $i = 0 To UBound($aInputArray) - 1
        _ArrayAdd($aInputArray2D, convertCoordTo2D($aInputArray[$i]))
    Next

    For $i = 2 To UBound($aBestRoute) - 1
        _ArrayAdd($aBestRoute2D, convertCoordTo2D($aBestRoute[$i]))
    Next

    Local $sMiddle2D = $iGridSize / 2
    Local $aStartCoordinate2D = StringSplit(convertCoordTo2D($sStartCoordinate), ':', 2)
    Local $aEndCoordinate2D = StringSplit(convertCoordTo2D($sEndCoordinate), ':', 2)
    Local $iXDifference = $aStartCoordinate2D[0] - $sMiddle2D
    Local $iYDifference = $aStartCoordinate2D[1] - $sMiddle2D

    For $i = 1 To $iMapWidth * $iMapRoot
        For $j = 1 To $iMapHeight * $iMapRoot
            ; Überprüfen Sie, ob die Koordinate in der besten Route ist
            If _ArraySearch($aBestRoute2D, $i & ":" & $j) >= 0 Then
                Local $iRouteIndex = _ArraySearch($aBestRoute2D, $i & ":" & $j)
                If $iRouteIndex >= 1 Then ; Beachten Sie den Index 1, um den ersten Zielpunkt einzubeziehen
                    _GDIPlus_GraphicsFillRect($hGraphic, ($i - 1 - $iXDifference) * $iLineSpacing, ($j - 1 - $iYDifference) * $iLineSpacing, $iLineSpacing, $iLineSpacing, $hBrushRed)
                    ; Beschriftung mit der Reihenfolge hinzufügen
                    _GDIPlus_GraphicsDrawString($hGraphic, $iRouteIndex & "", ($i - 1 - $iXDifference) * $iLineSpacing + 5, ($j - 1 - $iYDifference) * $iLineSpacing + 5, "Arial", 10, 0, 0xFFFFFFFF)
                EndIf
            ElseIf _ArraySearch($aInputArray2D, $i & ":" & $j) >= 0 Then
;~              ConsoleWrite("Zeichnen aus InputArray:" & @CRLF & $i & ":" & $j & " | " & $aInputArray[_ArraySearch($aInputArray2D, $i & ":" & $j)] & @CRLF)
                ; Koordinate im InputArray, aber nicht in der besten Route
                _GDIPlus_GraphicsFillRect($hGraphic, ($i - 1 - $iXDifference) * $iLineSpacing, ($j - 1 - $iYDifference) * $iLineSpacing, $iLineSpacing, $iLineSpacing, $hBrushOrange)
            EndIf

            ; Endpunkt zeichnen, wenn er nicht die Start-Koordinate ist
            If Not _2DCoordArraysEqual($aStartCoordinate2D, $aEndCoordinate2D) And _2DCoordStringsEqual($i & ":" & $j, convertCoordTo2D($sEndCoordinate)) Then
                _GDIPlus_GraphicsFillRect($hGraphic, ($i - 1 - $iXDifference) * $iLineSpacing, ($j - 1 - $iYDifference) * $iLineSpacing, $iLineSpacing, $iLineSpacing, $hBrushGrey)
                    ; "FIN" in die Koordinaten schreiben
                _GDIPlus_GraphicsDrawString($hGraphic, "FIN", ($i - 1 - $iXDifference) * $iLineSpacing + 5, ($j - 1 - $iYDifference) * $iLineSpacing + 5, "Arial", 10, 0, 0xFFFFFFFF)
            EndIf
        Next
    Next

    ; Startpunkt zeichnen und die Länge des Lösung angebe
    _GDIPlus_GraphicsFillRect($hGraphic, ($sMiddle2D - 1) * $iLineSpacing, ($sMiddle2D - 1) * $iLineSpacing, $iLineSpacing, $iLineSpacing, $hBrushBlack)
    _GDIPlus_GraphicsDrawString($hGraphic, $aBestRoute[1], ($sMiddle2D - 1) * $iLineSpacing + 10, ($sMiddle2D - 1) * $iLineSpacing + 15, "Arial", 12, 0, 0xFFFFFFFF)

    ; Gitter zeichnen
    For $i = 1 To $iGridSize
        For $j = 1 To $iGridSize
            _GDIPlus_GraphicsDrawRect($hGraphic, ($i - 1) * $iLineSpacing, ($j - 1) * $iLineSpacing, $iLineSpacing, $iLineSpacing, $hPen)
        Next
    Next

    ; GUI anzeigen
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop bis der Benutzer die Anwendung schließt.
    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ; Ressourcen aufräumen
    CleanupResources($hBrushOrange, $hBrushRed, $hBrushBlack, $hPen, $hGraphic, $hGUI)
EndFunc

Func CleanupResources($hBrushOrange, $hBrushRed, $hBrushBlack, $hPen, $hGraphic, $hGUI)
    _GDIPlus_BrushDispose($hBrushOrange)
    _GDIPlus_BrushDispose($hBrushRed)
    _GDIPlus_BrushDispose($hBrushBlack)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_Shutdown()
    GUIDelete($hGUI)
EndFunc

Func _2DCoordArraysEqual($coord1, $coord2)
    ; Überprüfen, ob beide Parameter Arrays sind
    If IsArray($coord1) And IsArray($coord2) Then
        ; Überprüfen Sie die Anzahl der Elemente in beiden Arrays
        If UBound($coord1) <> UBound($coord2) Then
            Return False
        EndIf

        ; Vergleichen Sie die Werte in den Arrays
        For $i = 0 To UBound($coord1) - 1
            If $coord1[$i] <> $coord2[$i] Then
                Return False
            EndIf
        Next

        ; Wenn alle Vergleiche erfolgreich sind, geben Sie True zurück
        Return True
    EndIf

    ; Wenn keiner der obigen Fälle zutrifft, geben Sie False zurück
    Return False
EndFunc

Func _2DCoordStringsEqual($coord1, $coord2)
    ; Zerlegen Sie die Koordinaten in Arrays
    Local $aCoord1 = StringSplit($coord1, ':')
    Local $aCoord2 = StringSplit($coord2, ':')

    ; Überprüfen Sie die Anzahl der Elemente in beiden Arrays
    If UBound($aCoord1) <> UBound($aCoord2) Then
        Return False
    EndIf

    ; Vergleichen Sie die Werte in den Arrays
    For $i = 1 To UBound($aCoord1) - 1
        If $aCoord1[$i] <> $aCoord2[$i] Then
            Return False
        EndIf
    Next

    ; Wenn alle Vergleiche erfolgreich sind, geben Sie True zurück
    Return True
EndFunc

Func convertCoordTo2D($sCoord)
    $aCoord = getArrayFromCoordString($sCoord)

    Local $ax = ($aCoord[0][0] - 1) * $iMapRoot + Mod($aCoord[0][2] - 1, $iMapRoot) + 1
    Local $ay = ($aCoord[0][1] - 1) * $iMapRoot + Int(($aCoord[0][2] - 1)/$iMapRoot) + 1

    Return $ax & ":" & $ay
EndFunc

Func convert2DTo3D($sCoord)
    Local $aCoord = StringSplit($sCoord, ':', 2)
    Local $ax = $aCoord[0]
    Local $ay = $aCoord[1]
    Local $ar = Mod(($ax - 1), $iMapRoot) + 1 + Mod(($ay - 1), $iMapRoot) * $iMapRoot
    Local $axx = Ceiling($ax / $iMapRoot)
    Local $ayy = Ceiling($ay / $iMapRoot)
    Return $axx & ":" & $ayy & ":" & $ar
EndFunc

Func getStringFromArrayCoord($aArray)
    Return $aArray[0][0] & ":" & $aArray[0][1] & ":" & $aArray[0][2]
EndFunc

Func getArrayFromCoordString($sString)
    Local $aTmp1 = StringSplit($sString, ':', 2)
    Local $aCoord[1][3] =  [[$aTmp1[0], $aTmp1[1], $aTmp1[2]]]

    Return $aCoord
EndFunc

Func getDistance($sStart, $sTarget)
    Local $aDist[9]

    ; If the start and destination are the same, the distance is 1
    If StringCompare($sStart, $sTarget) = 0 Then Return 1

    Local $aTmp1 = getArrayFromCoordString($sStart), $aTmp2 = getArrayFromCoordString($sTarget)
    Local $x1 = Int($aTmp1[0][0]), $y1 = Int($aTmp1[0][1]), $r1 = Int($aTmp1[0][2])
    Local $x2 = Int($aTmp2[0][0]), $y2 = Int($aTmp2[0][1]), $r2 = Int($aTmp2[0][2])

    $aDist[0] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2 - $iMapWidth) & ':' & ($y2 - $iMapHeight) & ':' & ($r2))
    $aDist[1] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2 - $iMapWidth) & ':' & ($y2)               & ':' & ($r2))
    $aDist[2] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2 - $iMapWidth) & ':' & ($y2 + $iMapHeight) & ':' & ($r2))
    $aDist[3] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2)              & ':' & ($y2 - $iMapHeight) & ':' & ($r2))
    $aDist[4] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2)              & ':' & ($y2)               & ':' & ($r2))
    $aDist[5] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2)              & ':' & ($y2 + $iMapHeight) & ':' & ($r2))
    $aDist[6] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2 + $iMapWidth) & ':' & ($y2 - $iMapHeight) & ':' & ($r2))
    $aDist[7] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2 + $iMapWidth) & ':' & ($y2)               & ':' & ($r2))
    $aDist[8] = getDistanceWithBorder($x1 & ':' & $y1 & ':' & $r1, ($x2 + $iMapWidth) & ':' & ($y2 + $iMapHeight) & ':' & ($r2))

    Return Min9($aDist[0], $aDist[1], $aDist[2], $aDist[3], $aDist[4], $aDist[5], $aDist[6], $aDist[7], $aDist[8])
EndFunc

Func getDistanceWithBorder($sStart, $sTarget)
    Local $aTmp1 = StringSplit($sStart, ':', 2), $aTmp2 = StringSplit($sTarget, ':', 2)

    Local $x1 = Int($aTmp1[0]), $y1 = Int($aTmp1[1]), $r1 = Int($aTmp1[2])
    Local $x2 = Int($aTmp2[0]), $y2 = Int($aTmp2[1]), $r2 = Int($aTmp2[2])

    Local $ax = ($x1 - 1) * $iMapRoot + Mod($r1 - 1, $iMapRoot)
    Local $ay = ($y1 - 1) * $iMapRoot + Int(($r1 - 1)/$iMapRoot)
    Local $bx = ($x2 - 1) * $iMapRoot + Mod($r2 - 1, $iMapRoot)
    Local $by = ($y2 - 1) * $iMapRoot + Int(($r2 - 1)/$iMapRoot)

    Local $iDist = Min2(Abs($ax - $bx), Abs($ay - $by))

    $ax += $ax > $bx ? -$iDist : $iDist
    $ay += $ay > $by ? -$iDist : $iDist

    Return $iDist + Abs($ax - $bx) + Abs($ay - $by)
EndFunc

Func Min($a, $b)
    Return $a < $b ? $a : $b
EndFunc   ;==>Min

Func Min2($a, $b)
    Return $a < $b ? $a : $b
EndFunc

Func Min3($a, $b, $c)
    Return $a < $b ? $a < $c ? $a : $c : $b < $c ? $b : $c
EndFunc

Func Min9($1, $2, $3, $4, $5, $6, $7, $8, $9)
    Return Min3(Min3($1, $2, $3), Min3($4, $5, $6), Min3($7, $8, $9))
EndFunc

Func Max($a, $b)
    Return $a > $b ? $a : $b
EndFunc

A section of the coordinate system to understand it:

;~ ---------------------------  +  ------------------------  +  ------------------------
;~ 60:60:1 | 60:60:2 | 60:60:3  |  1:60:1 | 1:60:2 | 1:60:3  |  2:60:1 | 2:60:2 | 2:60:3
;~ ---------------------------  |  ------------------------  |  ------------------------
;~ 60:60:4 | 60:60:5 | 60:60:6  |  1:60:4 | 1:60:5 | 1:60:6  |  2:60:4 | 2:60:5 | 2:60:6
;~ ---------------------------  |  ------------------------  |  ------------------------
;~ 60:60:7 | 60:60:8 | 60:60:9  |  1:60:7 | 1:60:8 | 1:60:9  |  2:60:7 | 2:60:8 | 2:60:9
;~ ---------------------------  +  ------------------------  +  ------------------------
;~ 60:1:1  | 60:1:2  | 60:1:3   |  1:1:1  | 1:1:2  | 1:1:3   |  2:1:1  | 2:1:2  | 2:1:3
;~ ---------------------------  |  ------------------------  |  ------------------------
;~ 60:1:4  | 60:1:5  | 60:1:6   |  1:1:4  | 1:1:5  | 1:1:6   |  2:1:4  | 2:1:5  | 2:1:6
;~ ---------------------------  |  ------------------------  |  ------------------------
;~ 60:1:7  | 60:1:8  | 60:1:9   |  1:1:7  | 1:1:8  | 1:1:9   |  2:1:7  | 2:1:8  | 2:1:9
;~ ---------------------------  +  ------------------------  +  ------------------------
;~ 60:2:1  | 60:2:2  | 60:2:3   |  1:2:1  | 1:2:2  | 1:2:3   |  2:2:1  | 2:2:2  | 2:2:3
;~ ---------------------------  |  ------------------------  |  ------------------------
;~ 60:2:4  | 60:2:5  | 60:2:6   |  1:2:4  | 1:2:5  | 1:2:6   |  2:2:4  | 2:2:5  | 2:2:6
;~ ---------------------------  |  ------------------------  |  ------------------------
;~ 60:2:7  | 60:2:8  | 60:2:9   |  1:2:7  | 1:2:8  | 1:2:9   |  2:2:7  | 2:2:8  | 2:2:9
;~ ---------------------------  +  ------------------------  +  ------------------------

Maybe this one helps either, to understand it:
 

#include <Array.au3>

$aTable = Create(60, 60, 3) ; Zum Anzeigen des ganzen Systems als Array
_ArrayDisplay($aTable)

Func Create($iTableHeight = 3, $iTableWidth = 3, $iTableElementsRoot = 3)
    Local $aRet[$iTableHeight * $iTableElementsRoot][$iTableWidth * $iTableElementsRoot]
    For $y = 0 To $iTableHeight - 1 Step 1
        For $x = 0 To $iTableWidth - 1 Step 1
            For $i = 1 To $iTableElementsRoot ^ 2 Step 1
                $aRet[$y * $iTableElementsRoot + Int(($i-1)/$iTableElementsRoot)][$x * $iTableElementsRoot + Mod($i - 1, $iTableElementsRoot)] = $i
            Next
        Next
    Next
    Return $aRet
EndFunc

 

Edited by Acanis
Posted (edited)

@Acanis: As they say in German: "Da gibt's noch Luft  nach oben." :D You're making things faaaaaar too complicated (I'm not even going to go into how you're setting up/evaluating your 3D/2D grid...:huh2:). Was this ChatGPT's idea? But you earn some points for at least annotating your code. Or was that ChatGPT as well?

I edited the TSP example on the assumption that the desired number of intermediate points is fixed, and point duplication is not allowed. You can play with $tempfactor (and $maxStepsWithoutImprovement) to adjust how much exploration of the solution space is allowed. If $verbose = true (default false now), the best sequence of journey legs so far is ArrayDisplayed every time a better solution is found (press <Esc> (every time) to continue); press <Space> to terminate prematurely.

This is all I'm going to write for you here; visualisation and any changes you'll have to implement yourself.:P Hope it helps.

  Reveal hidden contents

 

Edited by RTFC
minor efficiency optimisation in code
Posted (edited)
  On 3/5/2024 at 3:34 PM, RTFC said:

@Acanis: As they say in German: "Da gibt's noch Luft  nach oben." :D You're making things faaaaaar too complicated (I'm not even going to go into how you're setting up/evaluating your 3D/2D grid...:huh2:). Was this ChatGPT's idea? But you earn some points for at least annotating your code. Or was that ChatGPT as well?

I edited the TSP example on the assumption that the desired number of intermediate points is fixed, and point duplication is not allowed. You can play with $tempfactor (and $maxStepsWithoutImprovement) to adjust how much exploration of the solution space is allowed. If $verbose = true (default false now), the best sequence of journey legs so far is ArrayDisplayed every time a better solution is found (press <Esc> (every time) to continue); press <Space> to terminate prematurely.

This is all I'm going to write for you here; visualisation and any changes you'll have to implement yourself.:P Hope it helps.

Expand  

Hehe, Iam german and your right... ;) But I try to improve :D

The grid stuff is really old and I got some help of a more experienced programmer to finish it... ^^ (And I liked it, because it was there and understandable for me^^) ChatGPT did help me with the visualisation, because I never used GDI+ before ^^ I try to annotate my code, but ChatGPT did help, too :D

I planned the desired number of intermediate points to be an input to the function. But if Iam able to understand your code, Ill try to change that myself. :)

In any case, MANY THANKS in advance!

Ill add the visualisation stuff later and test it a lot to understand the code and the impact of the parameters. :) Really cool! :)

Edited by Acanis
Posted (edited)

I still dont understand everything, but working on it :) I reduced the global variables, put all the stuff into an function and renamed a lot of the variables. Maybe its worse for experienced eyes, but it helps me a lot :D

It seems that after the refactoring everything still works as before and I was able to customize it so that the drawing function can be used again without further customization. 

Ill play around with the parameters and compare the solutions with the one of my own try; but it feels pretty good for the example data :)! Ill post the updated code, so other interested people can use it :)
 

  Reveal hidden contents

 

Edited by Acanis
Posted (edited)

In path searches through a subset of points more generally (for simulated annealing or any other time-consuming algorithm), it may be of significant advantage (speedwise, that is) to compute all point-to-point distances in advance, and store these in a single look-up reference (table or matrix). Here's a simple, generic example of doing just that using matrices:

  Reveal hidden contents

This example is, however, far from optimised. One improvement would be to get rid of the final column vector copy to the output matrix, and instead repeatedly re-map a column in the final output as our vector to directly collect results in.

  Reveal hidden contents

This is faster, but still evaluates each point individually. A more efficient approach would be to create separate matrices for each coordinate dimension, and perform each math operation once per dimension only, collecting final results in the first dimension's container. Note that this much faster solution requires more memory.

  Reveal hidden contents

 

Edited by RTFC
more examples added

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...