This is a challenge I was randomly invited by Google while googling things for work, I found this problem quite interesting, so I decided to post my solution and explanation here.
As Commander Lambda's personal assistant, you've been assigned the task of configuring the LAMBCHOP doomsday device's axial orientation gears. It should be pretty simple -- just add gears to create the appropriate rotation ratio. But the problem is, due to the layout of the LAMBCHOP and the complicated system of beams and pipes supporting it, the pegs that will support the gears are fixed in place.
The LAMBCHOP's engineers have given you lists identifying the placement of groups of pegs along various support beams. You need to place a gear on each peg (otherwise the gears will collide with unoccupied pegs). The engineers have plenty of gears in all different sizes stocked up, so you can choose gears of any size, from a radius of 1 on up. Your goal is to build a system where the last gear rotates at twice the rate (in revolutions per minute, or rpm) of the first gear, no matter the direction. Each gear (except the last) touches and turns the gear on the next peg to the right.
Given a list of distinct positive integers named pegs representing the location of each peg along the support beam, write a function solution(pegs) which, if there is a solution, returns a list of two positive integers a and b representing the numerator and denominator of the first gear's radius in its simplest form in order to achieve the goal above, such that radius = a/b. The ratio a/b should be greater than or equal to 1. Not all support configurations will necessarily be capable of creating the proper rotation ratio, so if the task is impossible, the function solution(pegs) should return the list [-1, -1].
For example, if the pegs are placed at [4, 30, 50], then the first gear could have a radius of 12, the second gear could have a radius of 14, and the last one a radius of 6. Thus, the last gear would rotate twice as fast as the first one. In this case, pegs would be [4, 30, 50] and solution(pegs) should return [12, 1].
The list pegs will be given sorted in ascending order and will contain at least 2 and no more than 20 distinct positive integers, all between 1 and 10000 inclusive.
To provide a Java solution, edit Solution.java To provide a Python solution, edit solution.py
Your code should pass the following test cases. Note that it may also be run against hidden test cases not shown here.
-- Java cases --
Input:
Solution.solution({4, 17, 50})
Output:
-1,-1
Input:
Solution.solution({4, 30, 50})
Output:
12,1
-- Python cases --
Input:
solution.solution([4, 30, 50])
Output:
12,1
Input:
solution.solution([4, 17, 50])
Output:
-1,-1
Before we start solving the problem, let's discuss some physics.
For simplicity, we can imagine having perfectly circular wheels instead of gears; this physically and geometrically does not create any problems because the transmission ratio does not change.
First of all, let's define angular velocity as
The relation between velocity and angular velocity is
Our goal is to make the last gear rotates at twice the rate (in revolutions per minute, or rpm) of the first gear, which translates into the fact that the angular velocity of the last gear (
We know that the speed (
So we can say that all the teeth of all the gears move at the same speed:
Consequently, the speed of the first gear is equal to that of the last gear:
Let's solve the equation above:
From the solution above, it can be seen that for the last gear to rotate at double the speed of the first gear, the radius of the first gear must be double that of the last gear, and we don't have to worry about the radius and angular velocity of the intermediate gears.
We will use an example to then find the equations and consequently the algorithm.
In the following, we will use
Below is a graphic example of a system with 7 gears
Let's set up a system of equations to solve the example above:
Move the radii (in ascending order) to the left of the equations:
Substitute the radii to the right of the equations (starting from the third equation):
With the following step, we rearrange the terms
Above we will notice that there is a sequence (
We can generalize the sequence with the following formula:
We can also write this sequence (
In any case, let's continue by solving our example; with the following steps we calculate the radius of the first gear:
We can now note that to calculate
Let's expand the equation to notice all the
Now we can write the formula to calculate the radius of the first gear in a 7 gear system:
If we repeat the example with 6 gears we will find the following equation:
So we can derive the following formula to solve a system with 6 gears:
We can see that the above formula is very similar to the one with 7 gears; the only difference is that
If we keep repeating this exercise several times with different numbers of gears, we will notice at some point that
Same formula but using summation instead of
To implement the above formula into an algorithm it's quite simple, we just need to iterate through all the pegs calculating their sum and changing their signs depending on whether the index is even or odd.
/*r1 will contain the numerator and denominator of the radius of the first gear, also
* there are always at least two pegs, so we can already compute S_1 and save it in r1[0]. */
int[] r1 = {pegs[1] - pegs[0], 1};
//This cycle is the implementation of the summation according to the formula
for(int i = 2; i < pegs.length; i++) {
//Note that here 'i' is the index of the array "pegs", while in the formula it's the number of the peg (starting from 1)
if(i % 2 == 0) {
r1[0] += -pegs[i] + pegs[i-1]; //Note that r1[0] will contain odd S_N whenever 'i' is even
} else {
r1[0] += pegs[i] - pegs[i-1]; //Note that r1[0] will contain even S_N whenever 'i' is odd
}
}
//We multiply the result of the summation by 2 or 2/3, depending on whether the number of pegs is odd or even
r1[0] *= 2;
if(pegs.length % 2 == 0) {
//If possible we simplify the fraction (as required by the challenge)
if(r1[0] % 3 == 0) {
r1[0] /= 3;
} else {
r1[1] = 3;
}
}
The problem with the above formula/algorithm is that it doesn't take into account that the radius of the gears must be
So let's set up the following system of inequalities to impose all
Please note that above we don't need to set
In the following step, we rearrange the terms by moving
In the system above we can see something interesting; basically, to check that every radius is
This leads us to another conclusion, that is if we know the smallest
So now the problem reduces to finding r1[0]
, which means we don't need to do a second iteration to find the
/*r1 will contain the numerator and denominator of the radius of the first gear, also
* there are always at least two pegs, so we can already compute S_2 and save it in r1[0]. */
int[] r1 = {pegs[1] - pegs[0], 1};
int min_s_even = r1[0]; //Initialize with S_2, which is the smallest Sn_even known so far
int max_s_odd = 0; //Initialize with S_1 (defined as 0), which is the biggest Sn_odd known so far
//This cycle is the implementation of the summation according to the formula
for(int i = 2; i < pegs.length; i++) {
//Note that here 'i' is the index of the array "pegs", while in the formula it's the number of the peg (starting from 1)
if(i % 2 == 0) {
r1[0] += -pegs[i] + pegs[i-1]; //Note that r1[0] will contain odd S_N whenever 'i' is even
if(r1[0] > max_s_odd) { //Find max(Sn_odd)
max_s_odd = r1[0];
}
} else {
r1[0] += pegs[i] - pegs[i-1]; //Note that r1[0] will contain even S_N whenever 'i' is odd
if(r1[0] < min_s_even) { //Find min(Sn_even)
min_s_even = r1[0];
}
}
}
//We multiply the result of the summation by 2 or 2/3, depending on whether the number of pegs is odd or even
r1[0] *= 2;
if(pegs.length % 2 == 0) {
//If possible we simplify the fraction (as required by the challenge)
if(r1[0] % 3 == 0) {
r1[0] /= 3;
} else {
r1[1] = 3;
}
}
//Verify that no radius is less than 1
if(r1[0] < (max_s_odd + 1) * r1[1] || r1[0] > (min_s_even - 1) * r1[1]) {
r1[0] = r1[1] = -1;
}
If you have suggestions, improvements or want some clarification, feel free to open a discussion in the Issues section!