Bezug: Diese Knobelei (ist dann doch noch eine "richtige" Knobelei geworden)
Problem
Gegeben ist ein bestimmtes Quadrat, und 3 Winkel, die ein 3-eck definieren.
Gesucht ist nun die Position des 3-ecks innerhalb des Quadrats, bei dem es maximale Größe einnehmen kann.
Lösung durch Annäherung
Die Winkel werden sortiert. Der kleinste Winkel kommt in die Quadrat-Ecke obenLinks, der 2. Winkel kommt zunächstmal in die Q-Ecke obenRechts. Dadurch liegt die lange Seite des 3-Ecks auf der oberen Quadrat-Kante, und die 3.Ecke ist mit Sicherheit im Quadrat.
Wenn nun die rechte Ecke senkrecht runter wandert bis zur UntenRechts-Ecke des Quadrats, so verlängert sich die lange Seite, und das 3-eck vergrößert sich. Dabei kann (muss nicht) die 3.Ecke auch ausserhalb des Quadrates geraten. Also muss einfach der rechte 3-Eckpunkt gefunden werden, bei dem die sich daraus ergebende 3. Ecke grade noch innerhalb des Quadrats liegt.
meine Lösung
sieht hübsch aus, finde ich:
Gezeichnet wird das Quadrat, eine Folge von immer größeren 3-ecken, und das optimale 3-eck, also das größtmögliche, was noch ins Quadrat hineingeht:
Bisektion
informatisch interessant ist v.a. das Annäherungs-Verfahren "Bisektion":
es wird ein Tripel gebildet,
In einer Schleife wird der Testwert getestet, und je nach Ergebnis wird entweder das Minimum rauf- oder das Maximum runter-gesetzt, auf den Testwert (#7).
Anschließend wird der Testwert neu festgelegt, einfach als Mittel aus Min und Max.
Die gezeigte Bisektion-Schleife ist also stark verallgemeinerbar - man bräuchte nur das Testverfahren auszutauschen, um iwelche anneren Sachen zu approximieren.
(Edit: Ich glaub jedenfalls, dasses "Bisektion" heißt. Ist prinzipiell dasselbe wie eine binäre Suche, nur sucht nicht in einer sortierten Menge diskreter Elemente, sondern sucht in einem Kontinuum.)
Edit: hglhyr fand schließlich noch einen viel einfacheren Zusammenhang, der diese Approximation üflüssig gemacht hätte: alle 3. Ecken liegen auf einer Geraden. (Hab diese Grade nun gelb eingezeichnet.)
Problem
Gegeben ist ein bestimmtes Quadrat, und 3 Winkel, die ein 3-eck definieren.
Gesucht ist nun die Position des 3-ecks innerhalb des Quadrats, bei dem es maximale Größe einnehmen kann.
Lösung durch Annäherung
Die Winkel werden sortiert. Der kleinste Winkel kommt in die Quadrat-Ecke obenLinks, der 2. Winkel kommt zunächstmal in die Q-Ecke obenRechts. Dadurch liegt die lange Seite des 3-Ecks auf der oberen Quadrat-Kante, und die 3.Ecke ist mit Sicherheit im Quadrat.
Wenn nun die rechte Ecke senkrecht runter wandert bis zur UntenRechts-Ecke des Quadrats, so verlängert sich die lange Seite, und das 3-eck vergrößert sich. Dabei kann (muss nicht) die 3.Ecke auch ausserhalb des Quadrates geraten. Also muss einfach der rechte 3-Eckpunkt gefunden werden, bei dem die sich daraus ergebende 3. Ecke grade noch innerhalb des Quadrats liegt.
VB.NET-Quellcode
- Public Class frmTriangleInSquare
- Private _Square As New RectangleF(150, 80, 480, 480)
- Private _SquarePen As New Pen(Brushes.Blue, 2)
- Private _Angles As New List(Of Double) From {45, 60, 75}
- Public Sub New()
- InitializeComponent()
- ListBox1.DataSource = _Angles
- End Sub
- Private Sub PrepareAngles()
- Dim s = Microsoft.VisualBasic.InputBox("Komma-getrennt 2 Winkel eingeben, in Deg (nur Ganzzahlen)", DefaultResponse:=String.Join(" , ", _Angles.Take(2)))
- If s.Length = 0 Then Return
- Try
- _Angles = s.Split(","c).Select(AddressOf Double.Parse).ToList
- If _Angles.Count <> 2 Then Throw New Exception("ungültige Anzahl Winkel!")
- Dim third = 180 - _Angles.Sum
- If third < 0 Then Throw New Exception("Winkelsumme muss kleiner 180 sein!")
- _Angles.Add(third)
- _Angles.Sort()
- Catch ex As Exception
- MessageBox.Show(ex.Message, "Fail")
- Return
- End Try
- End Sub
- Private Sub AnyMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles TestToolStripMenuItem.Click
- PrepareAngles()
- ListBox1.DataSource = _Angles
- Me.Invalidate()
- End Sub
- Private Sub frmTriangleInSquare_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
- e.Graphics.DrawRectangles(_SquarePen, {_Square}) 'paint Square
- Dim corners = {_Square.Location, New PointF(_Square.Right, _Square.Y), PointF.Empty} 'leftCorner, rightCorner, unknownCorner
- Const stepSize = 60
- For y = 0 To _Square.Height Step stepSize 'paint increasing Triangles
- corners(2) = Get3rdCorner(corners(0), corners(1), _Angles(0), _Angles(1))
- e.Graphics.DrawPolygon(Pens.Red, corners)
- corners(1).Y += stepSize 'increment rightCorner.Y
- Next
- corners(1) = GetOptimumRigthCorner()
- corners(2) = Get3rdCorner(corners(0), corners(1), _Angles(0), _Angles(1))
- e.Graphics.DrawPolygon(Pens.White, corners) 'paint optimum Triangle
- End Sub
- Private Function GetOptimumRigthCorner() As PointF
- 'Bisektion mit 16 schritten ergibt einen max Fehlerwert von 2^-16 Kantenlängen, also < 1/100 Pixel
- With _Square
- Dim poss = {0.0F, .Height / 2, .Height}
- For i = 0 To 15
- Dim pt2 = Get3rdCorner(.Location, New PointF(.Right, poss(1)), _Angles(0), _Angles(1))
- poss(If(.Contains(pt2), 0, 2)) = poss(1)
- poss(1) = (poss(0) + poss(2)) / 2
- Next
- Return New PointF(.Right, poss(1))
- End With
- End Function
- Private Function Get3rdCorner(pt0 As PointF, pt1 As PointF, angle0 As Double, angle1 As Double) As PointF
- Dim m01 = (pt0.Y - pt1.Y) / (pt0.X - pt1.X) 'Steigung pt0->pt1
- Dim angleAdd = Math.Atan(m01)
- Dim tmp = Math.PI / 180
- angle0 *= tmp
- angle1 *= tmp
- Dim m02 = Math.Tan(angle0 + angleAdd) 'Steigung pt0->pt2
- Dim m12 = Math.Tan(-angle1 + angleAdd) 'Steigung pt1->pt2
- 'ab hier verstehe ich die Formeln nicht - aber sie stimmen!
- Dim b1 = pt0.Y - m02 * pt0.X
- Dim b2 = pt1.Y - m12 * pt1.X
- Dim det = m12 - m02
- Dim x = (b1 - b2) / det
- Dim y = (m12 * b1 - m02 * b2) / det
- Return New PointF(CSng(x), CSng(y))
- End Function
- End Class
sieht hübsch aus, finde ich:
Gezeichnet wird das Quadrat, eine Folge von immer größeren 3-ecken, und das optimale 3-eck, also das größtmögliche, was noch ins Quadrat hineingeht:
VB.NET-Quellcode
- Private Sub frmTriangleInSquare_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
- e.Graphics.DrawRectangles(_SquarePen, {_Square}) 'paint Square
- Dim corners = {_Square.Location, New PointF(_Square.Right, _Square.Y), PointF.Empty} 'leftCorner, rightCorner, unknownCorner
- Const stepSize = 60
- For y = 0 To _Square.Height Step stepSize 'paint increasing Triangles
- corners(2) = Get3rdCorner(corners(0), corners(1), _Angles(0), _Angles(1))
- e.Graphics.DrawPolygon(Pens.Red, corners)
- corners(1).Y += stepSize 'increment rightCorner.Y
- Next
- corners(1) = GetOptimumRigthCorner()
- corners(2) = Get3rdCorner(corners(0), corners(1), _Angles(0), _Angles(1))
- e.Graphics.DrawPolygon(Pens.White, corners) 'paint optimum Triangle
- End Sub
Bisektion
informatisch interessant ist v.a. das Annäherungs-Verfahren "Bisektion":
VB.NET-Quellcode
- Private Function GetOptimumRigthCorner() As PointF
- 'Bisektion mit 16 schritten ergibt einen max Fehlerwert von 2^-16 Kantenlängen, also < 1/100 Pixel
- With _Square
- Dim poss = {0.0F, .Height / 2, .Height}
- For i = 0 To 15
- Dim pt2 = Get3rdCorner(.Location, New PointF(.Right, poss(1)), _Angles(0), _Angles(1))
- poss(If(.Contains(pt2), 0, 2)) = poss(1)
- poss(1) = (poss(0) + poss(2)) / 2
- Next
- Return New PointF(.Right, poss(1))
- End With
- End Function
poss
, was Minimum, TestWert und Maximum enthält (zeile#4).In einer Schleife wird der Testwert getestet, und je nach Ergebnis wird entweder das Minimum rauf- oder das Maximum runter-gesetzt, auf den Testwert (#7).
Anschließend wird der Testwert neu festgelegt, einfach als Mittel aus Min und Max.
Die gezeigte Bisektion-Schleife ist also stark verallgemeinerbar - man bräuchte nur das Testverfahren auszutauschen, um iwelche anneren Sachen zu approximieren.
(Edit: Ich glaub jedenfalls, dasses "Bisektion" heißt. Ist prinzipiell dasselbe wie eine binäre Suche, nur sucht nicht in einer sortierten Menge diskreter Elemente, sondern sucht in einem Kontinuum.)
Edit: hglhyr fand schließlich noch einen viel einfacheren Zusammenhang, der diese Approximation üflüssig gemacht hätte: alle 3. Ecken liegen auf einer Geraden. (Hab diese Grade nun gelb eingezeichnet.)
Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()