Java'da faktöriyel hesaplayan bir yöntem var mı?


105

Henüz bulamadım. Bir şey mi kaçırdım? Faktöriyel yöntemin yeni başlayanlar için yaygın bir örnek program olduğunu biliyorum. Ancak, bunun yeniden kullanılması için standart bir uygulamaya sahip olmak yararlı olmaz mıydı? Böyle bir yöntemi standart tiplerle (Örn. İnt, long ...) ve BigInteger / BigDecimal ile de kullanabilirim.

Yanıtlar:


26

Faktöriyel için bir kütüphane fonksiyonuna sahip olmanın yararlı olacağını sanmıyorum. Etkili faktör uygulamalarıyla ilgili pek çok araştırma var. İşte bir avuç uygulama.


188
Faktöriyel için bir kütüphane işlevine sahip olmak neden yararlı değil?
midnite

17
Pek çok insan gerçek kodda faktöriyellere ihtiyaç duymaz. Bunu yaparsanız, muhtemelen bazı ileri düzey matematik veya istatistik yapıyorsunuzdur, bu durumda büyük olasılıkla zaten özel bir faktör uygulamasına sahip bir matematik kitaplığı kullanacaksınız.
mikera

3
Gama işlevi çok kullanışlıdır, bu yüzden C ++ standart kitaplığına dahil edilmiştir.
Columbo

2
Bana öyle geliyor ki @KarlthePagan , faktöriyel için standart bir kütüphane işlevine sahip olmanın yararlı olmadığı anlamına geliyor - bu doğru mu?
dantiston

bu yüzden röportajda kendin
yapıp yapamayacağını soracaklardı

59

Apache Commons Math , MathUtils sınıfında birkaç faktöryel yönteme sahiptir .


1
Evet. İyi şeyler. Float ve düz olmayan sayılar (MathUtils.factorial (int) ve MathUtils.factorialDouble (int)) için faktöriyel bir uygulama ve ayrıca n! (MathUtils.factorialLog (int))
Tomasz Błachowicz

3
şu anda ArithmeticUtils içinde.
MarianP

6
ArithmeticUtils.factorial
görünüşe göre

Lütfen bağlantıları kullanmayın! Yok olma eğilimindedirler (bunların TÜMÜNÜ olduğu gibi).
SMBiggs

1
@ScottBiggs Cevaptaki her iki bağlantı da iyi çalışıyor.
Bill Lizard

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'ın Big Numbers versiyonu :

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

Ayrıca bir tamsayının faktöriyelini istediğinizi varsayıyorsunuz
Andrew

Bu çözüm BigInteger sınıfını kullanmalıdır.
Oleg Abrazhaev

2
Büyük Sayılar Sürümü: genel statik BigInteger faktöryel (BigInteger n) {BigInteger factorial = BigInteger.valueOf (1); for (int i = 1; i <= n.intValue (); i ++) {factorial = factorial.multiply (BigInteger.valueOf (i)); } faktöryel dönüş; }
HoldOffHunger

23

Pratikte çıplak faktöriyellere nadiren ihtiyaç duyulur. Çoğu zaman aşağıdakilerden birine ihtiyacınız olacak:

1) bir faktöriyel diğerine bölmek veya

2) yaklaşık kayan nokta cevabı.

Her iki durumda da, basit özel çözümlerle daha iyi olursunuz.

(1) durumunda, x = 90 ise diyelim! / 85 !, sonra sonucu 90'ı tutmaya gerek kalmadan x = 86 * 87 * 88 * 89 * 90 şeklinde hesaplayacaksınız! bellekte :)

Durum (2), "Stirling yaklaşımı" için google.


3
Karşı örnek: N elemanlı permütasyon sayısının hesaplanması, çıplak faktöriyel gerektirir ve permütasyonları tutmak için bir yapı tahsis etmek istiyorsanız gereklidir.
Mark Jeronimus

İyi bir nokta! İlk sorum 90! / 85! 5 ortak paydası nedeniyle basitleştirildi, ancak aslında 85'in ortak paydasıydı! 90! / 85! = 90 * 89 * 88 * 87 * 86 * 85! / 85 !. Bu eşitlikte daha net görebilirsiniz.
HoldOffHunger


6

Faktöriyeller yeni başlayan programcılar için güzel bir alıştırma yapsalar da , çoğu durumda pek kullanışlı değildirler ve herkes faktöriyel bir fonksiyonun nasıl yazılacağını bilir, bu yüzden genellikle ortalama bir kütüphanede değildirler.


6
Size katılıyorum, daha önemli matematiksel fonksiyonlar var. Ama benim gözümde bu yöntem standart olmalı ki insanlar onu yeniden kullanabilsin. Birden çok kişi tarafından birden çok kez uygulamaya gerek yoktur. Eğitim amaçlı olabilir. Ancak günlük çalışma için modası geçmiş. Bu benim fikrim. Her neyse, cevapladığınız için teşekkürler. Bunu kendi başıma yapacağım - başka bir zaman.

Önerdiğiniz standardizasyonun faydası nedir? Standart kitaplığa yöntem eklemek maliyetsiz değildir. Diğerlerinin de belirttiği gibi, tek bir en iyi çözüm yoktur. Hangisini dilde inşa etmeyi öneriyorsunuz? Standart kitaplıkta bir yönteme sahip olmak, probleminizi anlamak için size zaman kazandırmayacaktır ve bunu yaptıktan sonra, iş için en uygun uygulamayı da seçebilirsiniz.
Matt G

2
"... ve herkes bir faktöryel işlevin nasıl yazılacağını bilir" chaosinmotion.com/blog/?p=622
James P.

4
Katılmıyorum. Yazılım tasarımının pek çok alanında ihtiyaç duyulan Kombinatorikler için faktörlere ihtiyaç vardır. Yerleşik bir matematik kitaplığındaki faktöriyelleri içermeyen argüman, yerleşik bir matematik kitaplığına sahip olmamakla aynı argümandır.
LateralFractal

Ne kadar olağanüstü bir mantık. Kesinlikle yıldız. Java.lang.Math sınıf tasarımcılarının abs () yöntemlerini bu kitaplığa eklediklerinde bundan habersiz olmaları çok kötü.
Igor Soudakevitch

6

Bunun bir arama tablosuyla en hızlı yol olacağına inanıyorum:

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];
}

Yerel tür için long(8 bayt), yalnızca20!

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

Açıkçası, 21!taşmaya neden olacaktır.

Bu nedenle, yerel tür için longyalnızca maksimum 20!sayıya izin verilir, anlamlı ve doğrudur.


1
Oldukça güzel fikir. Faktöriyel ilk 20'nin muhtemelen yeterli olduğunu göz önünde bulundurarak, sağlanan verilerle Math sınıfına statik sabitler eklerim (uygulama her başladığında hesaplamaya gerek yoktur). Pek çok insanın kodlarında faktöriyellere ihtiyaç duymaması, matematik dersinde bunu desteklememek için zayıf bir bahane.
TG

6

Faktöriyel çok hızlı büyüdüğü için, özyinelemeyi kullanırsanız yığın taşması sorun olmaz. Aslında 20'nin değeri! bir Java uzunluğunda temsil edilebilecek en büyüğüdür. Bu nedenle, aşağıdaki yöntem faktöriyel (n) 'yi hesaplar veya n çok büyükse bir IllegalArgumentException oluşturur.

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

Aynı şeyi yapmanın başka bir (daha havalı) yolu, Java 8'in akış kitaplığını şu şekilde kullanmaktır:

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'in akışlarını kullanan Factorials hakkında daha fazla bilgi edinin



6

Kısa cevap: özyinelemeyi kullanın.

Bir yöntem oluşturabilir ve bu yöntemi aynı yöntemin içinde özyinelemeli olarak çağırabilirsiniz:

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
özyinelemeli fonksiyonlar güzeldir, ancak birisi gerçekten büyük bir fatiorial saymayı denerse, StackOverflowException ile sonuçlanacaktır;) + Emin değilim, ama bence özyineleme, eski döngü yönteminden daha yavaş;)
TG

stackoverflow istisnasıyla sonuçlanacağını nasıl bilebilirsiniz? @TG
gumuruh

2
Bu basit. her biri özyinelemeli olarak mevcut yeri yığına koyar, böylece program, yöntem çağrısı bittikten sonra geri dönmek için yerin 'belleğine' sahip olacaktır. Yığının sınırları vardır. Kendinizi değişim Yukarıdaki kodda denemek için denemek için System.out.println(calc(10));için System.out.println(calc(Long.MAX_VALUE));size :) oldukça uzun stactrace almalısınız
TG

@TG net Denedim olmak benim özyinelemeli yöntemi ile çalışıyor BigInteger. 8020Bana sonucu 613578884952214809325384...veren 27831ondalık basamaklı sayının faktöriyelini hesaplamaya çalıştım . Yani sayılarla çalışırken bile bu kadar büyük bir hayır Stackoverflowatılmayacak. Tabii ki haklısın ama pratik kullanımda bu kadar büyük rakamlar olduğundan şüpheliyim :-)
Moritz Schmidt

3

Bunu dene

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-loop'ta bir hata olduğuna inanıyorum: öyle olmalı i <= value. For-döngüsü biraz optimize edilebilir (int i = 2; i <= value; i++).
Chris

2

Gerçek çarpımların sadece yarısında faktöriyelleri bulmak için harika bir numara buldum.

Lütfen sabırlı olun çünkü bu biraz uzun bir gönderi.

Çift Sayılar için: Çarpmayı çift sayılarla yarıya indirmek için, n / 2 çarpanları elde edersiniz. İlk faktör, faktöriyelini aldığınız sayı olacaktır, ardından sonraki bu sayı artı bu sayı eksi iki olacaktır. Bir sonraki sayı, önceki sayı artı son eklenen sayı eksi iki olacaktır. Son eklediğiniz sayı iki olduğunda (yani 2) işiniz bitti . Muhtemelen bu pek mantıklı gelmedi, bu yüzden size bir örnek vereyim.

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

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

Unutmayın ki 8 ile başladım, sonra ilk eklediğim sayı 6, sonra 4, sonra 2, eklenen her sayı kendisinden önce eklenen sayıdan iki eksi olacaktı. Bu yöntem, en küçük sayıları en büyük sayılarla çarpmaya eşdeğerdir, sadece daha az çarpma ile, örneğin:

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

Basit değil mi :)

Şimdi Tek Sayılar İçin: Eğer sayı tekse, toplama aynıdır, her seferinde iki çıkarırsınız, ancak üçte durursunuz. Ancak faktörlerin sayısı değişir. Sayıyı ikiye bölerseniz, 5 ile biten bir sayı elde edersiniz. Bunun nedeni, uçları birlikte çarparsak ortadaki sayı ile kalmamızdır. Temel olarak, bunların tümü, ikiye bölünen sayıya eşit bir dizi faktörü yuvarlayarak çözülebilir. Bu muhtemelen matematiksel geçmişi olmayanlar için de pek mantıklı gelmiyordu, öyleyse bir örnek yapayım:

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

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

Not: Bu yöntemi beğenmezseniz, tek sayıdan önceki çift sayının faktöriyelini de alabilir (bu durumda sekiz) ve tek sayı ile çarpabilirsiniz (yani 9! = 8! * 9).

Şimdi bunu Java'da uygulayalım:

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

isFirststatik olarak bildirilen bir boole değişkendir; önceki toplamı değiştirmek istemediğimiz 1. durum için kullanılır.

Tek sayıların yanı sıra çiftleri de deneyin.


2

Özyinelemeyi kullanabilirsiniz.

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

ve sonra yukarıdaki yöntemi (işlevi) oluşturduktan sonra:

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

1

Bir faktöriyel için düşünebildiğim tek iş kullanımı Erlang B ve Erlang C formülleridir ve herkes bir çağrı merkezinde veya telefon şirketi için çalışmaz. Bir özelliğin iş için kullanışlılığı, genellikle bir dilde neyin ortaya çıkacağını belirler gibi görünüyor - ana dillerdeki tüm veri işleme, XML ve web işlevlerine bakın.

Bunun gibi bir şey için bir faktöriyel snippet veya kitaplık işlevi tutmak kolaydır.


1

Faktörleri hesaplamak için çok basit bir yöntem:

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 kullandım çünkü çok büyük sayıları tutabilirler, ancak int, long, float vb. Gibi diğer türleri de kullanabilirsiniz.

Not: Bu en iyi çözüm olmayabilir, ancak kodlamada yeniyim ve faktöriyelleri hesaplayabilecek basit bir kod bulmam yıllar aldı, bu yüzden yöntemi kendim yazmak zorunda kaldım ama bunu buraya koyuyorum, bu yüzden benim gibi diğer insanlara yardımcı oluyor .


1

Özyineleme sürümünü de kullanabilirsiniz.

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

Özyineleme, özyinelemeleri itmek ve çoğaltmak zorunda olduğu için genellikle daha az etkilidir, bu nedenle yineleme daha hızlıdır. Öte yandan, özyinelemeli sürümler daha az yerel değişken kullanır veya hiç kullanmaz, bu da avantajdır.


1

Factorial, son derece artan ayrık fonksiyonlardır. Bu yüzden BigInteger kullanmanın int kullanmaktan daha iyi olduğunu düşünüyorum. Negatif olmayan tamsayıların faktöriyel hesaplaması için aşağıdaki kodu uyguladım.Bir döngü kullanmak yerine özyineleme kullandım.

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

Burada büyük tamsayının aralığı

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

Bununla birlikte, yukarıda verilen faktöryel yöntemin aralığı, imzasız BigInteger kullanılarak iki katına kadar uzatılabilir.


1

Hesaplamak için tek bir satırımız var:

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

1

Oldukça basit bir yöntem

    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
Bence OP, nasıl yazılacağını değil, API'lerde bir işlev olup olmadığını soruyor. Ayrıca 0! = 1 - kodunuzu bu durumu içerecek şekilde güncellemek isteyebilirsiniz.
SL Barth - Monica'yı Eski Haline

sonuç her zaman 0
Tom Brito

0

Yinelemeli uygulamamız gerekiyor. Özyinelemeli olarak uygularsak, girdi çok büyük olursa (yani 2 milyar) StackOverflow'a neden olur. Ve faktöriyel bir sayı belirli bir türün maksimum sayısından (yani int için 2 milyar) daha büyük olduğunda aritmatik bir taşmayı önlemek için BigInteger gibi ilişkisiz boyut numarası kullanmamız gerekir. İnt'i, taşmadan önce en fazla 14 faktöriyel için ve en fazla 20 faktöriyel için kullanabilirsiniz.

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 kullanamıyorsanız, bir hata denetimi ekleyin.

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 veya BigInteger kullanmalı;)
AxelH

0

while döngüsü (küçük sayılar için)

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

Bunu EDX'ten aldım, kullan! buna özyineleme denir

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

0

özyinelemeli:

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

while döngüsü ile:

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

0

DİNAMİK PROGRAMLAMAYI KULLANMAK ETKİLİDİR

tekrar tekrar hesaplamak için kullanmak istiyorsanız (önbelleğe alma gibi)

Java kodu:

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

özyineleme kullanmak en basit yöntemdir. N'nin faktöriyelini bulmak istiyorsak, N = 1 ve N> 1 olan iki durumu dikkate almalıyız çünkü faktöriyelde N, N-1, N-2 ,,,,,'yi 1'e kadar çarpmaya devam ediyoruz. N = 0'a gidin, cevap için 0 alacağız. faktöriyelin sıfıra ulaşmasını durdurmak için aşağıdaki özyinelemeli yöntem kullanılır. Faktöriyel fonksiyonun içinde, N> 1 iken, dönüş değeri faktöriyel fonksiyonun başka bir başlangıcı ile çarpılır. bu, kodu N = 1'e ulaşana kadar yinelemeli olarak faktöriyel () çağırarak tutacaktır. N = 1 durumu için, kendisini N (= 1) döndürür ve çarpılan dönüş N'nin önceden oluşturulmuş tüm sonuçları N ile çarpılır = 1. Böylece faktöriyel sonucu verir.

static int factorial(int N) {
    if(N > 1) { 
    return n * factorial(N - 1);
    }
    // Base Case N = 1
    else { 
    return N;
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.