在Java中实现选择表示法的好方法是什么?

发布于 2024-08-10 17:46:11 字数 409 浏览 5 评论 0原文

...最好是用Java。这就是我所拥有的:

//x choose y
public static double choose(int x, int y) {
    if (y < 0 || y > x) return 0;
    if (y == 0 || y == x) return 1;

    double answer = 1;
    for (int i = x-y+1; i <= x; i++) {
        answer = answer * i;
    }
    for (int j = y; j > 1; j--) {
        answer = answer / j;
    }
    return answer;
}

我想知道是否有更好的方法来做到这一点?

... preferably in Java. Here is what I have:

//x choose y
public static double choose(int x, int y) {
    if (y < 0 || y > x) return 0;
    if (y == 0 || y == x) return 1;

    double answer = 1;
    for (int i = x-y+1; i <= x; i++) {
        answer = answer * i;
    }
    for (int j = y; j > 1; j--) {
        answer = answer / j;
    }
    return answer;
}

I'm wondering if there's a better way of doing this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

2024-08-17 17:46:11
choose(n,k) = n! / (n-k)! k!

你可以在 O(k) 中做这样的事情:

public static double choose(int x, int y) {
    if (y < 0 || y > x) return 0;
    if (y > x/2) {
        // choose(n,k) == choose(n,n-k), 
        // so this could save a little effort
        y = x - y;
    }

    double denominator = 1.0, numerator = 1.0;
    for (int i = 1; i <= y; i++) {
        denominator *= i;
        numerator *= (x + 1 - i);
    }
    return numerator / denominator;
}

EDIT 如果 xy 很大,你会溢出得更慢(即,对于较大的 x 和 y 值来说是安全的)如果你一边做一边除以你的答案:

    double answer = 1.0;
    for (int i = 1; i <= y; i++) {
        answer *= (x + 1 - i);
        answer /= i;           // humor 280z80
    }
    return answer;
choose(n,k) = n! / (n-k)! k!

You could do something like this in O(k):

public static double choose(int x, int y) {
    if (y < 0 || y > x) return 0;
    if (y > x/2) {
        // choose(n,k) == choose(n,n-k), 
        // so this could save a little effort
        y = x - y;
    }

    double denominator = 1.0, numerator = 1.0;
    for (int i = 1; i <= y; i++) {
        denominator *= i;
        numerator *= (x + 1 - i);
    }
    return numerator / denominator;
}

EDIT If x and y are large, you will overflow more slowly (i.e., be safe for larger values of x & y) if you divide your answer as you go along:

    double answer = 1.0;
    for (int i = 1; i <= y; i++) {
        answer *= (x + 1 - i);
        answer /= i;           // humor 280z80
    }
    return answer;
混浊又暗下来 2024-08-17 17:46:11

您正在处理的数字将变得非常大,并且很快就会超过 double 值的精度,从而给您带来意想不到的错误结果。因此,您可能需要考虑任意精度的解决方案,例如使用 java.math.BigInteger,它不会遇到此问题。

The numbers you are dealing with will become quite large and will quickly exceed the precision of double values, giving you unexpectedly wrong results. For this reason you may want to consider an arbitrary-precision solution such as using java.math.BigInteger, which will not suffer from this problem.

谎言 2024-08-17 17:46:11

老实说,你所得到的对我来说看起来很清楚。诚然,我会将 return 语句放在大括号中,因为这是我遵循的约定,但除此之外,它看起来已经尽善尽美了。

我想我可能会颠倒第二个循环的顺序,以便两个循环都是升序的。

正如格雷格所说,如果您需要获得大量数据的准确答案,您应该考虑其他数据类型。鉴于结果应始终为整数,您可能需要选择 BigInteger(尽管进行了所有除法,结果始终是整数):

public static BigInteger choose(int x, int y) {
    if (y < 0 || y > x) 
       return BigInteger.ZERO;
    if (y == 0 || y == x) 
       return BigInteger.ONE;

    BigInteger answer = BigInteger.ONE;
    for (int i = x - y + 1; i <= x; i++) {
        answer = answer.multiply(BigInteger.valueOf(i));
    }
    for (int j = 1; j <= y; j++) {
        answer = answer.divide(BigInteger.valueOf(j));
    }
    return answer;
}

What you've got looks pretty clear to me, to be honest. Admittedly I'd put the return statements in braces as that's the convention I follow, but apart from that it looks about as good as it gets.

I think I'd probably reverse the order of the second loop, so that both loops are ascending.

As Greg says, if you need to get accurate answers for large numbers, you should consider alternative data types. Given that the result should always be an integer, you might want to pick BigInteger (despite all the divisions, the result will always be an integer):

public static BigInteger choose(int x, int y) {
    if (y < 0 || y > x) 
       return BigInteger.ZERO;
    if (y == 0 || y == x) 
       return BigInteger.ONE;

    BigInteger answer = BigInteger.ONE;
    for (int i = x - y + 1; i <= x; i++) {
        answer = answer.multiply(BigInteger.valueOf(i));
    }
    for (int j = 1; j <= y; j++) {
        answer = answer.divide(BigInteger.valueOf(j));
    }
    return answer;
}
寒江雪… 2024-08-17 17:46:11

我用 C# 编写了此代码,但我尝试使其尽可能适用于 Java。

源自其中一些来源,加上我的一些小东西。

代码:

public static long BinomialCoefficient(long n, long k)
{
    if (n / 2 < k)
        return BinomialCoefficient(n, n - k);

    if (k > n)
        return 0;

    if (k == 0)
        return 1;

    long result = n;
    for (long d = 2; d <= k; d++)
    {
        long gcd = (long)BigInteger.GreatestCommonDivisor(d, n);
        result *= (n / gcd);
        result /= (d / gcd);
        n++;
    }

    return result;
}

I coded this in C#, but I tried to make it as applicable as possible to Java.

Derived from some of these sources, plus a couple small things from me.

Code:

public static long BinomialCoefficient(long n, long k)
{
    if (n / 2 < k)
        return BinomialCoefficient(n, n - k);

    if (k > n)
        return 0;

    if (k == 0)
        return 1;

    long result = n;
    for (long d = 2; d <= k; d++)
    {
        long gcd = (long)BigInteger.GreatestCommonDivisor(d, n);
        result *= (n / gcd);
        result /= (d / gcd);
        n++;
    }

    return result;
}
ˇ宁静的妩媚 2024-08-17 17:46:11

为了

N!/((R!)(NR)!)

使用这个(伪代码)

if (R>N) return 0;

long r = max(R, N-r)+1;
if (R==N) return 1;

for (long m = r+1, long d = 2; m <= N; m++, d++ ) {
    r *= m;
    r /= d;
}
return r;

for

N!/((R!)(N-R)!)

use this (Pseudocode)

if (R>N) return 0;

long r = max(R, N-r)+1;
if (R==N) return 1;

for (long m = r+1, long d = 2; m <= N; m++, d++ ) {
    r *= m;
    r /= d;
}
return r;
但可醉心 2024-08-17 17:46:11

此版本不需要 BigInteger 或浮点算术,并且对于小于 62 的所有 n 都不会出现溢出错误。超过 28 的 62 是第一对导致溢出的结果。

public static long nChooseK(int n, int k) {
    k = Math.min(k, n - k);

    if (n < 0 || k < 0)
        throw new IllegalArgumentException();

    if (k == 0)
        return 1;

    long value = n--;

    for (int i = 2; i <= k; i++) {
        value = Math.multiplyExact(value, n--);
        value /= i;
    }

    return value;
}

下面的测试证明这是真的:

@Test
void nChooseKLongVsBigInt() {
    for (int n = 0; n < 62; n++) {
        for (int k = 0; k <= n; k++) {
            assertEquals(nChooseKBigInt(n, k), BigInteger.valueOf(nChooseK(n, k)));
        }
    }
}

private BigInteger nChooseKBigInt(int n, int k) {
    return factorial(n).divide(factorial(k).multiply(factorial(n - k)));
}

private BigInteger factorial(int number) {
    BigInteger result = BigInteger.ONE;

    for (int factor = 2; factor <= number; factor++) {
        result = result.multiply(BigInteger.valueOf(factor));
    }

    return result;
}

This version does not require BigInteger or floating-point arithmetic and works without overflow errors for all n less than 62. 62 over 28 is the first pair to result in an overflow.

public static long nChooseK(int n, int k) {
    k = Math.min(k, n - k);

    if (n < 0 || k < 0)
        throw new IllegalArgumentException();

    if (k == 0)
        return 1;

    long value = n--;

    for (int i = 2; i <= k; i++) {
        value = Math.multiplyExact(value, n--);
        value /= i;
    }

    return value;
}

The following test proves that this is true:

@Test
void nChooseKLongVsBigInt() {
    for (int n = 0; n < 62; n++) {
        for (int k = 0; k <= n; k++) {
            assertEquals(nChooseKBigInt(n, k), BigInteger.valueOf(nChooseK(n, k)));
        }
    }
}

private BigInteger nChooseKBigInt(int n, int k) {
    return factorial(n).divide(factorial(k).multiply(factorial(n - k)));
}

private BigInteger factorial(int number) {
    BigInteger result = BigInteger.ONE;

    for (int factor = 2; factor <= number; factor++) {
        result = result.multiply(BigInteger.valueOf(factor));
    }

    return result;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文