質問

Cを使用せずに、Cで数値の階乗(1〜10)を見つけるにはどうすればよいですか:

  • for、while、do whileなどのループ文;
  • ifやcaseなどの条件演算子。そして
  • +、<!>#8722などの算術演算子。 、*、%、/、++、<!>#8722; <!>#8722;?

FYI:この質問はC aptitudeで見つけました。

役に立ちましたか?

解決

1〜10であるため、単純に事前計算してサイズ11の単純なint配列に格納します。配列の最初の要素は1です。問題の有効な入力範囲ではありませんが、正しい。

必要な10個ではなく11個の要素を保存する必要があります。そうしないと、操作<!> quot;-<!> quot;適切なインデックスを取得します。ただし、問題では減算は許可されていません。

int factorial(int x)
{
  return precomputedArray[x];
}

他のヒント

これは、ループ、算術、または条件を使用せず、事前計算に頼らないソリューションです。 また、実際には&&と同等である||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));
    }
}

更新:ディスカッションには、<!> amp; <!> amp; ifを使用しないソリューションでは許容されます。これは、<!> amp; <!> amp;を使用して双方向の「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)))

そして、残りの問題は些細なものになります。

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

(宿題の質問にこの回答を使用している人は失敗するか、またはユーモアのセンスのある教師がいます。)

(ああ、私は遅かった。他の人はすでにこの答えをしてくれた。 答えてください。)

誰かの宿題を解決しているのかもしれませんが、それは楽しい挑戦のように見えました、とにかく、ここに私の解決策があります

編集:プログラムを変更して、かなり長い階乗(最大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]$

<!> quot; + <!> quot;、<!> quot;-<!> quot;および<!> quot; * <!> quot;明示的に禁止されていますが、<!> quot; + = <!> quot;、<!> quot;-= <!> quot;および<!> quot; * = <!> quot;そうではないので、再帰的な実装は<!>#8230;

になります
int factorial( int arg )
{
    int argcopy = arg;
    argcopy -= 1;
    return arg == 1 ? arg : arg *= factorial( argcopy );
}

VC7は、<!> quot; compile as C source mode <!> quot;の場合、上記のコンパイルを拒否します。 <!>#8211; <!> quot; * = <!> quot;のconst L値についてうめき声を上げますが、ここには同じものの別のバリアントがあります:

int factorial( int arg )
{
    int argcopy1 = arg;
    int argcopy2 = arg;
    argcopy1 -= 1;
    argcopy2 *= arg == 1 ? 1 : fact( argcopy1 );
    return argcopy2;
}

これは完全な答えではありませんが、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内の新しい型の定義を許可すると信じています。)

ここに、ポインター演算に基づく<=>のもう1つの(完全に移植不可能な)実装があります。

int add(int x, int y) {
    return (int) &((char*) x)[y];
}

必要な制限の下で実際に問題を解決するソリューション(これまでのところのみのソリューション)です。

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

asmを使用してアセンブリコードを記述します。

または、プログラムをプリコンパイルして、プログラムから実行します。

なぜコードにこのような制限を課すのですか?

これは、算術演算にポインター演算を使用し、条件演算に関数ポインターを使用するソリューションです。

#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項演算子の巨大なセットを生成します。マクロを使用して値を計算します。

階乗の計算は、再帰を使用する最初の(そして多くの人にとっては最後の)時間です。標準実装は

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

最後のステートメントは<!> quot; x * fact(x-1)<!> quot;であると主張する人もいます。コンパイラーが末尾再帰であることを認識できるようにします。個人的には、どのコンパイラもその形式でそれを見ることができ、他の形式でそれを見ることができないほど賢いのではないかと思います。

ただし、<!> quot; if <!> quot;を使用しないように制限しているため、または<!> quot;-<!> quot ;、どうしたらいいかわかりません。

大まかなスケッチ(すでに他の人によって提案されています!)

int[] factorials = {1,1,2,6,24, 120,720, ..etc };
return factorials[i];

iも値を配列に入れてみました。 ここでは、if条件とwhileループを使用していますが、算術演算子は使用していません。私もそれらを削除できるかどうかを試してみてください。

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

1 <!> lt; = n <!> lt; = 10 に依存せずに、半分優雅なことができるかどうかを見てみましょう。

  • ループの代わりに、もちろん再帰を使用します。
  • 再帰を終了するifの代わりに、関数ポインタの配列を使用します!
    <==などの比較演算子が必要です。)

編集: damaruは最初に関数ポインタートリックを使用しました。

これにより、以下が得られます。[すべてのコードはテストされておらず、Cコンパイラはありません!]

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

まだsub_1multiplyを実装する必要があります。 add_1から始めましょう。これは、キャリーが停止するまでのビットの単純な再帰です(これを理解していない場合は、末尾の同様のaddを考えるのが簡単です):

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

<=>:私が考えることができる最も簡単なものはロシアの農民の乗算です。これにより、バイナリシフトと加算が削減されます。条件付きでは、再帰的な定式化は次のようになります。

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

条件なしで、ディスパッチ配列トリックを2回使用する必要があります。

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

今見逃しているのは<=>です。これでどうなるかを推測する必要があります-2つの数値のビットに対する同時再帰。これにより、シフトと<=>:

に問題が軽減されます。
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 <=>は、キャリーが停止するまでのビットの単純な再帰です:

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

それだけだと思います! [上記のように、すべてのコードはテストされていません!]

再帰または算術演算を使用できず、入力範囲が限られている場合、結果を配列ルックアップにハードコーディングできます

so:

return factorials[x];

関連する値を事前に入力した場所factorials

1〜100の階乗を計算する必要がある場合はどうなりますか。この大きな数値を格納するにはどうすればよいですか

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

ライブラリ関数を使用しないと言わなかったため:

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

int main( int argc, char** argv)
{
    printf( "%d\n", (int)round( exp( lgamma(2))));
    printf( "%d\n", (int)round( exp( lgamma(3))));
    printf( "%d\n", (int)round( exp( lgamma(4))));
    printf( "%d\n", (int)round( exp( lgamma(5))));
    printf( "%d\n", (int)round( exp( lgamma(6))));
    printf( "%d\n", (int)round( exp( lgamma(7))));
    printf( "%d\n", (int)round( exp( lgamma(8))));
    printf( "%d\n", (int)round( exp( lgamma(9))));
    printf( "%d\n", (int)round( exp( lgamma(10))));
    printf( "%d\n", (int)round( exp( lgamma(11))));

    return 0;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top