Euler Problem 102
Euler Problem 102 asks:
'-1000 LTE x, y LTE 1000, such that a triangle is formed.
'
'Consider the following two triangles:
'
'A(-340,495), B(-153,-910), C(835,-947)
'
'X(-175,41), Y(-421,-714), Z(574,-645)
'
'It can be verified that triangle ABC contains the origin, whereas
'triangle XYZ does not.
'
'Using triangles.txt (right click and 'Save Link/Target As...'), a 27K text
'file containing the co-ordinates of one thousand "random" triangles, find
'the number of triangles for which the interior contains the origin.
'
'NOTE: The first two examples in the file represent the triangles in the
'example given above.
In the above, LTE is "less than or equal", to outsmart the html bugs.
Any triangle that has all-positive x-coordinates, or all-negative x-coordinates, or similarly all-positive or all-negative y-coordinates, can not contain the origin. A pre-screen will throw all those out. When I first solved this problem, I looked for triangles with both a positive and a negative y-intercept, coupled with both a positive and negative x-intercept, and counted those triangles. That was a successful strategy. LINEST() will return x-intercepts it you swap known-xs and known-ys in the formula. The code looked like this:
Application.WorksheetFunction.LinEst(Known_Ys, Known_Xs), 2)
XIntercept(3, 1) = Application.WorksheetFunction.Index( _
Application.WorksheetFunction.LinEst(Known_Xs, Known_Ys), 2)
It's right out of the Help for LINEST(), or at least the y-intercept part is. However, after I checked my answer in I read another strategy that was just so flat-out neat that I coded it up. It uses Heron's Law which calculates the area of a triangle based on a calculation of the semi-perimeter, or 1/2 the sum of the sides: The area of a triangle, given the lengths a,b,c of the sides, is A = sqrt s*(s-a)*(s-b)*(s-c), where s is the semiperimeter 0.5*(a+b+c). If we calculate the area of the suspect triangle, and then the area of the three sub-triangles with a common vertex of the origin, if the sum of the sub-triangle areas equals the area of the whole, then the origin must be within the suspect triangle. That code looked like this:
Option Base 1
Sub Problem_102B()
Dim Triangle(1000) As Variant
Dim i As Long
Dim j As Long
Dim Most As Long
Dim Impossible As Boolean
Dim IsTest As Boolean
Dim x(3) As Long 'X Coordinates
Dim Y(3) As Long 'Y Coordinates
Dim d(3) As Double 'Distance between points
Dim O(3) As Double 'Distance from points to (0,0)
Dim Area As Double 'Triangle area
Dim a(3) As Double 'Sub-triangle area
Dim T As Single
Dim Answer As Long
Const text As String = "D:\Downloads\Euler\triangles.txt"
T = Timer
IsTest = False
If IsTest Then
Most = 2
Else
Most = 1000
End If
i = 1
Open text For Input As #1 '1000 lines, comma delimited
Do While Not EOF(1)
Line Input #1, Triangle(i)
Triangle(i) = Split(Triangle(i), ",") 'zero-based array
i = i + 1
Loop
Close #1
For i = 1 To Most
Impossible = False
x(1) = Triangle(i)(0) 'zero-based array
Y(1) = Triangle(i)(1)
x(2) = Triangle(i)(2)
Y(2) = Triangle(i)(3)
x(3) = Triangle(i)(4)
Y(3) = Triangle(i)(5)
'For Triangles all above or all below the X-axis
If Y(1)> 0 And Y(2)> 0 And Y(3)> 0 Then Impossible = True
If Y(1) <0 And Y(2) <0 And Y(3) <0 Then Impossible = True
'For Triangles all to left of or all to right of the Y-axis
If Not Impossible Then 'yet
If x(1)> 0 And x(2)> 0 And x(3)> 0 Then Impossible = True
If x(1) <0 And x(2) <0 And x(3) <0 Then Impossible = True
End If
If Impossible Then GoTo Next_i
d(1) = TriSides(CDbl(x(1) - x(2)), CDbl(Y(1) - Y(2)))
d(2) = TriSides(CDbl(x(2) - x(3)), CDbl(Y(2) - Y(3)))
d(3) = TriSides(CDbl(x(1) - x(3)), CDbl(Y(1) - Y(3)))
Area = Heron(d(1), d(2), d(3))
O(1) = TriSides(CDbl(x(1)), CDbl(Y(1)))
O(2) = TriSides(CDbl(x(2)), CDbl(Y(2)))
O(3) = TriSides(CDbl(x(3)), CDbl(Y(3)))
a(1) = Heron(d(1), O(1), O(2))
a(2) = Heron(d(2), O(2), O(3))
a(3) = Heron(d(3), O(1), O(3))
If CSng(Area) = CSng(a(1) + a(2) + a(3)) Then
Answer = Answer + 1
End If
Next_i:
Next i
Debug.Print Answer; " Time:"; Timer - T
End Sub
Those are the escape code for > and < . Can't figure out how to get it right inside the vb blocks. We can escape it outside, but we truncate if we use the angle brackets, and don't render the brackets right if we escape. It's been a conundrum for a while.
I used two functions, TriSides(), which returns the square root of the sum or difference of two squares, so I could do Pythagorean math, and Heron(), which implements Heron's law:
If IsMissing(Sum) Then Sum = True
If Sum Then
TriSides = Sqr(a ^ 2 + b ^ 2)
Else
TriSides = Sqr(Abs(a ^ 2 - b ^ 2)) ' order doesn't matter
End If
End Function
Function Heron(a As Double, b As Double, C As Double) As Double
'Use Heron 's formula for the area of a triangle
'given the lengths a,b,c of the sides
'A = sqrt s*(s-a)*(s-b)*(s-c)
'where s is the semiperimeter 0.5*(a+b+c).
Dim s As Double
s = 0.5 * (a + b + C)
Heron = Sqr(s * (s - a) * (s - b) * (s - C))
End Function
I admire those who saw the application of Heron going in. One thing that I don't understand is why I had to convert doubles to singles at the end for the areas to total per Heron. I presume is has to do with the square root routine, but I invite comment. Code ran in one-tenth the time of 102A, at about .01 seconds.
...mrt
