Place n points while maximizing the minimum distance - java

Given a distance d (going from 0 to d) and 2 points s and e in between which no points can be placed (placing points on s and e is fine, it's not allowed to place points between them).
Place n points such that the distance between each point is as large as possible (distribute them as evenly as possible).
Output the minimal distance between 2 points.
Graphic representation, place n points on the black line (it's a 1-dimensional line) so that the smallest distance between each 2 points is as large as possible (an absolute error of up to 10^(-4) is allowed).
Examples:
d=7, n=2, s=6, e=7, Output is: 7.0000000000
d=5, n=3, s=5, e=5, Output is: 2.5000000006
d=3, n=3, s=0, e=1, Output is: 1.5000000007
d=9, n=10, s=5, e=6, Output is: 1.0000000001
d=6, n=2, s=1, e=6, Output is: 6.0000000000
d=5, n=3, s=4, e=5, Output is: 2.5000000006
My approach:
I tried looking at the intervals separately, distributing points (ideal distribution, lengthOfInterval/n) on the first and second interval (0 to s and e to d) and inspecting all distributions whose number of points sum up to n, I would store a (distribution, largest minimal distance) pair and pick the pair with the largest minimal distance. I don't know how to work with the 10^(-4) tolerance (how does this part even look in code?) and am not sure if my approach is correct. Every suggestion is welcome.
I'm stuck on this question :/

You can use binary search over the possible sizes of gaps between points (from 0 to d) to converge to the largest minimum gap size.
To determine the viability of any given gap size, you basically try to place points from the left and from the right and see whether the gap in the middle is big enough:
Determine how many points can be placed left of s (which is s/gapSize + 1).
Determine how many points will then be required to be placed to the right of e
(which is n - points on left).
Determine how far inwards each side will go.
Check whether the points on the right fits in the gap [e, d] and whether there's at least gap size difference between each side.
Code for this: (note that I worked with number of gaps instead of points, which is just 1 less than the number of points, since it leads to simpler code)
double high = d, low = 0, epsilon = 0.000001;
while (low + epsilon < high)
{
double mid = (low + high)/2;
int gapsOnLeft = (int)(s/mid); // gaps = points - 1
if (gapsOnLeft + 1 > n)
gapsOnLeft = n - 1;
int gapsOnRight = n - gapsOnLeft - 2; // will be -1 when there's no point on the right
double leftOffset = mid*gapsOnLeft;
// can be > d with no point on the right, which makes the below check work correctly
double rightOffset = d - mid*gapsOnRight;
if (leftOffset + mid <= rightOffset && rightOffset >= e)
low = mid;
else
high = mid;
}
System.out.println(low);
Live demo.
The time complexity is O(log d).
The problem with your approach is that it's hard to figure out how big the gaps between points are supposed to be, so you won't know how many points are supposed to go on either side of (s, e) as to end up with an optimal solution and to correctly deal with both cases when s and e are really close together and when they're far apart.

Binary search
Its very easy to find the number of points you can place if the minimum separation distance b/w any pair l is given.
If l=d, then at the most only 2 points can be placed.
..
...
....
so just do a binary search on l.
A crude implementation goes like this.
low,high=0.00001,d
while(high-low>eps):
m = (low+high)/2
if((no. of points placed s.t any pair is at most m units away) >=n):
low=mid
else:
high=mid

TL;DR: Your approach does not always work (and you're not doing it as fast as you could) see the 3rd bullet point for one that works (and uses the given 10^(-4)).
If [s, e] is small and well-placed, then the optimum is just distributing evenly on the whole segment, best value is now d/(n-1). But you'll have to check that none of your elements is between s and e.
Your approach works if s and e are "far enough".
You can do it faster than what you seem to suggest though, by lookign for the best splitting between the two segments in time O(1): if you put n1 (1<=n1<=n-1) elements on the left, you want to maximize min(s/(n1-1), (d-e)/(n-n1-1)) (one of these quantities being possibly +infinity, but then the other is not). The maximum of that function is obtained for s/(x-1) = (d-e)/(n-x-1), just compute the corresponding value for x, and either its floor or ceiling is the best value for n1. The distance obtained is best = min(s/(n1-1), (d-e)/(n-n1-1)) Then you put n1 points on the left, starting at 0, separated by distance best, and n-n1 on the right, starting at d, going left, separated by best.
If the distance between the last point on the left and the first on the right is smaller than best, then you have a problem, this approach does not work.
The complicated case is when the two previous approaches failed: the hole is small and not well placed. Then there are probably many ways to solve the problem. One is to use binary search to find the optimal space between two consecutive points. Given a candidate space sp, try distributing points on the line starting at 0, spaced by sp, as many as you can while remaining below s. Do the same on the right while staying above e and above (last on the left + sp). If you have successfully placed at least n points in total, then sp is too small. Otherwise, it is too big.
Thus, you can use binary search to find the optimal spas follows: start at sp possibly in [max(s, d-e)/(n-1), d/(n-1)]. At each step, take the middle mid of your possible segment [x, y]. Check if the real optimum is above or below mid. According to your case, look for the optimum in [mid, y] or [x, mid]. Stop iff y-x < 10^(-4).
The two previous cases will actually also be found by this method, so you don't need to implement them, except if you want the exact optimal value when possible (i.e. in the first two cases).

It's pretty tricky, except for the simple case (no point lands in the gap):
double dMin = d / (n - 1.0);
if (Math.ceil(e / dMin - 1) * dMin <= s)
return dMin;
Let's continue with the edge cases, placing one point on one side and the rest of the points on the other one:
dMin = Math.min((d - e) / (n - 2.0), e); // one point at 0
double dm = Math.min(s / (n - 2.0), d - s); // one point at d
if (dm > dMin) // 2nd configuration was better
dMin = dm;
And finally for two or more points on both sides:
// left : right = (x - 1) : (n - x - 1)
// left * n - left * x - left = right * x - right
// x * (left + right) = left * n - left + right
// x = (left * n - left + right) / (left + right) = (left * n) / (left + right) - 1
int x = s * n / (d - e + s) - 1;
if (x < 2)
x = 2;
for (int y = x; y <= x + 2 && y < n - 1; y++) {
double dLeft = s / (y - 1.0);
double dRight = (d - e) / (n - y - 1.0);
dm = Math.min(dLeft, dRight);
if (dm > e - s) { // dm bigger than gap
if (dLeft > dRight)
dLeft = e / ((double) y);
else
dRight = (d - s) / ((double) n - y);
dm = Math.min(dLeft, dRight);
}
if (dm > dMin)
dMin = dm;
}
This would be O(1) space and time, but I'm not 100% positive if all cases are checked. Please let me know if it worked. Tested against all the test cases. The above works for n >= 2, if n equals 2 it will be caught by the first check.

Related

How to determine the points belonging to the distance between two not intersecting lines in space

I have a question that I would like to share with you in case you could help me. I am working on 3D lines and I need to obtain the exact points at which the minimum distance of two intersecting lines is located. Why do I need these points?
Of each one of the infinite lines, I would only be interested in knowing if this minimum distance is in a certain range from that of points on the same line. For example, I have the points P (0,0,0) and Q (10,10,10) of a line r and I would only be interested to know if said minimum distance is in that coordinate interval or not.
To obtain the distance, I use the formula:
But once said I do not know how to ensure if such a minimum distance is reached within the range of values of P and Q that I comment above.
If anyone has any better ideas on how to check this or knows how to get such points, I would really appreciate it.
Usually, lines in 3D are specified by the coordinates of one point on the line and the coordinates of one vector aligned (or parallel) to the line. So I am going to assume that what you have as input data is:
1) line s with point S = [xS, yS, zS] on l and a vector u = [u1, u2, u3] aligned with s
2) line r defined by the pair of points P = [xP, yP, zP] and Q = [xQ, yQ, zQ]
Your goal is to check whether the point on r that is closest to line s
is inside the line segment PQ on r or not.
Algorithm.
n = u x (Q - P)
SP = P - S
SQ = Q - S
proj_SP = SP - ( (n . SP) / (n . n) ) n
proj_SQ = SQ - ( (n . SQ) / (n . n) ) n
w = n x u
if (proj_SP . w) * (proj_SQ . w) < 0
then the point on r that is closest to line s
is inside the line segment PQ
else
the point on r that is closest to line s
is outside the line segment PQ
If I understand your question right:
for point P get orthogonal projections onto both lines (denominator is not needed if v and w are normalized)
PAproj = A - P + dot(AP, v) / dot(v,v) * v
PBproj = B - P + dot(BP, w) / dot(w,w) * w
Then check that these vectors are anticollinear
cross(PAproj, PBproj) == 0
and
dot(PAproj, PBproj) has negative sign

Complete AND Full Binary tree max and min indexes?

I'm not sure if i'm overthinking this, but I cannot think of the general case solution :(
As far as we store nodes having greater values in right subtree and smaller values in left subtree for any node, the node at Bottom Right corner will be having the maximum value and the node at Bottom Left corner will be having the minimum value.
So now you need to find the nodes. It is given that the tree is Complete and Full BST and also it is stored in Array so indexes are uniformly distributed over the nodes. So here we need to move Top to Bottom and Left to Right assigning index to nodes starting from 1 to n if there are n nodes.
So If we write indexes for given tree,
1
/ \
2 3
/ \ / \
4 5 6 7
/ \ ^^^-------------Largest value
8 9
Smallest value----^^^
So here a node having 8 index will be having the smallest value and a node having 7 index will be having the largest value.
So now the question is how to find it. So consider that we have a tree with level l then the index for largest value will be 2^level - 1 and smallest value will be at 2^levelth index.However the index we get here for largest value may give us a wrong answer if total_nodes = 2^level-1. So we need to calculate level for this in a different way by considering total_nodes = n+1.
int level = (int)(Math.ceil ( Math.log(n)/Math.log(2) )); //For index of smallest value;
int smallest_index = (int) Math.pow (2,level);
level = (int)(Math.ceil ( Math.log(n+1)/Math.log(2) )); //For index of largest value;
int largest_index = (int) Math.pow (2,level) - 1;
Sanket's answer is basically right, but the way it is eventually stated makes me uncomfortable. Who's to say that the rounded(!) ratio of two rounded(!) logs won't round to just slightly higher than the intended integer? And then ceil will carry it all the way up. Maybe it works, but it would essentially be by accident, not by design.
It can also be stated "cleanly", without the need to think/worry about such things, in terms of bitwise arithmetic. Everything stays an integer, so it's easier to reason about.
The index of the lowest item is at the highest power of 2 present in n, so the highest set bit. In Java there is even a function for that: Integer.highestOneBit, or we could write it:
int highestOneBit(int x) {
x |= x >> 16;
x |= x >> 8;
x |= x >> 4;
x |= x >> 2;
x |= x >> 1;
return x ^ (x >>> 1);
}
And now we have
indexOfLowest = highestOneBit(n);
indexOfHighest = highestOneBit(n + 1) - 1;
This still assumes 1-based indexes (leaving index 0 unused), you can simply offset it all by 1 to make it 0-indexed.

Arithmetical sequence that needs to be solved without using arrays

I have this assignment in my Introduction to CompSci class. Our professor says the code is easy to do, we just need to solve the assignment on paper with math. Here is the assignment (NOTE: We mustn't use arrays or any kind of similar stuff, we can only use loops and if):
For the elements of a sequence X0, X1, X2,...,Xn it stands that X0=5, X1=-1,..., Xn+2+2Xn+1+Xn=0. We need to write a program that scans the value of k and prints out Xk.
For max points we need to make X1=A, X2=B where A and B are scanned from keyboard.
Here is my try, I know how to print Xk but I don't know what to print (Since this is Java I will only copy the content of my main):
Scanner in = new Scanner(System.in);
int k=in.nextInt();
int a = 5;
int b = -1;
for(i=3;i<=k;i++)
{
}
I know this is probably demeaning for anybody here to do, but I'm really stuck and I don't know how to solve this one. I have examples of different types of similar assignments that i solved easy but this part Xn+2+2Xn+1+Xn=0 bugs me.
So, the rule is that
Xn+2 + 2 * Xn+1 + Xn = 0
So the value of Xn+2 is
Xn+2 = 0 - 2 * Xn+1 - Xn
Since you know X0 and X1, you can compute X2 using that rule. Since you now know X2 and X1, you can compute X3 using that rule. Since you now know X3 and X2, you can compute X4 using that rule. And you can continue until you know Xk.
The implementation is left as an exercise. The important trick is that all you need to remember (i.e. store in two variables a and b, for example), are the two previous values of Xn+1 and Xn, to be able to compute Xn+2.
As was already stated
Xn+2 = 0 - 2 * Xn+1 - Xn
now, substitute
n = k - 2
and you get
Xk = - 2 * Xk-1 - Xk-2
now, write a function that returns Xk
int x(int k) {
return -2 * x(k - 1) - x(k - 2);
}
Now this is obviously an infinite recursion, so fix it by introducing known values and limits:
int x(int k) {
if (k < 0) throw new runtimeException();
if (k == 0) return 5;
if (k == 1) return -1;
return -2 * x(k - 1) - x(k - 2);
}
This is not an efficient solution, just an exercise.

Big-O complexity of a recursive solution

I have a simple recursive solution as below:
public int countPaths(int x, int y) {
if(x == 0 && y == 0) {
return 0;
} else if(x == 0) {
return 1;
} else if(y == 0) {
return 1;
} else {
int count = countPaths(x-1, y);
count += countPaths(x, y-1);
return count;
}
}
This is to solve the following problem from the book: Cracking the coding interview
Imagine a robot sitting on the upper left corner of an X by Y grid. The robot can only move in two directions: right and down. How many possible paths are there for the robot to go from (0,0) to (X,Y)?
I am trying to ascertain the run time complexity and I believe it is O(x+y). I arrived at this by using a recursion tree, for example if x=2 and y=2
The max depth of this tree is (x+y) and work done at each step is a constant. So max work done is (x+y) * c and hence the run time complexity is O(x+y)
Question 1: Am I correct? I believe the upper bound I have calculated is not tight enough
Question 2: Next, if I were to improve the run time using memoization and hence not repeating computing sub-problems, how would the run time complexity as described by Big-o change?
While it's true that the depth of the tree is O(x+y), there's increasingly many nodes at each layer, and it's the number of nodes that determines complexity, not the depth of the tree.
If you write down recurrence relations for the runtime, you get:
T(0, y) = T(x, 0) = 1
T(x, y) = T(x-1, y) + T(x, y-1) + 1
If you ignore the +1 on the second equation (which can only make the run-time better), you get the same function that your code was computing in the first place, which is choose(x+y, y).
For x=y, this is the central binomial coefficient, which is approximately 4^x/sqrt(pi*x), which for even moderately large values of x is large enough to make the algorithm useless.
With memoisation, you're doing a constant amount of work for each value of x and y, so the complexity is O(xy).
If you evaluate the complexity in terms of the number of additions required to evaluate the count for a given pair (x, y), you get the recurrence
A(x,y) = A(x-1,y) + A(x,y-1) + 1,
with A(x,0) = A(0,y) = 0.
Setting A(x,y) = P(x,y) - 1 the recurrence becomes
P(x,y) - 1 = P(x-1,y) - 1 + P(x,y-1) - 1 + 1,
or
P(x,y) = P(x-1,y) + P(x,y-1),
with P(x,0) = P(0,y) = 1, which gives the classical Pascal's triangle, and
A(x,y) = (x+y)!/(x!y!) - 1.
You can also work with the number of recursive function calls,
C(x,y) = C(x-1,y) + C(x,y-1) + 2,
with C(0,y) = C(x,0) = 0.
You will solve it by setting C(x,y) = 2P(x,y) - 2, and get
C(x,y)= 2(x+y)!/(x!y!)-2.
As regards the asymptotic complexity, this makes no difference. The is no really simpler formula than O((x+y)!/x!y!).
With memoization, every evaluation (with x, y>0) costs only one addition or two calls, and assuming constant time for storage/retrieval of a value the total complexity is the much better O(xy).
Based on the valuable input from #Anonymous, we know the recurrence relation is:
T(x, y) = T(x-1, y) + T(x, y-1) + 1
Abusing (which is ok in Big-O analysis) the notation, let x = y
T(x, x) = 2 * T(x-1, x) = 2 * 2 * T(x-2, x) = ... = 2 * ... * 2 * T(0, x)
= O(2^x)
So the run time complexity is
O(2^n) ; where n = max(x, y)
With memoization, I get it, thank #Anonymous, it should be O(xy)

Converting random numbers into XY coordinates for graphing

My professor gave us an assignment to test the difference in runtimes and search sizes using linear & binary algorithms, and the data is to be graphed.
I have the search methods put the runtime & array sizes as Points in an ArrayList, which is then sent to the GraphResults class for plotting. I need to convert those data points into xy coordinates before. The search size is the x-axis and the runtime is the y axis
As the search sizes are fixed as a multiple of 128 and there are only 8 sizes, I used switch for calculating the x value, but am looking for a more efficient way to convert the runtimes into coordinates.
Right now, I'm using nested conditionals with 5 like this:
if (y<=1000) {
if (y<= 500) {
if (y<= 250) {
newy= yaxis-32; }//equals to 250ms category
else {
newy= yaxis-(32*2); }//500ms category
}
else if (y<=750) {
newy= yaxis-(32*3);} //750ms category
else {
newy= yaxis-(32*4);} //1000ms category
} //end of the 1000ms tests
Right now, the numbers that are over 5000ms require 7 tests. Is there a more efficient way to assign a number based on a number size?
As you are trying to determine the range of your measurement, you can divide the amount by the range size, followed by calculating the number you want to show in the graph.
Btw, in your code, you made a logic error, if the value is y <= 1000 the first condition evaluates to true, and the second for y <= 750 will never be evaluated.
Also it seems that the higher the value range, the lower your graph point. Is that as intended? (1000 -> ymax - 128 while 1 -> ymax - 32)
As an aside, if you want to compare values to uneven ranges, you can also do something like an array lookup (pseudo code):
int[] ranges = new int { 50, 500, 5000, 50000 };
for (int n = 0; n < ranges.length && value > ranges[n]; n++) {
}
int range = n;
int newy = yaxis - range * 32;
Note that the out-of-range index acts as the range found for a value that is bigger than the biggest value in your array.
How about newy = yaxis - 32 * ((y/250)% 8);?
I would reformat your code to something more like this:
newy = yaxis - 32 * ((y-1)/250 + 1);
This way, you're calculating the multiplier rather than choosing it manually.

Categories

Resources