有没有一种计算Java中阶乘的方法?


105

我还没找到。我错过了什么?我知道阶乘方法是初学者常用的示例程序。但是,有一个标准的实现来重用它不是有用的吗?我可以对标准类型(例如int,long ...)和BigInteger / BigDecimal使用这样的方法。

Answers:


26

我认为为阶乘提供库功能不会有用。关于有效阶乘实现的研究很多。这里有一些实现。


188
为什么具有阶乘的库功能没有用?
2013年

17
实际代码中实际上并没有多少人需要阶乘。如果这样做,那么您可能正在做一些高级数学或统计,在这种情况下,您很可能已经在使用带有专门阶乘实现的数学库。
mikera

3
gamma函数非常有用,这就是为什么它包含在C ++标准库中的原因。
Columbo'9

2
在我看来,@ KarlthePagan意味着拥有用于阶乘的标准库功能没有用-是正确的吗?
丹迪斯顿'17

所以他们会在面试时问你,看看你是否可以自己做*我的案子
摩尔多瓦州

59

Apache Commons MathMathUtils类中有一些析因方法。


1
是的 好东西。对于浮点数和非整数数,有一个阶乘的实现(MathUtils.factorial(int)和MathUtils.factorialDouble(int)),并且还有n的有用自然对数!(MathUtils.factorialLog(int))
TomaszBłachowicz09年

3
目前在ArithmeticUtils中。
MarianP

6
ArithmeticUtils.factorial现在
似乎

请不要使用链接!它们倾向于消失(就像所有这些都消失了一样)。
SMBiggs

1
@ScottBiggs答案中的两个链接都工作正常。
比尔蜥蜴

40
public class UsefulMethods {
    public static long factorial(int number) {
        long result = 1;

        for (int factor = 2; factor <= number; factor++) {
            result *= factor;
        }

        return result;
    }
}

HoldOffHunger的 Big Numbers版本:

public static BigInteger factorial(BigInteger number) {
    BigInteger result = BigInteger.valueOf(1);

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

    return result;
}

另外,您假设您要使用整数的阶乘
安德鲁(Andrew)

此解决方案应使用BigInteger类。
Oleg Abrazhaev '16

2
大数字版本:公共静态BigInteger阶乘(BigInteger n){BigInteger阶乘= BigInteger.valueOf(1); 对于(int i = 1; i <= n.intValue(); i ++){factorial = factorial.multiply(BigInteger.valueOf(i)); }返回阶乘;}
HoldOffHunger

23

在实践中几乎不需要裸露阶乘。通常,您将需要以下之一:

1)将一个阶乘除以另一个,或

2)近似浮点答案。

在这两种情况下,使用简单的自定义解决方案都会更好。

在情况(1)中,假设x = 90!/ 85 !,那么您将计算出x = 86 * 87 * 88 * 89 * 90的结果,而无需保持90!在记忆中 :)

在情况(2)中,用Google搜索“斯特林近似值”。


3
反例:计算带有N个元素的排列数仅需进行阶乘,如果要分配一个结构来保存排列,则需要此乘数。
Mark Jeronimus '16

好点子!我的第一个问题是90!/ 85!由于公分母为5,所以简化了下来,但实际上,它是公分母85!。90!/ 85!= 90 * 89 * 88 * 87 * 86 * 85!/ 85!。通过这种平等,您可以更清楚地看到它。
HoldOffHunger


6

尽管阶乘对于初学者来说是一个很好的练习,但是在大多数情况下它们并不是很有,而且每个人都知道如何编写阶乘函数,因此它们通常不在普通库中。


6
我同意你的看法,还有更重要的数学函数。但是在我看来,这种方法应该是标准的,以便人们可以重复使用。无需由多个人多次实施。出于教育目的,可以做。但是,每天的工作都过时了。这是我的意见。无论如何,谢谢您的回答。我会自己做的-再一次。

您提议的标准化有什么好处?向标准库添加方法并非没有成本。正如其他人指出的那样,没有唯一的最佳解决方案。您建议将哪一种嵌入到语言中?在标准库中有一个方法并不会节省您理解问题的时间,一旦完成,您最好选择最适合该工作的实现。
Matt G

2
“ ...而且每个人都知道如何编写阶乘函数” chaosinmotion.com/blog/?p=622
James P.

4
不同意。Combinatorics需要阶乘,这在软件设计的许多领域都需要。内置数学库中不包含阶乘的参数与没有内置数学库时的参数相同。
LateralFractal 2013年

多么出色的逻辑。绝对是恒星。不幸的是,当java.lang.Math类设计器将abs()方法包含到该库中时,他们对此一无所知。
Igor Soudakevitch

6

我相信通过查找表,这将是最快的方法:

private static final long[] FACTORIAL_TABLE = initFactorialTable();
private static long[] initFactorialTable() {
    final long[] factorialTable = new long[21];
    factorialTable[0] = 1;
    for (int i=1; i<factorialTable.length; i++)
        factorialTable[i] = factorialTable[i-1] * i;
    return factorialTable;
}
/**
 * Actually, even for {@code long}, it works only until 20 inclusively.
 */
public static long factorial(final int n) {
    if ((n < 0) || (n > 20))
        throw new OutOfRangeException("n", 0, 20);
    return FACTORIAL_TABLE[n];
}

对于本机类型long(8个字节),它最多只能容纳20!

20! = 2432902008176640000(10) = 0x 21C3 677C 82B4 0000

显然,21!会引起溢出。

因此,对于本机类型long,仅20!允许最大数量,有意义和正确的。


1
很好的主意。考虑到,阶乘20可能就足够了,我将使用提供的那些数据向Math类添加一个静态常量(无需在每次应用启动时都进行计算)。事实是,没有多少人在他们的代码中需要阶乘,这是在数学课中不支持它的一个不好的借口。
TG

6

因为阶乘增长如此之快,所以如果您使用递归,堆栈溢出就不会成为问题。其实值20!是Java long中最大的代表。因此,如果n太大,则以下方法将计算factorial(n)或引发IllegalArgumentException。

public long factorial(int n) {
    if (n > 20) throw new IllegalArgumentException(n + " is out of range");
    return (1 > n) ? 1 : n * factorial(n - 1);
}

另一种(更酷)的方法来做同样的事情是使用Java 8的流库,如下所示:

public long factorial(int n) {
    if (n > 20) throw new IllegalArgumentException(n + " is out of range");        
    return LongStream.rangeClosed(1, n).reduce(1, (a, b) -> a * b);
}

阅读有关使用Java 8流的阶乘的更多信息



6

简短的答案是:使用递归。

您可以创建一个方法,然后在同一方法内递归调用该方法:

public class factorial {

    public static void main(String[] args) {
        System.out.println(calc(10));
    }

    public static long calc(long n) {
        if (n <= 1)
            return 1;
        else
            return n * calc(n - 1);
    }
}

5
递归函数很好,但是如果有人尝试计算一个非常大的喜好,它们将最终导致StackOverflowException;)+我不确定,但我认为递归比好的旧循环方法要慢;)
TG

你怎么知道它将以stackoverflow异常结束?@TG
gumuruh

2
那很简单。每个递归都将当前位置放到堆栈上,因此程序在方法调用完成后将有“内存”可返回的位置。堆栈有其局限性。尝试为自己的上述变化的代码尝试System.out.println(calc(10));System.out.println(calc(Long.MAX_VALUE));你应该会很长stactrace :)
TG

@TG要明确一点,我尝试与一起使用的递归方法BigInteger。我试图计算数字的阶乘,8020这使我的结果613578884952214809325384...具有27831小数位。因此,即使处理数字,也Stackoverflow不会抛出巨大的错误。当然,您说得对,但我怀疑实际使用中的数字是否很大:-)
Moritz Schmidt,

3

试试这个

public static BigInteger factorial(int value){
    if(value < 0){
        throw new IllegalArgumentException("Value must be positive");
    }

    BigInteger result = BigInteger.ONE;
    for (int i = 2; i <= value; i++) {
        result = result.multiply(BigInteger.valueOf(i));
    }

    return result;
}

3
我相信for循环中存在一个错误:应该是i <= value。for循环可以稍微优化为(int i = 2; i <= value; i++)
克里斯(Chris)

2

我发现了一个惊人的技巧,可以在一半的实际乘法中找到阶乘。

请耐心等待,因为这是一个漫长的过程。

对于偶数: 要使偶数减半,您将得到n / 2个因子。第一个因素将是您要提取的阶乘的数字,然后第二个因素将是该数字加上该数字减去2。下一个数字将是前一个数字加上最后添加的数字减去2。当您添加的最后一个数字为2(即2)时,您就完成了。那可能没有多大意义,所以让我举个例子。

8! = 8 * (8 + 6 = 14) * (14 + 4 = 18) * (18 + 2 = 20)

8! = 8 * 14 * 18 * 20 which is **40320** 

请注意,我从8开始,然后添加的第一个数字是6,然后是4,然后是2,每个添加的数字比之前添加的数字少两个。此方法等效于用最小的数乘以最大的数,而乘以较少的乘数,如下所示:

8! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 
8! = (1 * 8) * (2 * 7) * (3 * 6) * (4 * 5)
8! = 8 * 14 * 18 * 20

是不是很简单:)

现在,对于奇数:如果数字是奇数,则加法是相同的,就像您每次都减去两个一样,但是停在三个位置。但是,因素的数量会发生变化。如果将数字除以二,最终将得到以.5结尾的数字。原因是,如果我们将两端相乘,则剩下中间的数字。基本上,所有这些都可以通过求解等于四舍五入的因子来解决。没有数学背景的人,这对大脑也可能没有多大意义,所以让我举一个例子:

9! = 9 * (9 + 7 = 16) * (16 + 5 = 21) * (21 + 3 = 24) * (roundUp(9/2) = 5)

9! = 9 * 16 * 21 * 24 * 5 = **362880**

注意:如果您不喜欢这种方法,您也可以将奇数之前的偶数阶乘(在本例中为8),然后乘以奇数(即9!= 8!* 9)。

现在让我们用Java实现它:

public static int getFactorial(int num)
{
    int factorial=1;
    int diffrennceFromActualNum=0;
    int previousSum=num;

    if(num==0) //Returning  1 as factorial if number is 0 
        return 1;
    if(num%2==0)//  Checking if Number is odd or even
    { 
        while(num-diffrennceFromActualNum>=2)
        {
            if(!isFirst)
            {
                previousSum=previousSum+(num-diffrennceFromActualNum);  
            }
            isFirst=false;
            factorial*=previousSum;
            diffrennceFromActualNum+=2;
        }
    }
    else // In Odd Case (Number * getFactorial(Number-1))
    {
        factorial=num*getFactorial(num-1);
    }
    return factorial;
}

isFirst是一个声明为静态的布尔变量;它用于第一种情况,我们不想更改先前的总和。

尝试使用偶数和奇数。


2

您可以使用递归。

public static int factorial(int n){    
      if (n == 0)    
        return 1;    
      else    
        return(n * factorial(n-1));    
     }

然后创建上面的方法(函数)后:

System.out.println(factorial(number of your choice));  
    //direct example
    System.out.println(factorial(3));

1

我可以想到的阶乘唯一的商业用途是Erlang B和Erlang C公式,并不是每个人都在呼叫中心或电话公司工作。功能对业务的有用性似乎通常决定了语言中显示的内容-查看主要语言中的所有数据处理,XML和Web函数。

对于这样的事情,保留阶乘代码片段或库函数很容易。


1

一种计算阶乘的非常简单的方法:

private double FACT(double n) {
    double num = n;
    double total = 1;
    if(num != 0 | num != 1){
        total = num;
    }else if(num == 1 | num == 0){
        total = 1;
    }
    double num2;
    while(num > 1){
        num2 = num - 1;
        total = total * num2;
        num = num - 1;
    }
    return total;
}

我使用double是因为它们可以容纳大量数字,但是您可以使用其他任何类型,例如int,long,float等。

PS:这可能不是最好的解决方案,但是我是编码的新手,花了我很多年才能找到一个可以计算阶乘的简单代码,所以我不得不自己编写方法,但是我将其放在此处,以帮助其他像我这样的人。


1

您也可以使用递归版本。

static int myFactorial(int i) {
    if(i == 1)
        return;
    else
        System.out.prinln(i * (myFactorial(--i)));
}

由于必须推送和弹出递归,因此递归效率通常较低,因此迭代速度更快。另一方面,递归版本使用较少或不使用局部变量,这是有利的。


1

阶乘是离散函数的高度增加,因此我认为使用BigInteger比使用int更好。我已经实现了以下代码,用于计算非负整数的阶乘。

public  BigInteger factorial(BigInteger x){     
    if(x.compareTo(new BigInteger("1"))==0||x.compareTo(new BigInteger("0"))==0)
        return new BigInteger("1");
    else return x.multiply(factorial(x.subtract(new BigInteger("1")))); 
}

这里大整数的范围是

-2^Integer.MAX_VALUE (exclusive) to +2^Integer.MAX_VALUE,
where Integer.MAX_VALUE=2^31.

但是,通过使用无符号BigInteger,以上给出的阶乘方法的范围可以扩展到两倍。


1

我们只有一行来计算它:

Long factorialNumber = LongStream.rangeClosed(2, N).reduce(1, Math::multiplyExact);

1

一个相当简单的方法

    for ( int i = 1; i < n ; i++ )
    {
            answer = answer * i;
    }

1
    /**
import java liberary class

*/
import java.util.Scanner;

/* class to find factorial of a number
*/

public class factorial
{
public static void main(String[] args)
{

// scanner method for read keayboard values

    Scanner factor= new Scanner(System.in);

    int n;
    double total = 1;
    double sum= 1;

    System.out.println("\nPlease enter an integer: ");
    n = factor.nextInt();

// evaluvate the integer is greater than zero and calculate factorial

if(n==0)

{
    System.out.println(" Factorial of 0 is 1");
}
else if (n>0)
{
    System.out.println("\nThe factorial of " + n + " is " );

    System.out.print(n);

    for(int i=1;i<n;i++)
    {
        do // do while loop for display each integer in the factorial
              {
                System.out.print("*"+(n-i) );
              }

        while ( n == 1);

      total = total * i;

    }

// calculate factorial
sum= total * n;


// display sum of factorial

    System.out.println("\n\nThe "+ n +" Factorial is : "+" "+ sum);
}

// display invalid entry, if enter a value less than zero

else

{
    System.out.println("\nInvalid entry!!");

}System.exit(0);
}
}

0
public static int fact(int i){
    if(i==0)
       return 0;
    if(i>1){
       i = i * fact(--i);
    }

   return i;
}

1
我认为OP正在询问API中是否有一个函数,而不是如何编写一个函数。另外,0!= 1-您可能希望更新代码以包括这种情况。
SL Barth-恢复莫妮卡2014年

将导致始终为0
汤姆·布里托

0

我们需要迭代实现。如果我们递归实现,那么如果输入很大(即20亿),它将导致StackOverflow。而且,当阶乘数变得大于给定类型的最大数(即int为20亿)时,我们需要使用BigInteger之类的未绑定大小数来避免算术溢出。溢出之前,可以将int用于最多14个阶乘,将long用于最多20个阶乘。

public BigInteger getFactorialIteratively(BigInteger input) {
    if (input.compareTo(BigInteger.ZERO) <= 0) {
        throw new IllegalArgumentException("zero or negatives are not allowed");
    }

    BigInteger result = BigInteger.ONE;
    for (BigInteger i = BigInteger.ONE; i.compareTo(input) <= 0; i = i.add(BigInteger.ONE)) {
        result = result.multiply(i);
    }
    return result;
}

如果您不能使用BigInteger,请添加错误检查。

public long getFactorialIteratively(long input) {
    if (input <= 0) {
        throw new IllegalArgumentException("zero or negatives are not allowed");
    } else if (input == 1) {
        return 1;
    }

    long prev = 1;
    long result = 0;
    for (long i = 2; i <= input; i++) {
        result = prev * i;
        if (result / prev != i) { // check if result holds the definition of factorial
            // arithmatic overflow, error out
            throw new RuntimeException("value "+i+" is too big to calculate a factorial, prev:"+prev+", current:"+result);
        }
        prev = result;
    }
    return result;
}

0
public int factorial(int num) {
        if (num == 1) return 1;
        return num * factorial(num - 1);
}

应该使用long或BigInteger;)
AxelH 2016年

0

while循环(用于小数)

public class factorial {

public static void main(String[] args) {
    int counter=1, sum=1;

    while (counter<=10) {
        sum=sum*counter;
        counter++;
   }

    System.out.println("Factorial of 10 is " +sum);
   }
}

0

我从EDX那里得到的使用它!它称为递归

   public static int factorial(int n) {
    if (n == 1) {
        return 1;
    } else {
        return n * factorial(n-1);
    }
}

0

递归:

public static int factorial(int n)
{
    if(n == 1)
    {
        return 1;
    }               
    return n * factorial(n-1);
}

使用while循环:

public static int factorial1(int n)
{
    int fact=1;
    while(n>=1)
    {
        fact=fact*n;
        n--;
    }
    return fact;
}

0

使用动态编程非常有效

如果您想使用它来一次又一次地计算(例如缓存)

Java代码:

int fact[]=new int[n+1]; //n is the required number you want to find factorial for.
int factorial(int num)
 {
    if(num==0){
     fact[num]=1;
     return fact[num];
       }
     else
       fact[num]=(num)*factorial(num-1);

     return fact[num];
 }

0

使用递归是最简单的方法。如果要查找N的阶乘,则必须考虑N = 1和N> 1的两种情况,因为在阶乘中,我们一直将N,N-1,N-2 ,,,,,乘以1。转到N = 0,我们将得到0的答案。为了使阶乘停止为零,使用了以下递归方法。在阶乘函数内部,当N> 1时,返回值将乘以阶乘函数的另一个起始值。这将使代码递归地调用factorial(),直到达到N =1。对于N = 1的情况,它将自身返回N(= 1),并且先前乘以返回N s的所有累积结果都将与N相乘。 = 1。从而给出阶乘结果。

static int factorial(int N) {
    if(N > 1) { 
    return n * factorial(N - 1);
    }
    // Base Case N = 1
    else { 
    return N;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.