Geometric Algorithms IV
Geometric problems are hard (even when they are labeled as “medium” in LC). The complications arise primarily from the floating-point calculations, over and underflow, as well as the precise equations that one must come up with.
This problem Separate Squares I - LeetCode requires a binary search in space 10^9. For each iteration, we calculate the areas above and below. Hence the total time is of the order LogN * N. But there are a few things to keep in mind:
1/ Start by transforming all the input from int to double.
This will avoid any over and underflow calculation
2/ Break down into smaller subroutines, for debugging
3/ Remember that when dealing with floating-points, to say
that A == B, what you really need to do is ensure that |A – B| <= T where T
is some threshold (in our case T = 0.00001)
You can use LLM such as OpenAI ChatGPT o1 (with advanced
reasoning) for debugging. For example, you can ask it to plot some input in a
Cartesian System for debugging purposes:
If you ask it to solve the problem, it actually does it well
(not as fast as the binary search approach, it managed to solve in T(n) =
8nLogN + 20N, which is slower than the binary search approach suggested as a
hint in the problem description).
Code works well, and it is shown below. Cheers, ACC
public double SeparateSquares(int[][] squares)
{
double maxY = 0.0;
double minY = 0.0;
double[][] dSquares = new double[squares.Length][];
for (int i = 0; i < dSquares.Length; i++)
{
dSquares[i] = new double[squares[i].Length];
for (int j = 0; j < squares[i].Length; j++)
dSquares[i][j] = (double)squares[i][j];
}
FindAreaBoundaries(dSquares, ref maxY, ref minY);
double areaAbove = 0.0;
double areaBelow = 0.0;
double midY = 0.0;
while (maxY > minY)
{
midY = (maxY + minY) / 2.0;
//Console.WriteLine("{0}, {1}, {2}", minY, maxY, midY);
CalculateSplitAreas(dSquares, midY, ref areaAbove, ref areaBelow);
if (EqualDoubles(areaAbove, areaBelow) || EqualDoubles(maxY, minY))
break;
else if (areaAbove > areaBelow)
minY = midY;
else
maxY = midY;
}
double retVal = double.MinValue;
foreach (double[] square in dSquares)
{
if (square[1] <= midY && square[1] + square[2] >= midY)
return midY;
if (square[1] + square[2] < midY)
retVal = Math.Max(retVal, square[1] + square[2]);
}
return retVal;
}
public bool EqualDoubles(double area1,
double area2)
{
//Console.WriteLine(Math.Abs(area1 - area2));
return Math.Abs(area1 - area2) <= (1.0 / 100000.0);
}
public void CalculateSplitAreas(double[][] squares,
double y,
ref double areaAbove,
ref double areaBelow)
{
areaAbove = 0.0;
areaBelow = 0.0;
foreach (double[] square in squares)
{
double tempAreaAbove = 0.0;
double tempAreaBelow = 0.0;
CalculateSplitAreas(square, y, ref tempAreaAbove, ref tempAreaBelow);
areaAbove += tempAreaAbove;
areaBelow += tempAreaBelow;
}
}
public void FindAreaBoundaries(double[][] squares,
ref double maxY,
ref double minY)
{
maxY = Double.MinValue;
minY = Double.MaxValue;
foreach (double[] square in squares)
{
maxY = Math.Max(maxY, square[1] + square[2]);
minY = Math.Min(minY, square[1]);
}
}
public void CalculateSplitAreas(double[] square,
double y,
ref double areaAbove,
ref double areaBelow)
{
if (square[1] > y)
{
areaBelow = 0.0;
areaAbove = square[2] * square[2];
}
else if (square[1] + square[2] < y)
{
areaAbove = 0.0;
areaBelow = square[2] * square[2];
}
else
{
areaAbove = (square[1] + square[2] - y) * square[2];
areaBelow = (y - square[1]) * square[2];
}
}


Comments
Post a Comment