patternjavaModerate
Minimising the triangle
Viewed 0 times
triangletheminimising
Problem
A triangle needs a good foundation. Every row in the triangle is derived from the sum of the two values below it. However, there can be no repeated values, if a value shows up more than once the triangle crumbles. Find the base which minimises the value in the top of the triangle satisfying the condition of no duplicates.
Source
Example:
Here 3 occurs twice, so the triangle is invalid
The base \$n\$ can be of any size \$n > 0\$ with
I would like suggestions on how I can improve:
```
import java.util.*;
public class spaceship {
public static void main(String[] args) {
long start = System.nanoTime();
spaceship kling = new spaceship();
kling.solveAll();
System.out.println("Took: " + (System.nanoTime() - start) / 1000000 + "ms");
}
public void solveAll() {
int len = 6;
int bestFound = 400; //definitely big enough from previous tests
int res = bestFound;
int[] best = new int[len];
int[] x = new int[len];
int[] co = new int[] { 1, 1, 1, 1, 1, 1 };
for (int a = 1; co[0] * a data = new HashSet<>();
// check input for duplicates
// could be done in loops
for (int a : in) {
if (data.contains(a))
return -1;
data.add(a);
}
int size = in.length;
int[][] arr = new int[size][size];
arr[0] = in;
// first row is the input
// every row after is the sum of the two elements below it
Source
Example:
20
8 12
[3] 5 7
1 2 [3] 4Here 3 occurs twice, so the triangle is invalid
The base \$n\$ can be of any size \$n > 0\$ with
solve(1) = 1, solve(2) = [1, 2] = 3, solve(3) = [2, 1, 4] = 8I would like suggestions on how I can improve:
- The code style or best practices, so the code is easy to read.
- The algorithm/constraints, I'm pretty sure that the loops can be made tighter, so not as many numbers have to be checked, but I can't figure out what the cut off is.
- Is there a better way to implement the base size without hard-coding extra nested loops, as this doesn't seem very scalable?
```
import java.util.*;
public class spaceship {
public static void main(String[] args) {
long start = System.nanoTime();
spaceship kling = new spaceship();
kling.solveAll();
System.out.println("Took: " + (System.nanoTime() - start) / 1000000 + "ms");
}
public void solveAll() {
int len = 6;
int bestFound = 400; //definitely big enough from previous tests
int res = bestFound;
int[] best = new int[len];
int[] x = new int[len];
int[] co = new int[] { 1, 1, 1, 1, 1, 1 };
for (int a = 1; co[0] * a data = new HashSet<>();
// check input for duplicates
// could be done in loops
for (int a : in) {
if (data.contains(a))
return -1;
data.add(a);
}
int size = in.length;
int[][] arr = new int[size][size];
arr[0] = in;
// first row is the input
// every row after is the sum of the two elements below it
Solution
Style
In Java it is common practice to have class names to use PascalCase (a.k.a. UpperCamelCase). Also the name
Readability
I prefer to prefix my formal parameters with
Please prefer descriptive names. Currently I have no idea what
Nested for loops
You are basically implementing a multi-byte counter. You can do something like this (not tested, but you get the idea):
Performance
Lets look at
Here:
you allocate space for the whole triangle but you only ever use the last two rows. So you only need to store two rows and alternate between them. This has the added benefit that both rows will be hot in cache all the time, avoiding cache misses as you go from line to line. It will also avoid some extra indirections in your inner most loop.
Better algorithm
The first thing we need to realize is that the problem is kind of recursive (I can't remember the proper term off the top of my head).
What I mean by that is that if a base
The second thing to realize about this problem is that if
The third thing is that for the base
Knowing the above, you have to unrestrainedly search for the combination of
The difficult part is to generate the nodes to be searched. This can be done effectively by starting with small
One should also take care to not generate symmetry pairs to triangles that have already been tried to exploit (2).
It is useful to store the base and right edge of the triangle in addition to a
I.e. for the triangle:
store:
and
Using the right edge, we can quickly calculate any expansion of this triangle. For example:
is easily calculated as:
if we have the right edge.
I hope this helps. It's an interesting problem.
In Java it is common practice to have class names to use PascalCase (a.k.a. UpperCamelCase). Also the name
spaceship doesn't make sense to me and is confusing. Also, why is the instance called kling?Readability
I prefer to prefix my formal parameters with
a for argument, for example aInputArray. I believe it would help readability of your code.Please prefer descriptive names. Currently I have no idea what
x, co and res are.Nested for loops
You are basically implementing a multi-byte counter. You can do something like this (not tested, but you get the idea):
boolean incr(int[] co, int max, int min, int pos){
if(co[pos] + 1 > max){
co[pos] = min;
if(pos == 0)
return false;
return incr(co, max, min, pos - 1);
}
else{
co[pos]++;
return true;
}
}
void solveAll(){
int[] co = new int[len];
while(incr(co, bestFound, 1, co.length - 1)){
res = solve(co);
}
}Performance
Lets look at
solve(int[] in). You're using HashSet which is fast as long as you don't get excessive collisions. Seeing as you are dealing with many numbers that are possibly small, you could be having many hash collisions. If profiling indicates this is the case, you can try a better hash function (for example multiply by a large prime number).Here:
int[][] arr = new int[size][size];you allocate space for the whole triangle but you only ever use the last two rows. So you only need to store two rows and alternate between them. This has the added benefit that both rows will be hot in cache all the time, avoiding cache misses as you go from line to line. It will also avoid some extra indirections in your inner most loop.
Better algorithm
The first thing we need to realize is that the problem is kind of recursive (I can't remember the proper term off the top of my head).
What I mean by that is that if a base
{a,b,c} for n=3 crumbles, then the all bases {a,b,c,x} for any x will also crumble. This means that by solving the easier problem n=3 we get information about a lot of combinations for n=4 that will crumble. (1)The second thing to realize about this problem is that if
{a,b,c} crumbles, then {c,b,a} will also crumble. And if {a,b,c} doesn't crumble and has top value x then {c,b,a} will also not crumble and have the same top value x. (2)The third thing is that for the base
{a,b,c} the top value will be strictly larger than a+b+c. This is a useful stopping condition. (3)Knowing the above, you have to unrestrainedly search for the combination of
n base elements that produces the smallest top value as result. Use a greedy search for the smallest top value and terminate when all the nodes remaining fulfil (3). The difficult part is to generate the nodes to be searched. This can be done effectively by starting with small
n and then combinatorially incrementing the base elements and adding an additional base element to create triangles with n+1 base for any triangle that doesn't crumble. This exploits (1) to prune large sets of crumbling triangles from the start.One should also take care to not generate symmetry pairs to triangles that have already been tried to exploit (2).
It is useful to store the base and right edge of the triangle in addition to a
HashSet of all the values in the triangle.I.e. for the triangle:
20
8 12
3 5 7
1 2 3 4store:
1 2 3 4 (base row)and
4 7 12 20 (right edge, bottom -> up).Using the right edge, we can quickly calculate any expansion of this triangle. For example:
1 2 3 4 10is easily calculated as:
53
20 33
12 21
7 14
4 10if we have the right edge.
I hope this helps. It's an interesting problem.
Code Snippets
boolean incr(int[] co, int max, int min, int pos){
if(co[pos] + 1 > max){
co[pos] = min;
if(pos == 0)
return false;
return incr(co, max, min, pos - 1);
}
else{
co[pos]++;
return true;
}
}
void solveAll(){
int[] co = new int[len];
while(incr(co, bestFound, 1, co.length - 1)){
res = solve(co);
}
}int[][] arr = new int[size][size];20
8 12
3 5 7
1 2 3 41 2 3 4 (base row)4 7 12 20 (right edge, bottom -> up).Context
StackExchange Code Review Q#92673, answer score: 14
Revisions (0)
No revisions yet.