java 项目euler45

hjzp0vay  于 2023-04-10  发布在  Java
关注(0)|答案(4)|浏览(119)

我还不是一个熟练的程序员,但我认为这是一个有趣的问题,我想我会给予它一个去。
三角形、五边形和六边形数由以下公式生成:

  • 三角形T_(n)=n(n+1)/2 1,3,6,10,15,...
  • 五边形P_(n)=n(3 n −1)/2 1,5,12,22,35,...
  • 六边形H_(n)=n(2n−1)1,6,15,28,45,...

T_(285)= P_(165)= H_(143)= 40755。
找出下一个三角形数,它也是五边形和六边形。
是任务描述。
我知道六边形数是三角形数的一个子集,这意味着你只需要找到一个Hn=Pn的数字。但是我似乎无法让我的代码工作。我只知道java语言,这就是为什么我在网上找不到解决方案的原因。无论如何,希望有人能帮助。这是我的代码

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");
                }
            }

            }

    }
}

我意识到这可能是一个非常缓慢和低效的代码,但这并不关心我在这一点上,我只关心找到下一个数字,即使它会采取我的电脑年。

juzqafwq

juzqafwq1#

我们知道T285 = P165 = H143 = 40755。我们从nt=286np=166nh=144开始,分别计算出三角形,五边形和六边形的数字。无论哪个数字最小,我们都将其n的值提高。继续这样做,直到所有数字都相等,你就得到了答案。
这个算法的Python实现在我的计算机上运行时间为0.1秒。
您的代码存在溢出问题。当答案适合32位int时,临时值i * (3 * i - 1)在到达答案之前溢出。使用64位long值修复您的代码。

sirbozc5

sirbozc52#

你的代码看起来会很快产生正确的答案。如果你在循环结束后打印结果,while循环可以简化:

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);

注意:你的内部循环看起来很像我的C++解决方案的内部循环,它在我的机器上大约0.002秒就产生了想要的答案。

xxls0lw8

xxls0lw83#

另一种需要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;
    }

}

改善

  • 您已经指定:六边形数是三角形数,但我添加一个简短的证明:k*(2*k-1)可以写成如下形式i*(i+1)/2,如果i = 2*k-1
  • 在这种情况下,可以删除isTriangle
  • 性能将是相似的,因为该函数很少被调用(只有当数字是五边形时才被调用)。
cvxl0en2

cvxl0en24#

数学

关键是:
ti = hi*2 - 1
其中tihi分别是T和H的索引。
这意味着对于任何H(hi),总是有一个T(ti)
因而只需要求出P(pi) = H(hi),并即可以线性求解。

代码

  • (单位:golang)*
  • 解决方案:
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
}
  • 测试用例:
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
        }
    }
}

结果

  • (在旧笔记本电脑上运行)*
  • 问题44:

大约花了2ms

=== RUN   TestNextAllEqual_first
v = 1533776805, indices:
    ti = 55385, pi = 31977, hi = 27693

--- PASS: TestNextAllEqual_first (0.00s)
  • 以在有符号的32位的范围内找到所有这样的索引。

它花费了大约5.4s,有4个这样的值,不包括开始的(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)

相关问题