Springerproblem Algorithmus

    • VB.NET

    Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von nafets3646.

      Springerproblem Algorithmus

      Aufgrund von Marcus seinem Posting in diesem Thread dachte ich, dass das doch mal ein interessantes Problem sein könnte. Also habe ich mir einen kleinen auf Rekursion basierenden Algorhytmus gebastelt.
      Das Ziel der Aufgabe ist, mit einem Springer, der auf einem Schachbrett beliebiger Größe an einem beliebigen Punkt steht, alle Felder des Schachbretts einmal zu betreten, keines darf doppelt betreten werden.
      Der Ablauf ist immer folgender:
      -Die aktuelle Position des Springers wird übergeben
      -Alle möglichen Züge werden durchprobiert bis ein möglicher Zug gefunden wurde
      -Der Zug wird ausgeführt (auf den Stack gelegt) und der Ablauf startet von vorne
      -Wenn kein möglicher Zug gefunden wurde, wird einen Schritt zurück gegangen (der letzte Zug vom Stack genommen) und der Ablauf startet auch wieder von vorne
      -Wenn die Aufgabe erfüllt ist (alle Felder abgefahren), wird der Vorgang abgebrochen.

      Hier jedenfalls mein Ansatz:

      VB.NET-Quellcode

      1. Public Class Algorythm
      2. ''' <summary>
      3. ''' Eine Auflistung aller möglichen Bewegungen eines Springers
      4. ''' </summary>
      5. Private Shared MöglicheZüge As New List(Of Integer()) From {New Integer() {1, -2}, New Integer() {-1, -2}, New Integer() {2, 1}, New Integer() {2, -1}, _
      6. New Integer() {-1, 2}, New Integer() {1, 2}, New Integer() {-2, -1}, New Integer() {-2, 1}}
      7. ''' <summary>
      8. ''' Löst das Springerproblem
      9. ''' </summary>
      10. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      11. ''' <param name="buffer">Der Puffer für die Spielzüge.</param>
      12. Private Shared Sub Springerproblem(ByVal fieldSize As Size, ByRef buffer As Stack(Of Point))
      13. 'Geht alle Züge durch
      14. For Each Zug In MöglicheZüge
      15. 'Wenn die Lösung schon gefunden wurde, Vorgang abbrechen
      16. If buffer.Count = fieldSize.Width * fieldSize.Height Then
      17. Exit Sub
      18. End If
      19. 'Berechnet das Ziel des aktuell ausgewählten Zuges
      20. Dim ptZug As Point = New Point(buffer.Peek.X + Zug(0), buffer.Peek.Y + Zug(1))
      21. 'Überprüfen ob der aktuelle Zug möglich ist
      22. If ((ptZug.X <= fieldSize.Width AndAlso ptZug.X >= 1) AndAlso (ptZug.Y <= fieldSize.Height AndAlso ptZug.Y >= 1)) AndAlso (Not buffer.Contains(ptZug)) Then
      23. 'Zug zum Puffer hinzufügen
      24. buffer.Push(ptZug)
      25. 'Nächste Rekursionsstufe aufrufen
      26. Springerproblem(fieldSize, buffer)
      27. End If
      28. Next
      29. 'Wenn keine Bewegungsmöglichkeit gefunden wurde, einen Schritt zurück gehen
      30. buffer.Pop()
      31. End Sub
      32. ''' <summary>
      33. ''' Löst das Springerproblem
      34. ''' </summary>
      35. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      36. ''' <param name="start">Der Startpunkt des Springers.</param>
      37. ''' <returns>Die Züge die durchzuführen sind.</returns>
      38. Public Shared Function Springerproblem(ByVal fieldSize As Size, start As Point) As Stack(Of Point)
      39. 'Der Puffer in dem später alle Züge gespeichert werden
      40. Dim Buffer As New Stack(Of Point)
      41. 'Den Startpunkt zum Puffer hinzufügen
      42. Buffer.Push(start)
      43. 'Die Berechnung starten
      44. Springerproblem(fieldSize, Buffer)
      45. 'Wenn alle Berechnungen abgeschlossen sind, den Puffer zurückgeben
      46. Return Buffer
      47. End Function
      48. End Class

      nafets3646 schrieb:

      der auf einem Schachbrett beliebiger Größe an einem beliebigen Punkt steht
      Fangen wir mit einem 3x3-Brett und der Mittelposition an. :D
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Es gibt noch eine Heuristik, dass der Springer, wenn es mehrere legale Möglichkeiten gibt, immer den Zug machen sollte, von dem aus er beim nächsten Zug am wenigsten legale Möglichkeiten hat. Wenn es davon mehrere mit derselben Anzahl gibt, würde ich den Zufall entscheiden lassen.

      Das könntest du noch implementieren, der Algorithmus sollte dann wesentlich schneller sein.
      Ich hab das jetzt mal umgesetzt, allerdings ist es noch deutlich langsamer als die erste Methode, um genau zu sein, ist es immer ungefähr um den Faktor 3. Getestet habe ich mit 6x6-Feldern und einem zufällig generierten Startpunkt für beide Methoden.
      Hier ist jedenfalls der Code:

      VB.NET-Quellcode

      1. Public Class AlgorythmV2
      2. ''' <summary>
      3. ''' Löst das Springerproblem
      4. ''' </summary>
      5. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      6. ''' <param name="buffer">Der Puffer für die Spielzüge.</param>
      7. Private Shared Sub Springerproblem(ByVal fieldSize As Size, ByRef buffer As Stack(Of Point))
      8. 'Alle möglichen Züge durchgehen
      9. For Each Zug In GetPossibleMovesSorted(fieldSize, buffer)
      10. 'Wenn die Lösung schon gefunden wurde, Vorgang abbrechen
      11. If buffer.Count = fieldSize.Width * fieldSize.Height Then
      12. Exit Sub
      13. End If
      14. 'Den Zug zum Puffer hinzufügen
      15. buffer.Push(Zug)
      16. 'Prüfen ob hiermit die Aufgabe gelöst ist
      17. If buffer.Count = fieldSize.Width * fieldSize.Height Then
      18. 'Wenn ja, die Sub verlassen
      19. Exit Sub
      20. Else
      21. 'Wenn nein, nächste Rekursionsstufe aufrufen
      22. Springerproblem(fieldSize, buffer)
      23. End If
      24. Next
      25. 'Wenn keine Bewegungsmöglichkeit gefunden wurde, einen Schritt zurück gehen
      26. buffer.Pop()
      27. End Sub
      28. ''' <summary>
      29. ''' Berechnet alle möglichen Züge und gibt diese sortiert zurück.
      30. ''' </summary>
      31. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      32. ''' <param name="buffer">Der Puffer für die Spielzüge</param>
      33. ''' <returns>Alle möglichen Züge, sortiert nach Relevanz.</returns>
      34. Private Shared Function GetPossibleMovesSorted(ByVal fieldSize As Size, ByVal buffer As Stack(Of Point)) As List(Of Point)
      35. 'Alle möglichen Züge mit Anzahl der als nächsten Schritt möglichen Züge
      36. Dim MöglicheZüge As New List(Of Tuple(Of Point, Integer))
      37. 'Alle möglichen Züge, sortiert nach Relevanz
      38. Dim MöglicheZügeSortiert As New List(Of Point)
      39. 'Alle möglichen Züge durchgehen
      40. For Each Zug As Point In GetPossibleMoves(fieldSize, buffer)
      41. 'Den Zügen ein neues Element hinzufügen: Ziel des Zuges, mögliche darauffolgende Züge
      42. MöglicheZüge.Add(Tuple.Create(Zug, GetPossibleMoves(fieldSize, buffer, Zug).Count))
      43. Next
      44. 'Die möglichen Züge nach der Anzahl der möglichen darauffolgenden Züge absteigend sortieren
      45. MöglicheZüge.OrderByDescending(Function(t) t.Item2)
      46. 'Alle sortierten Züge zur Liste 'MöglicheZügeSortiert' hinzufügen
      47. MöglicheZügeSortiert = MöglicheZüge.Select(Function(t) t.Item1).ToList
      48. 'Die möglichen Züge sortiert zurückgeben
      49. Return MöglicheZügeSortiert
      50. End Function
      51. ''' <summary>
      52. ''' Eine Auflistung aller möglichen Bewegungen eines Springers
      53. ''' </summary>
      54. Private Shared Züge As New List(Of Integer()) From {New Integer() {1, -2}, New Integer() {-1, -2}, New Integer() {2, 1}, New Integer() {2, -1}, _
      55. New Integer() {-1, 2}, New Integer() {1, 2}, New Integer() {-2, -1}, New Integer() {-2, 1}}
      56. ''' <summary>
      57. ''' Berechnet alle möglichen Züge.
      58. ''' </summary>
      59. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      60. ''' <param name="buffer">Der Puffer für die Spielzüge</param>
      61. ''' <returns>Alle möglichen Züge.</returns>
      62. Private Shared Function GetPossibleMoves(ByVal fieldSize As Size, ByVal buffer As Stack(Of Point)) As List(Of Point)
      63. Dim MöglicheZüge As New List(Of Point)
      64. 'Alle Züge durchgehen
      65. For Each Zug As Integer() In Züge
      66. 'Ziel des Zuges berechnen
      67. Dim ptZug As Point = New Point(buffer.Peek.X + Zug(0), buffer.Peek.Y + Zug(1))
      68. 'Prüfen, ob Zug möglich
      69. If ((ptZug.X <= fieldSize.Width AndAlso ptZug.X >= 1) AndAlso (ptZug.Y <= fieldSize.Height AndAlso ptZug.Y >= 1)) AndAlso (Not buffer.Contains(ptZug)) Then
      70. 'Wenn Zug möglich, Zug zur Liste hinzufügen
      71. MöglicheZüge.Add(ptZug)
      72. End If
      73. Next
      74. 'Die möglichen Züge zurückgeben
      75. Return MöglicheZüge
      76. End Function
      77. ''' <summary>
      78. ''' Berechnet alle möglichen Züge.
      79. ''' </summary>
      80. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      81. ''' <param name="buffer">Der Puffer für die Spielzüge</param>
      82. ''' <param name="position">Ein zusätzlicher Punkt</param>
      83. ''' <returns>Alle möglichen Züge.</returns>
      84. Private Shared Function GetPossibleMoves(ByVal fieldSize As Size, ByVal buffer As Stack(Of Point), ByVal position As Point) As List(Of Point)
      85. 'Den zusätzlichen Punkt zum Stack hinzufügen und den Rückgabewert der Funktion GetPossibleMoves(fieldSize, buffer) zurückgeben
      86. Return GetPossibleMoves(fieldSize, New Stack(Of Point)(buffer.Concat({position})))
      87. End Function
      88. ''' <summary>
      89. ''' Löst das Springerproblem
      90. ''' </summary>
      91. ''' <param name="fieldSize">Die Größe des Schachfeldes.</param>
      92. ''' <param name="start">Der Startpunkt des Springers.</param>
      93. ''' <returns>Die Züge die durchzuführen sind.</returns>
      94. Public Shared Function Springerproblem(ByVal fieldSize As Size, ByVal start As Point) As Stack(Of Point)
      95. 'Der Puffer in dem später alle Züge gespeichert werden
      96. Dim Buffer As New Stack(Of Point)
      97. 'Den Startpunkt zum Puffer hinzufügen
      98. Buffer.Push(start)
      99. 'Die Berechnung starten
      100. Springerproblem(fieldSize, Buffer)
      101. 'Wenn alle Berechnungen abgeschlossen sind, den Puffer zurückgeben
      102. Return Buffer
      103. End Function
      104. End Class

      Weiß jemand vielleicht, woran das liegen könnte, dass dieser Code so lange braucht?
      Ich weiß das ganze gehört wohl schon zum alten Eisen, aber wenn man mal etwas langeweile haben sollte, wäre eine grafische Darstellung wohl Nett anzusehen
      (Auch wenn das jetzt nicht wirklich Spektakulär wäre, wie sich da ein paar Felder einfärben und/oder eine Figur sich bewegt).
      Ich bin zwar kein Deutschlehrer, aber bitte: A-l-g-o-r-i-t-h-m-u-s :D
      Und auf Englisch: Algorithm ;)

      Desweiteren mal zum Code, ich empfehle dir, dich für eine Sprache zu entscheiden, Deutsch oder Englisch :)
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais