Факториал в C без условий, циклов и арифметических операторов - PullRequest
10 голосов
/ 17 марта 2009

Как найти факториал числа (от 1 до 10) в C, не используя:

  • операторы цикла вроде for, while и do while;
  • условные операторы типа if и case; и
  • арифметические операторы, такие как +, -, *,%, /, ++, −−?

К вашему сведению: я нашел этот вопрос в Cptitude.

Ответы [ 18 ]

30 голосов
/ 17 марта 2009

Поскольку это всего 1-10, просто предварительно вычислите его и сохраните в простом массиве int размером 11. Для первого элемента массива укажите 1. Это не допустимый диапазон ввода для вашей проблемы, но может также будь прав.

Нам нужно хранить 11 элементов вместо 10, которые нам нужны, потому что в противном случае нам нужно было бы использовать операцию "-", чтобы получить правильный индекс. Вычитание не разрешено в вашей задаче.

int factorial(int x)
{
  return precomputedArray[x];
}
25 голосов
/ 17 марта 2009

Вот решение без циклов, арифметики или условных выражений, которое не прибегает к предварительным вычислениям. В нем также не используются условия короткого замыкания, такие как && или ||, которые на практике эквивалентны if. Так что, похоже, это первое правильное решение без каких-либо условий вообще. Теперь в правильном C без функций C ++:)

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

Обновлено: обсуждение содержало утверждение, что короткое замыкание, такое как &&, будет приемлемо в решении, в котором не используется if. Вот простой макрос, который имитирует двустороннее «если» с использованием && и, очевидно, делает всю проблему намного менее интересной:

#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);

Затем вы можете определить

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

и тогда остальная часть проблемы становится тривиальной.

16 голосов
/ 17 марта 2009
#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;
}

(Любой, кто использует этот ответ для домашнего задания, либо терпит неудачу, либо имеет учителя с хорошим чувством юмора)

(Бах, я был медленным. Другие люди уже дали этот ответ. Не стесняйтесь голосовать ответ.)

11 голосов
/ 17 марта 2009

Может быть, я решаю чью-то домашнюю работу, но это выглядело как забавное испытание, в любом случае, вот мое решение (компилируется с предупреждениями, но не может помочь тем, кто не выглядит уродливо (э))

РЕДАКТИРОВАТЬ: Я изменил программу, чтобы она поддерживала значительно более длинные факториалы (до 20 или около того) и сделал код немного более аккуратным, удалив таблицу поиска внутри 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;
}

Пример прогона:

[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]$
9 голосов
/ 17 марта 2009

"+", "-" и "*" явно запрещены, но "+ =", "- =" и "* =" - нет, поэтому рекурсивная реализация становится…

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

VC7 отказывается компилировать вышеупомянутое, когда находится в «режиме компиляции как источника C» - стонет о константном L-значении для «* =», но вот другой вариант того же самого:

int factorial( int arg )
{
    int argcopy1 = arg;
    int argcopy2 = arg;
    argcopy1 -= 1;
    argcopy2 *= arg == 1 ? 1 : fact( argcopy1 );
    return argcopy2;
}
7 голосов
/ 18 марта 2009

Это не полный ответ, а просто разные подходы к add() и mult() функциям:

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

(я считаю, что C, в отличие от C ++, позволяет определять новые типы внутри sizeof.)

Вот еще одна (полностью непереносимая) реализация add() на основе арифметики указателей:

int add(int x, int y) {
    return (int) &((char*) x)[y];
}
4 голосов
/ 17 мая 2009

Вот решение (пока только ), которое фактически решает проблему с необходимыми ограничениями.

int fac( int n )
{
    /* The is the binary representation of the function: */
    /* 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);
}

Вот тестовая программа:

#include <stdio.h>

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 );
        }
    }
}
3 голосов
/ 31 августа 2009

вот решение, которое использует арифметику указателей для арифметики и указатели функций для условий.

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

Пробный прогон:

 ~ > 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
3 голосов
/ 17 марта 2009

Используйте asm для написания кода сборки.

Или предварительно скомпилируйте программу и запустите ее из вашей программы.

Почему вы налагаете такие ограничения на ваш код?

2 голосов
/ 17 марта 2009

Создание гигантского набора троичных операторов, возвращающих предварительно рассчитанное значение для каждого разрешенного ввода Используйте макросы для вычисления значений.

...