Reputation: 33
I'm not yet a skilled programmer but I thought this was an interesting problem and I thought I'd give it a go.
Triangle, pentagonal, and hexagonal numbers are generated by the following formulae:
- Triangle T_(n)=n(n+1)/2 1, 3, 6, 10, 15, ...
- Pentagonal P_(n)=n(3n−1)/2 1, 5, 12, 22, 35, ...
- Hexagonal H_(n)=n(2n−1) 1, 6, 15, 28, 45, ...
It can be verified that T_(285) = P_(165) = H_(143) = 40755.
Find the next triangle number that is also pentagonal and hexagonal.
Is the task description.
I know that Hexagonal numbers are a subset of triangle numbers which means that you only have to find a number where Hn=Pn. But I can't seem to get my code to work. I only know java language which is why I'm having trouble finding a solution on the net womewhere. Anyway hope someone can help. Here's my code
public class NextNumber {
public NextNumber() {
next();
}
public void next() {
int n = 144;
int i = 165;
int p = i * (3 * i - 1) / 2;
int h = n * (2 * n - 1);
while(p!=h) {
n++;
h = n * (2 * n - 1);
if (h == p) {
System.out.println("the next triangular number is" + h);
} else {
while (h > p) {
i++;
p = i * (3 * i - 1) / 2;
}
if (h == p) {
System.out.println("the next triangular number is" + h); break;
}
else if (p > h) {
System.out.println("bummer");
}
}
}
}
}
I realize it's probably a very slow and ineffecient code but that doesn't concern me much at this point I only care about finding the next number even if it would take my computer years.
Upvotes: 3
Views: 2633
Reputation: 24904
The key is:
ti = hi*2 - 1
where ti
and hi
are indices of T and H respectively.
That means for any H(hi)
, there is always a T(ti)
.
Thus only need to find the P(pi)
= H(hi)
, and that can be solved linear.
go
)Solution:
package p45
// refer: https://projecteuler.net/problem=45
func NextAllEqual(startHi, startPi int32) (v int64, hi, pi int32) {
start := calculateH(startHi)
h, p := nextH(start, startHi), nextP(start, startPi)
hi, pi = startHi+1, startPi+1
for h != p {
if hi <= 0 || pi <= 0 { // out of range,
return -1, -1, -1
}
if h < p {
h = nextH(h, hi)
hi++
} else {
p = nextP(p, pi)
pi++
}
}
return h, hi, pi
}
// h(i) = i * (2*i - 1)
func calculateH(i int32) int64 {
return int64(i) * (int64(i)<<1 - 1)
}
// h(i+1) = h(i) + (4*i + 1)
func nextH(h int64, i int32) int64 {
return h + (int64(i)<<2 + 1)
}
// p(i+1) = p(i) + (3*i + 1)
func nextP(p int64, i int32) int64 {
return p + (3*int64(i) + 1)
}
// ti = 2*hi- 1
func TQiFromHi(hi int32) int32 {
return hi<<1 - 1
}
Test case:
package p45
import (
"fmt"
"testing"
)
const (
zeroHi, zeroPi = int32(1), int32(1)
firstHi, firstPi = 143, 165
)
func TestNextAllEqual_first(t *testing.T) {
// runOnce_NextAllEqual(t, zeroHi, zeroPi)
runOnce_NextAllEqual(t, firstHi, firstPi)
}
func runOnce_NextAllEqual(t *testing.T, preHi, prePi int32) (v int64, hi, pi int32) {
v, hi, pi = NextAllEqual(preHi, prePi)
if v > 0 {
ti := TQiFromHi(hi)
fmt.Printf("v = %d, indices:\n\tti = %d, pi = %d, hi = %d\n\n", v, ti, pi, hi)
} else {
fmt.Println("Done, index out of range of int32")
}
return v, hi, pi
}
// find all with indices in range of int32,
func TestNextAllEqual_all_int32_indices(t *testing.T) {
hi, pi := zeroHi, zeroPi
var v int64
for seq := 1; ; seq++ {
fmt.Printf("[%d]\t", seq)
v, hi, pi = runOnce_NextAllEqual(t, hi, pi)
if v <= 0 {
break
}
}
}
(Run on an old laptop)
For problem 44:
It took about 2ms
.
=== RUN TestNextAllEqual_first
v = 1533776805, indices:
ti = 55385, pi = 31977, hi = 27693
--- PASS: TestNextAllEqual_first (0.00s)
To find all such indices within range of signed 32 bits.
It took about 5.4s
, there are 4 such values, not including the start one (1,1,1)
.
=== RUN TestNextAllEqual_all_int32_indices
[1] v = 40755, indices:
ti = 285, pi = 165, hi = 143
[2] v = 1533776805, indices:
ti = 55385, pi = 31977, hi = 27693
[3] v = 57722156241751, indices:
ti = 10744501, pi = 6203341, hi = 5372251
[4] v = 2172315626468283465, indices:
ti = 2084377905, pi = 1203416145, hi = 1042188953
[5] Done, index out of range of int32
--- PASS: TestNextAllEqual_all_int32_indices (5.33s)
Upvotes: 0
Reputation: 56684
Another solution that takes 2 ms:
public class P45 {
public static void main(String[] args) {
long H = 0;
long i = 144;
while(true) {
H = i*((i<<1)-1);
if ( isPentagonal(H) && isTriangle(H) ) {
break;
}
i++;
}
System.out.println(H);
}
private static boolean isPentagonal(long x) {
double n = (1 + Math.sqrt(24*x+1)) / 6;
return n == (long)n;
}
private static boolean isTriangle(long x) {
double n = (-1 + Math.sqrt((x<<3)+1)) / 2;
return n == (long)n;
}
}
Improved:
k*(2*k-1)
can be written in the following form i*(i+1)/2
if i = 2*k-1
.isTriangle
can be removed.Upvotes: 1
Reputation: 138377
We know that T285 = P165 = H143 = 40755. We start with nt=286
, np=166
and nh=144
and work out the respective triangle, pentagonal, and hexagonal numbers. Whichever resulting number is smallest, we bump up its n
value. Continue doing this until all numbers are equal and you have your answer.
A Python implementation of this algorithm runs in 0.1 seconds on my computer.
The problem with your code is overflow. While the answer fits into a 32 bit int
, the temporary values i * (3 * i - 1)
overflows before reaching the answer. Using 64 bit long
values fixes your code.
Upvotes: 7
Reputation: 18652
Your code looks like it will produce the correct answer fairly quickly. The while loop can be simplified if you just print the result after the loop terminates:
while (p != h) {
n++;
h = n * (2 * n - 1);
while (h > p) {
i++;
p = i * ((3 * i - 1) / 2);
}
}
System.out.println("the next triangular number is" + h);
Note: your inner loop looks very much like the inner loop of my C++ solution. It produced the desired answer in about 0.002 seconds on my machine.
Upvotes: 1