views:

4230

answers:

18

How can I find the factorial of a number (from 1 to 10) in C, without using:

  • loop statements like for, while, and do while;
  • conditional operators like if and case; and
  • arithmetic operators like + , − , * , % , /, ++, −−?

FYI: I found this question in C aptitude.

+29  A: 

Since it is only 1 to 10, simply precompute it and store it in a simple int array of size 11. For the first element in the array put 1. It is not a valid input range for your problem but might as well be correct.

We need to store 11 elements instead of the 10 we need because otherwise we'd need to use operation "-" to get the right index. Subtraction is not allowed in your problem though.

int factorial(int x)
{
  return precomputedArray[x];
}
Brian R. Bondy
this uses '-' (lol)
hasen j
haha nice catch, I fixed it.
Brian R. Bondy
this can be done symbollically as well, without the need for a precomp array (but nice answer)
dsm
google says 0! = 1
hasen j
@hasen j: agree might as well have the right value
Brian R. Bondy
Actually I just googled the "proof" for it and it's rather stupid. 0! is just undefined but I guess it's 1 by convention.
hasen j
@hanse j: Yes factorial is just a mathematical function defined as fact(x) = 1 if x = 0, else x * fact(x-1)
Brian R. Bondy
Well, this implicitly uses + because arr[i] is *(arr + i) ;)
Ates Goral
@Brian R. Bondy: Actually, factorial is defined as n! = product of i where 1 <= i <= n. The point is then that 0! = product of i where 1 <= i <= 0. There are no i satisfying 1 <= i <= 0 so 0! reduces to an empty product. Empty products are equal to one. There are several good reasons why the empty product is equal to one. Consider product of i where 1 <= i <= 10 and i is even. This product is also equal to product of 2i where 1 <= i <= 5 and i is even times the product of (2i - 1) where 1 <= i <= 5 and i is even. But the last product is empty so it must be one for the equality to hold.
Jason
@Jason: Thanks for the correction, I didn't know that.
Brian R. Bondy
+2  A: 

Produce a giant set of ternary operators returning a precalculated value for each allowed input. Use macros to compute the values.

sharptooth
unfortunately, it looks like ? is on the forbidden list
cobbal
Sure, and who asked for an elegant solution?
sharptooth
@cobbal Looks that ? was just a question mark. It's strange to see it in the list of arithmetic operations.
sharptooth
+1 for bad taste. :-)
hillu
+1  A: 

Calculating factorial is the first (and for many people, the last) time you'll use recursion. The standard implementation is

long fact(int x)
{
   if (x < 2)
     return 1L;
   else
     return fact(x - 1) * x;
}

Some would argue that that last statement should be "x * fact(x-1)" so that the compiler can recognize that it's tail recursion. Personally, I doubt any compiler is smart enough to see it in that form and not see it in the other form.

However, since you've restricted it to not use "if" or "-", I don't know how you'd do it.

Paul Tomblin
I wrote this at first too, but then I seen he doesn't want to use if statements
Brian R. Bondy
fail. violates two conditions of his assignment :p
Johannes Schaub - litb
Oh right. I'll modify it.
Paul Tomblin
Correct me if I'm wrong, but neither "x * fact(x-1)" nor "fact(x-1) * x" are tail recursive. Case in point, write it out like this: "int intermediate = fact(x-1); return intermediate * x;" You can plainly see the code is doing extra work after the recursive call.
Juliet
Princess is right. This code isn't tail recursive in any of the two forms (but it can be refactored appropriately). Oh, and by the way: the GCC compiler still recgnizes and optimizes the above code! Litb posted details in some other answer here on SO.
Konrad Rudolph
@Princess: The function is not tail recursive because there is a multiply operation after the recursive call. With tail recursion there must be no operations after the recursive call. To do this you would need to pass the result up all the way up to the base case, and in the base case return it
Brian R. Bondy
To keep the exact signature of fact(int x) you could do this helper recursion call inside the definition of another function.
Brian R. Bondy
@Princess: So you were right, nice catch :)
Brian R. Bondy
A: 

If you cannot use recursion, or arithmetic and you have a limited range of inputs, you could hard-code the result to be an array lookup,

so:

return factorials[x];

where you've pre-filled factorials with the relevant values

Rowland Shaw
+15  A: 
#include <stdio.h>

static const int factorial[] = {
    1,
    1,
    2,
    6,
    24,
    120,
    720,
    5040,
    40320,
    362880,
    3628800,
};

/* Test/demo program. */
int main(void)
{
    int i;

    for (i = 0; i <= 10; ++i)
        printf("%d %d\n", i, factorial[i]);

    return 0;
}

(Anyone using this answer for a homework question either fails or has a teacher with a good sense of humor.)

(Bah, I was slow. Other people gave this answer already. Feel free to vote their answer up.)

Lars Wirzenius
"for" and "++" is not allowed.
rics
gee, that was in the test program
Lars Wirzenius
0! is 1 by definition of factorial. Not -1. (This allows formulae for combinations etc. to work without special cases.)
Richard
...richard and makes it consistent with the gamma function
nlucaroni
Richard, oops, you're right, I'll fix that.
Lars Wirzenius
Since they can't use loops and ++, just cut/paste 10 lines with 'i' replaced by the number. Technically correct and smartass FTW.
Chris Doggett
+1  A: 

rough sketch (already proposed by others!)

int[] factorials = {1,1,2,6,24, 120,720, ..etc };
return factorials[i];
hasen j
+1  A: 

i too tried by putting the values in array. here i have used if conditions and while loops but no arithmetic operators involved.! trying if i could remove them too.

#include <stdio.h>

int add(int a, int b)
{
int t1, t2, ab, bb, cb=0, orb=1, ans=0;

do {
    t1 = a >> 1; 
    t2 = t1 << 1;

    if (a==t2) ab=0; else ab=1;

    t1 = b >> 1;
    t2 = t1 << 1; 

    if (b==t2) bb=0; else bb=1;

    if (ab==1 && bb==1) { 
     if (cb==1) ans=ans | orb; 
     cb = 1; 
     }

    if ( ab!=bb ) { 
     if (cb==0) ans = ans | orb; 
     }

    if (ab==0 && bb==0) {
     if (cb==1) { 
     ans = ans | orb;
     cb=0;
       }
     }

    orb = orb << 1; 
    a = a >> 1;
    b = b >> 1;

    } while (a!=0 || b!=0);

if (cb==1) ans = ans | orb;

return ans;
}



int multiply(int x,int y)
{
    int result = 0, i = 0 , j=0;

    while((i=add(i,1)) <= y)
     result = add(result,x);

    return result;

}

int factorial(int x)
{
    if(x==1)
     return 1;
    else
     return multiply(x,factorial(x-1));

}


int main()
{
    int x;
    printf("Enter a number between 0 and 10: ");
    scanf("%d" , &x);
    printf("\nFactorial: %d\n" , factorial(x));
    return 0;
}
pragadheesh
ya i have mentioned it. and trying to refine it further such that it satisfies those constraints.
pragadheesh
This violates a lot of constraints, and it needs either a loop or recursion. But to have recursion you need to have a base case, which means you need at least 1 conditional statement.
Brian R. Bondy
Sorry I seen that you wrote that at the top of your answer and then deleted my comment, just as you posted your above comment.
Brian R. Bondy
+3  A: 

Use asm to write assembly code.

Or, precompile a program and execute it from your program.

Why would you impose such limits on your code?

strager
Assembly language would be unportable.
sharptooth
It didn't say it had to be portable. (It did, however, limit it to C).
Beska
He limited it to C which implies that any standard obidient compiler should be able to compile it. The standard doesn't require a compiler to be able to compile any assembler on any target machine.
sharptooth
It's technically "in C". You can make non-portable plain C, too.
strager
No, assembler is not "in C". It is in certain supersets of C that certain compilers may accept, but any program using inline asm is not, strictly speaking, a C program at all.
bdonlan
+10  A: 

"+", "-" and "* " are explicitly prohibited, but "+=", "-=" and "*=" are not and so the recursive implementation becomes…

int factorial( int arg )
{
    int argcopy = arg;
    argcopy -= 1;
    return arg == 1 ? arg : arg *= factorial( argcopy );
}

VC7 refuses to compile the above when in "compile as C source mode" – moans about the const L-value for "*=", but here is another variant of the same:

int factorial( int arg )
{
    int argcopy1 = arg;
    int argcopy2 = arg;
    argcopy1 -= 1;
    argcopy2 *= arg == 1 ? 1 : fact( argcopy1 );
    return argcopy2;
}
sharptooth
Doesn't using ?: violate the no ifs rule?
Ferruccio
@Ferruccio: same way that *= violates the 'No '*' rule
dsm
It does violate ideologically, but doesn't violate formally. If you really want to avoid this operations you have to use a precomputed array and a value fetcher.
sharptooth
+11  A: 

Maybe I'm solving someone's homework, but it looked like a fun challenge, anyways, here is my solution (compiles with warnings, but can't help those without making it look ugly(er))

EDIT: I have changed the program to make it support considerably longer factorials (up to 20 or so) and made the code a bit tidier by removing the lookup table inside prev().

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

#define _if(CND, OP1, OP2) (((CND) && ((OP1) || 1)) || (OP2))

long long int add(long long int x, long long int y){
    long long int r = x ^ y;
    long long int c = x & y;
        c = c << 1;    
    _if(c != 0, r = add(r, c), 1);

    return r;
}

long long int prev(long long int x){
    return add(x, -1);
}                           

long long int mult(long long int x, long long int y){
    long long int r;

    _if(x == 0,
         r = 0,
       _if(x == 1, 
            r = y, 
            r = add(y, mult(prev(x), y))));

    return r;
}

long long int fac(long long int x){
    long long int r;

    _if(x < 2,
        r = 1,
        r = mult(x, fac(prev(x))));

    return r;
}

int main(int argc, char**argv){
    long long int i;

    for(i = 0; i <= 20; i++)
        printf("factorial(%lli) => %lli\n", i, fac(i));

    return 0;
}

Sample run:

[dsm@localhost:~/code/c]$ gcc -o proc proc.c
[dsm@localhost:~/code/c]$ ./proc #/
factorial(0) => 1
factorial(1) => 1
factorial(2) => 2
factorial(3) => 6
factorial(4) => 24
factorial(5) => 120
factorial(6) => 720
factorial(7) => 5040
factorial(8) => 40320
factorial(9) => 362880
factorial(10) => 3628800
factorial(11) => 39916800
factorial(12) => 479001600
factorial(13) => 6227020800
factorial(14) => 87178291200
factorial(15) => 1307674368000
factorial(16) => 20922789888000
factorial(17) => 355687428096000
factorial(18) => 6402373705728000
factorial(19) => 121645100408832000
factorial(20) => 2432902008176640000
[dsm@localhost:~/code/c]$
dsm
if this works, you're the man
Oliver N.
antti.huima
dsm
+24  A: 

Here is a solution without loops, arithmetics, or conditionals and which does not resort to precomputation. It also does not use short-circuiting conditionals like && or || which are in practice equivalent to if. So this seems to be the first proper solution without any conditionals at all. Now in proper C without C++ features :)

#include <stdio.h>
#define uint unsigned int

void A(uint *a, uint *b)
{
    uint tmp = *a & *b;
    *a = (*a | *b) & ~tmp;
    *b = tmp << 1;
}

#define REPEAT32(s) \
s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s

uint add(uint a, uint b)
{
    REPEAT32(A(&a, &b);) return a;
}

uint bitexpand(uint b)
{
    b = (b << 1)  | b; b = (b << 2)  | b; b = (b << 4)  | b;
    b = (b << 8)  | b; b = (b << 16) | b;
    return b;
}

void M(uint *acc, uint *a, uint *b)
{
    *acc = add(*acc, *a & bitexpand(*b & 1));
    *a <<= 1;
    *b >>= 1;
}

uint mult(uint a, uint b)
{
    uint acc = 0;
    REPEAT32(M(&acc, &a, &b);) return acc;
}

uint factorial(int n)
{
    uint k = 1;
    uint result = 0;
    result |= (bitexpand(n == 1) & k);
    k = mult(k, 2); result |= (bitexpand(n == 2) & k);
    k = mult(k, 3); result |= (bitexpand(n == 3) & k);
    k = mult(k, 4); result |= (bitexpand(n == 4) & k);
    k = mult(k, 5); result |= (bitexpand(n == 5) & k);
    k = mult(k, 6); result |= (bitexpand(n == 6) & k);
    k = mult(k, 7); result |= (bitexpand(n == 7) & k);
    k = mult(k, 8); result |= (bitexpand(n == 8) & k);
    k = mult(k, 9); result |= (bitexpand(n == 9) & k);
    k = mult(k, 10); result |= (bitexpand(n == 10) & k);
    return result;
}

int main(int argc, char **argv)
{
    uint i;
    /* Demonstration loop, not part of solution */
    for (i = 1; i <= 10; i++)
    {
        printf("%d %d\n", i, factorial(i));
    }
}

Updated: the discussion contained the claim that short-circuiting conditional like && would be acceptable in a solution that does not use if. Here is a simple macro that mimics two-way 'if' using && and obviously makes the whole problem much less interesting:

#define IF(i, t, e) \
(void)((i) && (goto then##__LINE__, 1)); goto else##__LINE__;
then##__LINE__: t; goto cont##__LINE__; \
else##__LINE__: e; cont##__LINE__: ((void)0);

You can then define

#define WHILE(c, s) \
loop##__LINE__: IF(c, s; goto loop##__LINE__, ((void)0)))

and then the rest of the problem becomes trivial.

antti.huima
The post says C, that you have here is C++
dsm
Yes... but we don't use C++ features here really. Well, let me fix it.
antti.huima
Your '==' is effectively a conditional operator. You can fix it by "add (n, -2)" and or'ing the bits of the result together: "bitexpand(or_all_bits(add(n,-2))^1)"
Skizz
antti.huima
dsm
j_random_hacker
antti.huima
@antti.huima - does that mean that functions are not allowed because they cause a jump in assembler?
dsm
The jump is unconditional in that case, dsm
antti.huima
Dave
Agreed, loving the bit expanding.
Chet
Isn't `*a = (*a | *b) ` equivalent to `*a = *a ^ *b;`? Any reason for not using the latter?
outis
antti.huima
+7  A: 

This is not a complete answer, but just different approaches to add() and mult() functions:

#define add(a, b)  sizeof (struct { char x[a]; char y[b]; })
#define mult(a, b) sizeof (struct { char x[a][b]; })

(I believe that C, unlike C++, allows definition of new types inside a sizeof.)

Here is one more (totally nonportable) implementation of add() based on pointer arithmetic:

int add(int x, int y) {
    return (int) &((char*) x)[y];
}
j_random_hacker
Not sure if the sizeof() trick works for sizes known at runtime, but it's completely genius anyway! +1.
Beni Cherniavsky-Paskin
Thanks :) But no, `a` and `b` have to be known at compile time for the sizeof call to work (at least in standard C).
j_random_hacker
A: 

what if the we have to valculate factorials of 1 to 100. How to store this big numbers?

using strings, and you would have to write your own method of adding two strings, regardless of size (with carry addition, and all)
Tudor Olariu
A: 
#include<stdio.h>
void main()
{
    unsigned long int num,fact,counter;
    while(counter<=num)
    {
        printf("Enter the number");
        scanf("%d",&num);
        fact=fact*counter;
        counter++;
        printf("The factorial of number entered is %lu",fact);
    }
    printf("press any key to exit...");
    getch();
}
The standard requires 'int main()'; unless you know you're using C99, you should return a value from main() - and good practice suggests putting a return into main().
Jonathan Leffler
Darn - the Community Wiki entry gives me too much credit; all I did was add blanks.
Jonathan Leffler
+3  A: 
#include <stdio.h>

int fac( int n )
{
    /* 0000 => 0000000000000000001 */
    /* 0001 => 0000000000000000001 */
    /* 0010 => 0000000000000000010 */
    /* 0011 => 0000000000000000110 */
    /* 0100 => 0000000000000011000 */
    /* 0101 => 0000000000001111000 */
    /* 0110 => 0000000001011010000 */
    /* 0111 => 0000001001110110000 */
    /* 1000 => 0001001110110000000 */
    /* 1001 => 1011000100110000000 */
    int bit0 = n & 1;
    int bit1 = (n & 2) >> 1;
    int bit2 = (n & 4) >> 2;
    int bit3 = (n & 8) >> 3;
    int notbit0 = bit0 ^ 1;
    int notbit1 = bit1 ^ 1;
    int notbit2 = bit2 ^ 1;
    int notbit3 = bit3 ^ 1;
    return
    (bit0 & notbit1 & notbit2 & bit3) << 18 |
    (bit0 & notbit1 & notbit2 & bit3) << 16 |
    (notbit1 & notbit2 & bit3) << 15 |
    (notbit1 & notbit2 & bit3) << 11 |
    (notbit1 & notbit2 & bit3) << 8 |
    (notbit1 & notbit2 & bit3) << 7 |
    (notbit0 & notbit1 & notbit2 & bit3) << 12 |
    (notbit0 & notbit1 & notbit2 & bit3) << 10 |
    (bit0 & bit1 & bit2 & notbit3) << 12 |
    (bit1 & bit2 & notbit3) << 9 |
    (bit0 & bit1 & bit2 & notbit3) << 8 |
    (bit1 & bit2 & notbit3) << 7 |
    (bit0 & bit2 & notbit3) << 5 |
    (bit2 & notbit3) << 4 |
    (notbit0 & bit1 & bit2 & notbit3) << 6 |
    (bit0 & notbit1 & bit2 & notbit3) << 6 |
    (notbit1 & bit2 & notbit3) << 3 |    
    (bit0 & bit1 & notbit2 & notbit3) << 2 |    
    (bit1 & notbit2 & notbit3) << 1 |    
    (notbit1 & notbit2 & notbit3);
}

int main()
{
    int i, expected, j;
    for( i = 0; i < 10; ++i )
    {
        expected = 1;
        for( j = 2; j <= i; ++j )
        {
            expected *= j;
        }
        if( expected != fac( i ) )
        {
            printf( "FAILED: fac(%d) = %d, expected %d\n", i, fac( i ), expected );
        }
    }
}
JPS
Brilliantly horrible!
Beni Cherniavsky-Paskin
+2  A: 

here is a solution that uses pointer arithmetics for arithmetics and function pointers for conditionals.

#include <stdio.h>

int fact(int n);

int mul(int a, int b)
{
        struct s {
                char _v[b];
        };
        struct s *p = (struct s*)0;
        return (int) &p[a];
}

int add(int a, int b)
{
        return (int) (&((char *)a)[b]);
}

int is_0(int n)
{
        return (n == 0);
}

int fact_0(int n)
{
        return 1;
}

int fact_n(int n)
{
        return mul(n, fact(add(n,-1)));
}

int (*facts[2])(int) = {fact_n, fact_0};

int fact(int n)
{
        return facts[is_0(n)](n);
}

int main(int argc, char **argv)
{
        int i;
        for(i = 0; i<=10; i++) {
                printf("fact %d = %d\n", i, fact(i));
        }
}

Sample Run:

 ~ > gcc -std=c99 fact.c 
 ~ > ./a.out 
fact 0 = 1
fact 1 = 1
fact 2 = 2
fact 3 = 6
fact 4 = 24
fact 5 = 120
fact 6 = 720
fact 7 = 5040
fact 8 = 40320
fact 9 = 362880
fact 10 = 3628800
nice trick, I never thought of pointers as computation capability.
Raoul Supercopter
+1  A: 

Let's see if we can do something half-elegant, without depending on 1 <= n <= 10.

  • Instead of looping we'll of course use recursion.
  • Instead of an if for terminating the recursion, we'll use an array of function pointers!
    (We still need comparison operators, such as < and ==.)

EDIT: damaru used the function pointers trick first.

This gives: [All code is untested, no C compiler under hand!]

typedef int (*unary_fptr)(int);

int ret_1(int n) {
    return 1;
}

int fact(int n) {
    unary_fptr ret_1_or_fact[] = {ret_1, fact};
    return multiply(ret_1_or_fact[n > 1](sub_1(n)), n);
}

We still need to implement sub_1 and multiply. Let's start with sub_1, which is a simple recursion on the bits until the carry stops (if you don't understand this, the similar add_1 at the end is simpler to think about):

int identity(int n) {
    return n;
}

int sub_1(int n) {
    unary_fptr sub_1_or_identity[] = {sub_1, identity};
    int lsb = n & 1;
    int rest = sub_1_or_identity[lsb](n >> 1);
    return (rest << 1) | (lsb ^ 1);
}

multiply: The simplest I can think of is Russian Peasant multiplication, which reduces it to binary shifts and addition. With conditionals, a recursive formulation would look like this:

 /* If we could use conditionals */
int multiply(int a, int b) {
    int subproduct;
    if(a <= 1) {
       subproduct = 0;
    } else {
       subproduct = multiply(a >> 1, b << 1);
    }

    if(a & 1) {
       return add(b, subproduct);
    } else {
       return subproduct;
    }
}

Without conditionals, we have to use the dispatch array trick twice:

typedef int (*binary_fptr)(int, int);

int ret_0(int a, int b) {
    return 0;
}

int multiply(int a, int b) {
    binary_fptr ret_0_or_multiply = {ret_0, multiply};
    int subproduct = ret_0_or_multiply[a >= 2](a >> 1, b << 1);

    binary_fptr ret_0_or_add = {ret_0, add};
    return ret_0_or_add[a & 1](subproduct, b);
}

Now all we miss is add. You should by now guess how it will go - a simultaneous recursion over bits of the two numbers, which reduces the problem to shifts and add_1:

int add(int a, int b) {
    int lsb = (a & 1) ^ (b & 1);
    int carry = (a & 1) & (b & 1);

    binary_fptr ret_0_or_add = {ret_0, add};
    int subsum = ret_0_or_add[(a >= 2) & (b >= 2)](a >> 1, b>> 1);

    unary_fptr identity_or_add_1 = {identity, add_1};
    return identity_or_add_1[carry](subsum << 1);
}

and add_1 is a simple recursion over bits until the carry stops:

int add_1(int n) {
    unary_fptr identity_or_add_1[] = {identity, add_1};
    int lsb = n & 1;
    int rest = identity_or_add_1[lsb](n >> 1);
    return (rest << 1) | (lsb ^ 1);
}

That's it I think! [As noted above all code is untested!]

Beni Cherniavsky-Paskin
A: 
int factorial(int n)
{
    return (n>1) ? factorial(n) : 1;
}
Veer