C语言 我如何找到阶乘?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2416483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:48:17  来源:igfitidea点击:

How do I find a factorial?

calgorithmmathfactorial

提问by Kraken

How can I write a program to find the factorial of any natural number?

如何编写程序来查找任何自然数的阶乘?

回答by Kyle Rozendo

This will work for the factorial (although a very small subset) of positive integers:

这将适用于正整数的阶乘(虽然是非常小的子集):

unsigned long factorial(unsigned long f)
{
    if ( f == 0 ) 
        return 1;
    return(f * factorial(f - 1));
}

printf("%i", factorial(5));

Due to the nature of your problem (and level that you have admitted), this solution is based more in the concept of solving this rather than a function that will be used in the next "Permutation Engine".

由于您的问题的性质(以及您承认的级别),此解决方案更多地基于解决此问题的概念,而不是将在下一个“置换引擎”中使用的函数。

回答by Steve Jessop

This calculates factorials of non-negative integers[*] up to ULONG_MAX, which will have so many digits that it's unlikely your machine can store a whole lot more, even if it has time to calculate them. Uses the GNU multiple precision library, which you need to link against.

这计算了非负整数 [*] 的阶乘,最大为 ULONG_MAX,它有很多数字,以至于您的机器不太可能存储更多,即使它有时间计算它们。使用需要链接的 GNU 多精度库。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

void factorial(mpz_t result, unsigned long input) {
    mpz_set_ui(result, 1);
    while (input > 1) {
        mpz_mul_ui(result, result, input--);
    }
}

int main() {
    mpz_t fact;
    unsigned long input = 0;
    char *buf;

    mpz_init(fact);
    scanf("%lu", &input);
    factorial(fact, input);

    buf = malloc(mpz_sizeinbase(fact, 10) + 1);
    assert(buf);
    mpz_get_str(buf, 10, fact);
    printf("%s\n", buf);

    free(buf);
    mpz_clear(fact);
}

Example output:

示例输出:

$ make factorial CFLAGS="-L/bin/ -lcyggmp-3 -pedantic" -B && ./factorial
cc -L/bin/ -lcyggmp-3 -pedantic    factorial.c   -o factorial
100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

[*] If you mean something else by "number" then you'll have to be more specific. I'm not aware of any other numbers for which the factorial is defined, despite valiant efforts by Pascal to extend the domain by use of the Gamma function.

[*] 如果你的“数字”是其他意思,那么你必须更具体。我不知道定义阶乘的任何其他数字,尽管 Pascal 勇敢地通过使用 Gamma 函数扩展域。

回答by fortran

Why doing it in C when you can do it in Haskell:

当您可以在 Haskell 中执行时,为什么要在 C 中执行此操作

Freshman Haskell programmer

新生 Haskell 程序员

fac n = if n == 0 
           then 1
           else n * fac (n-1)

Sophomore Haskell programmer, at MIT (studied Scheme as a freshman)

麻省理工学院大二 Haskell 程序员(大一时学习 Scheme)

fac = (\(n) ->
        (if ((==) n 0)
            then 1
            else ((*) n (fac ((-) n 1)))))

Junior Haskell programmer (beginning Peano player)

初级 Haskell 程序员(初级 Peano 球员)

fac  0    =  1
fac (n+1) = (n+1) * fac n

Another junior Haskell programmer (read that n+k patterns are “a disgusting part of Haskell” 1and joined the “Ban n+k patterns”-movement [2])

另一位初级 Haskell 程序员(读到 n+k 模式是“Haskell 令人厌恶的部分” 1并加入了“禁止 n+k 模式”运动 [2])

fac 0 = 1
fac n = n * fac (n-1)

Senior Haskell programmer (voted for Nixon Buchanan Bush — “leans right”)

高级 Haskell 程序员(投票支持尼克松·布坎南·布什——“向右倾斜”)

fac n = foldr (*) 1 [1..n]

Another senior Haskell programmer (voted for McGovern Biafra Nader — “leans left”)

另一位高级 Haskell 程序员(投票给 McGovern Biafra Nader — “向左倾斜”)

fac n = foldl (*) 1 [1..n]

Yet another senior Haskell programmer (leaned so far right he came back left again!)

又一个高级 Haskell 程序员(靠得很远,他又回到了左边!)

-- using foldr to simulate foldl

fac n = foldr (\x g n -> g (x*n)) id [1..n] 1

Memoizing Haskell programmer (takes Ginkgo Biloba daily)

记忆 Haskell 程序员(每天服用 Ginkgo Biloba)

facs = scanl (*) 1 [1..]

fac n = facs !! n

Pointless (ahem) “Points-free” Haskell programmer (studied at Oxford)

Pointless (ahem) “Points-free” Haskell 程序员(在牛津学习)

fac = foldr (*) 1 . enumFromTo 1

Iterative Haskell programmer (former Pascal programmer)

迭代 Haskell 程序员(前 Pascal 程序员)

fac n = result (for init next done)
        where init = (0,1)
              next   (i,m) = (i+1, m * (i+1))
              done   (i,_) = i==n
              result (_,m) = m

for i n d = until d n i

Iterative one-liner Haskell programmer (former APL and C programmer)

迭代单行 Haskell 程序员(前 APL 和 C 程序员)

fac n = snd (until ((>n) . fst) (\(i,m) -> (i+1, i*m)) (1,1))

Accumulating Haskell programmer (building up to a quick climax)

积累 Haskell 程序员(建立快速高潮)

facAcc a 0 = a
facAcc a n = facAcc (n*a) (n-1)

fac = facAcc 1

Continuation-passing Haskell programmer (raised RABBITS in early years, then moved to New Jersey)

继续通过 Haskell 程序员(早年养兔子,然后搬到新泽西)

facCps k 0 = k 1
facCps k n = facCps (k . (n *)) (n-1)

fac = facCps id

Boy Scout Haskell programmer (likes tying knots; always “reverent,” he belongs to the Church of the Least Fixed-Point [8])

Boy Scout Haskell 程序员(喜欢打结;总是“虔诚”,他属于最小定点教会 [8])

y f = f (y f)

fac = y (\f n -> if (n==0) then 1 else n * f (n-1))

Combinatory Haskell programmer (eschews variables, if not obfuscation; all this currying's just a phase, though it seldom hinders)

组合 Haskell 程序员(避开变量,如果不是混淆的话;所有这些柯里化只是一个阶段,尽管它很少阻碍)

s f g x = f x (g x)

k x y   = x

b f g x = f (g x)

c f g x = f x g

y f     = f (y f)

cond p f g x = if p x then f x else g x

fac  = y (b (cond ((==) 0) (k 1)) (b (s (*)) (c b pred)))

List-encoding Haskell programmer (prefers to count in unary)

列表编码 Haskell 程序员(更喜欢以一元计数)

arb = ()    -- "undefined" is also a good RHS, as is "arb" :)

listenc n = replicate n arb
listprj f = length . f . listenc

listprod xs ys = [ i (x,y) | x<-xs, y<-ys ]
                 where i _ = arb

facl []         = listenc  1
facl n@(_:pred) = listprod n (facl pred)

fac = listprj facl

Interpretive Haskell programmer (never “met a language” he didn't like)

解释性 Haskell 程序员(从未“遇到过他不喜欢的语言”)

-- a dynamically-typed term language

data Term = Occ Var
          | Use Prim
          | Lit Integer
          | App Term Term
          | Abs Var  Term
          | Rec Var  Term

type Var  = String
type Prim = String


-- a domain of values, including functions

data Value = Num  Integer
           | Bool Bool
           | Fun (Value -> Value)

instance Show Value where
  show (Num  n) = show n
  show (Bool b) = show b
  show (Fun  _) = ""

prjFun (Fun f) = f
prjFun  _      = error "bad function value"

prjNum (Num n) = n
prjNum  _      = error "bad numeric value"

prjBool (Bool b) = b
prjBool  _       = error "bad boolean value"

binOp inj f = Fun (\i -> (Fun (\j -> inj (f (prjNum i) (prjNum j)))))


-- environments mapping variables to values

type Env = [(Var, Value)]

getval x env =  case lookup x env of
                  Just v  -> v
                  Nothing -> error ("no value for " ++ x)


-- an environment-based evaluation function

eval env (Occ x) = getval x env
eval env (Use c) = getval c prims
eval env (Lit k) = Num k
eval env (App m n) = prjFun (eval env m) (eval env n)
eval env (Abs x m) = Fun  (\v -> eval ((x,v) : env) m)
eval env (Rec x m) = f where f = eval ((x,f) : env) m


-- a (fixed) "environment" of language primitives

times = binOp Num  (*)
minus = binOp Num  (-)
equal = binOp Bool (==)
cond  = Fun (\b -> Fun (\x -> Fun (\y -> if (prjBool b) then x else y)))

prims = [ ("*", times), ("-", minus), ("==", equal), ("if", cond) ]


-- a term representing factorial and a "wrapper" for evaluation

facTerm = Rec "f" (Abs "n" 
              (App (App (App (Use "if")
                   (App (App (Use "==") (Occ "n")) (Lit 0))) (Lit 1))
                   (App (App (Use "*")  (Occ "n"))
                        (App (Occ "f")  
                             (App (App (Use "-") (Occ "n")) (Lit 1))))))

fac n = prjNum (eval [] (App facTerm (Lit n)))

Static Haskell programmer (he does it with class, he's got that fundep Jones! After Thomas Hallgren's “Fun with Functional Dependencies” [7])

静态 Haskell 程序员(他在课堂上做这件事,他有那个基金琼斯!在 Thomas Hallgren 的“Fun with Functional Dependencies”之后[7])

-- static Peano constructors and numerals

data Zero
data Succ n

type One   = Succ Zero
type Two   = Succ One
type Three = Succ Two
type Four  = Succ Three


-- dynamic representatives for static Peanos

zero  = undefined :: Zero
one   = undefined :: One
two   = undefined :: Two
three = undefined :: Three
four  = undefined :: Four


-- addition, a la Prolog

class Add a b c | a b -> c where
  add :: a -> b -> c

instance              Add  Zero    b  b
instance Add a b c => Add (Succ a) b (Succ c)


-- multiplication, a la Prolog

class Mul a b c | a b -> c where
  mul :: a -> b -> c

instance                           Mul  Zero    b Zero
instance (Mul a b c, Add b c d) => Mul (Succ a) b d


-- factorial, a la Prolog

class Fac a b | a -> b where
  fac :: a -> b

instance                                Fac  Zero    One
instance (Fac n k, Mul (Succ n) k m) => Fac (Succ n) m

-- try, for "instance" (sorry):
-- 
--     :t fac four

Beginning graduate Haskell programmer (graduate education tends to liberate one from petty concerns about, e.g., the efficiency of hardware-based integers)

刚开始毕业的 Haskell 程序员(研究生教育倾向于将一个人从对基于硬件的整数的效率等小问题中解放出来)

-- the natural numbers, a la Peano

data Nat = Zero | Succ Nat


-- iteration and some applications

iter z s  Zero    = z
iter z s (Succ n) = s (iter z s n)

plus n = iter n     Succ
mult n = iter Zero (plus n)


-- primitive recursion

primrec z s  Zero    = z
primrec z s (Succ n) = s n (primrec z s n)


-- two versions of factorial

fac  = snd . iter (one, one) (\(a,b) -> (Succ a, mult a b))
fac' = primrec one (mult . Succ)


-- for convenience and testing (try e.g. "fac five")

int = iter 0 (1+)

instance Show Nat where
  show = show . int

(zero : one : two : three : four : five : _) = iterate Succ Zero


Origamist Haskell programmer
(always starts out with the “basic Bird fold”)

-- (curried, list) fold and an application

fold c n []     = n
fold c n (x:xs) = c x (fold c n xs)

prod = fold (*) 1


-- (curried, boolean-based, list) unfold and an application

unfold p f g x = 
  if p x 
     then [] 
     else f x : unfold p f g (g x)

downfrom = unfold (==0) id pred


-- hylomorphisms, as-is or "unfolded" (ouch! sorry ...)

refold  c n p f g   = fold c n . unfold p f g

refold' c n p f g x = 
  if p x 
     then n 
     else c (f x) (refold' c n p f g (g x))


-- several versions of factorial, all (extensionally) equivalent

fac   = prod . downfrom
fac'  = refold  (*) 1 (==0) id pred
fac'' = refold' (*) 1 (==0) id pred

Cartesianally-inclined Haskell programmer (prefers Greek food, avoids the spicy Indian stuff; inspired by Lex Augusteijn's “Sorting Morphisms” [3])

笛卡尔倾向的 Haskell 程序员(更喜欢希腊食物,避免辛辣的印度菜;灵感来自 Lex Augusteijn 的“Sorting Morphisms” [3])

-- (product-based, list) catamorphisms and an application

cata (n,c) []     = n
cata (n,c) (x:xs) = c (x, cata (n,c) xs)

mult = uncurry (*)
prod = cata (1, mult)


-- (co-product-based, list) anamorphisms and an application

ana f = either (const []) (cons . pair (id, ana f)) . f

cons = uncurry (:)

downfrom = ana uncount

uncount 0 = Left  ()
uncount n = Right (n, n-1)


-- two variations on list hylomorphisms

hylo  f  g    = cata g . ana f

hylo' f (n,c) = either (const n) (c . pair (id, hylo' f (c,n))) . f

pair (f,g) (x,y) = (f x, g y)


-- several versions of factorial, all (extensionally) equivalent

fac   = prod . downfrom
fac'  = hylo  uncount (1, mult)
fac'' = hylo' uncount (1, mult)

Ph.D. Haskell programmer (ate so many bananas that his eyes bugged out, now he needs new lenses!)

博士 Haskell 程序员(吃了太多香蕉,眼睛都瞎了,现在他需要新镜头!)

-- explicit type recursion based on functors

newtype Mu f = Mu (f (Mu f))  deriving Show

in      x  = Mu x
out (Mu x) = x


-- cata- and ana-morphisms, now for *arbitrary* (regular) base functors

cata phi = phi . fmap (cata phi) . out
ana  psi = in  . fmap (ana  psi) . psi


-- base functor and data type for natural numbers,
-- using a curried elimination operator

data N b = Zero | Succ b  deriving Show

instance Functor N where
  fmap f = nelim Zero (Succ . f)

nelim z s  Zero    = z
nelim z s (Succ n) = s n

type Nat = Mu N


-- conversion to internal numbers, conveniences and applications

int = cata (nelim 0 (1+))

instance Show Nat where
  show = show . int

zero = in   Zero
suck = in . Succ       -- pardon my "French" (Prelude conflict)

plus n = cata (nelim n     suck   )
mult n = cata (nelim zero (plus n))


-- base functor and data type for lists

data L a b = Nil | Cons a b  deriving Show

instance Functor (L a) where
  fmap f = lelim Nil (\a b -> Cons a (f b))

lelim n c  Nil       = n
lelim n c (Cons a b) = c a b

type List a = Mu (L a)


-- conversion to internal lists, conveniences and applications

list = cata (lelim [] (:))

instance Show a => Show (List a) where
  show = show . list

prod = cata (lelim (suck zero) mult)

upto = ana (nelim Nil (diag (Cons . suck)) . out)

diag f x = f x x

fac = prod . upto


Post-doc Haskell programmer
(from Uustalu, Vene and Pardo's “Recursion Schemes from Comonads” [4])

-- explicit type recursion with functors and catamorphisms

newtype Mu f = In (f (Mu f))

unIn (In x) = x

cata phi = phi . fmap (cata phi) . unIn


-- base functor and data type for natural numbers,
-- using locally-defined "eliminators"

data N c = Z | S c

instance Functor N where
  fmap g  Z    = Z
  fmap g (S x) = S (g x)

type Nat = Mu N

zero   = In  Z
suck n = In (S n)

add m = cata phi where
  phi  Z    = m
  phi (S f) = suck f

mult m = cata phi where
  phi  Z    = zero
  phi (S f) = add m f


-- explicit products and their functorial action

data Prod e c = Pair c e

outl (Pair x y) = x
outr (Pair x y) = y

fork f g x = Pair (f x) (g x)

instance Functor (Prod e) where
  fmap g = fork (g . outl) outr


-- comonads, the categorical "opposite" of monads

class Functor n => Comonad n where
  extr :: n a -> a
  dupl :: n a -> n (n a)

instance Comonad (Prod e) where
  extr = outl
  dupl = fork id outr


-- generalized catamorphisms, zygomorphisms and paramorphisms

gcata :: (Functor f, Comonad n) =>
           (forall a. f (n a) -> n (f a))
             -> (f (n c) -> c) -> Mu f -> c

gcata dist phi = extr . cata (fmap phi . dist . fmap dupl)

zygo chi = gcata (fork (fmap outl) (chi . fmap outr))

para :: Functor f => (f (Prod (Mu f) c) -> c) -> Mu f -> c
para = zygo In


-- factorial, the *hard* way!

fac = para phi where
  phi  Z             = suck zero
  phi (S (Pair f n)) = mult f (suck n)


-- for convenience and testing

int = cata phi where
  phi  Z    = 0
  phi (S f) = 1 + f

instance Show (Mu N) where
  show = show . int

Tenured professor (teaching Haskell to freshmen)

终身教授(向新生教授 Haskell)

fac n = product [1..n]

回答by Pascal Cuoq

Thanks to Christoph, a C99 solution that works for quite a few "numbers":

感谢 Christoph,一个适用于相当多“数字”的 C99 解决方案:

#include <math.h>
#include <stdio.h>

double fact(double x)
{
  return tgamma(x+1.);
}

int main()
{
  printf("%f %f\n", fact(3.0), fact(5.0));
  return 0;
}

produces 6.000000 120.000000

产生 6.000000 120.000000

回答by ast4

For large n you may run into some issues and you may want to use Stirling's approximation:

对于较大的 n,您可能会遇到一些问题,您可能需要使用斯特林近似:

Which is:

这是:

alt text

替代文字

回答by sth

If your main objective is an interesting looking function:

如果你的主要目标是一个有趣的函数:

int facorial(int a) {
   int b = 1, c, d, e;
   a--;
   for (c = a; c > 0; c--)
   for (d = b; d > 0; d--)
   for (e = c; e > 0; e--)
   b++;
   return b;
}

(Not recommended as an algorithm for real use.)

(不推荐作为实际使用的算法。)

回答by sth

a tail-recursive version:

尾递归版本:

long factorial(long n)
{
    return tr_fact(n, 1);
}
static long tr_fact(long n, long result)
{
    if(n==1)
        return result;
    else
        return tr_fact(n-1, n*result);
}

回答by starblue

In C99 (or Java) I would write the factorial function iteratively like this:

在 C99(或 Java)中,我会像这样迭代地编写阶乘函数:

int factorial(int n)
{
    int result = 1;
    for (int i = 2; i <= n; i++)
    {
        result *= i;
    }
    return result;
}
  • C is not a functional language and you can't rely on tail-call optimization. So don't use recursion in C (or Java) unless you need to.

  • Just because factorial is often used as the first example for recursion it doesn't mean you need recursion to compute it.

  • This will overflow silently if n is too big, as is the custom in C (and Java).

  • If the numbers int can represent are too small for the factorials you want to compute then choose another number type. long long if it needs be just a little bit bigger, float or double if n isn't too big and you don't mind some imprecision, or big integers if you want the exact values of really big factorials.

  • C 不是函数式语言,您不能依赖尾调用优化。所以除非需要,否则不要在 C(或 Java)中使用递归。

  • 仅仅因为阶乘经常用作递归的第一个示例,并不意味着您需要递归来计算它。

  • 如果 n 太大,这将悄悄溢出,就像 C(和 Java)中的习惯一样。

  • 如果 int 可以表示的数字对于您要计算的阶乘来说太小,则选择另一种数字类型。long long 如果它需要大一点, float 或 double 如果 n 不是太大并且你不介意一些不精确,或者如果你想要真正大阶乘的确切值,或者大整数。

回答by Orr Matarasso

int factorial(int n){
    return n <= 1 ? 1 : n * factorial(n-1);
}

回答by indiv

Here's a C program that uses OPENSSL's BIGNUM implementation, and therefore is not particularly useful for students. (Of course accepting a BIGNUM as the input parameter is crazy, but helpful for demonstrating interaction between BIGNUMs).

这是一个使用 OPENSSL 的 BIGNUM 实现的 C 程序,因此对学生来说不是特别有用。(当然,接受 BIGNUM 作为输入参数很疯狂,但有助于演示 BIGNUM 之间的交互)。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <openssl/crypto.h>
#include <openssl/bn.h>

BIGNUM *factorial(const BIGNUM *num)
{
    BIGNUM *count = BN_new();
    BIGNUM *fact = NULL;
    BN_CTX *ctx = NULL;

    BN_one(count);
    if( BN_cmp(num, BN_value_one()) <= 0 )
    {
        return count;
    }

    ctx = BN_CTX_new();
    fact = BN_dup(num);
    BN_sub(count, fact, BN_value_one());
    while( BN_cmp(count, BN_value_one()) > 0 )
    {
        BN_mul(fact, count, fact, ctx);
        BN_sub(count, count, BN_value_one());
    }

    BN_CTX_free(ctx);
    BN_free(count);

    return fact;
}

This test program shows how to create a number for input and what to do with the return value:

这个测试程序展示了如何为输入创建一个数字以及如何处理返回值:

int main(int argc, char *argv[])
{
    const char *test_cases[] =
    {
        "0", "1",
        "1", "1",
        "4", "24",
        "15", "1307674368000",
        "30", "265252859812191058636308480000000",
        "56", "710998587804863451854045647463724949736497978881168458687447040000000000000",
        NULL, NULL
    };
    int index = 0;
    BIGNUM *bn = NULL;
    BIGNUM *fact = NULL;
    char *result_str = NULL;

    for( index = 0; test_cases[index] != NULL; index += 2 )
    {
        BN_dec2bn(&bn, test_cases[index]);

        fact = factorial(bn);

        result_str = BN_bn2dec(fact);
        printf("%3s: %s\n", test_cases[index], result_str);
        assert(strcmp(result_str, test_cases[index + 1]) == 0);

        OPENSSL_free(result_str);
        BN_free(fact);
        BN_free(bn);
        bn = NULL;
    }

    return 0;
}

Compiled with gcc:

用 gcc 编译:

gcc factorial.c -o factorial -g -lcrypto