2015-01-21 5 views
1

저는 컴퓨터 공학 학생이며 다음 학기에 C 코스를 시작할 예정입니다. 그러니 약간 자신을 준비하기 위해, 저는 혼자서 C를 배우기 시작했고, 처음에는 어떻게 보 였는지, 매우 진보 된 수준으로는 보이지 않는, 재미있는 일을 발견했습니다.파스칼의 삼각형 (C 언어)

주어진 위치의 값을 계산하는 프로그램을 작성하려면 파스칼의 삼각형. 그리고 그것을 계산하기 위해 주어진 수식은 요소 = 행으로 기록됩니다!/(위치! * (행 위치)!)

개의 숫자로 테스트 할 때까지는 괜찮은 것으로 보이는 간단한 콘솔 프로그램을 작성했습니다.

이 프로그램을 행 16과 위치 3에서 사용하려고하면 값이 0으로 계산됩니다 (실제로이 값은 560으로 계산되어야 함). 삼각형은 정수이고 하나보다 큽니다.

큰 번호를 저장하고 처리하는 에 문제가 있다고 가정합니다.. 계승 함수는 정상적으로 작동하는 것처럼 보입니다. 많은식이를 시도 할 때까지 제가 사용한 공식은 작동합니다.

여기서 가장 좋은 해결책은 How do you printf an unsigned long long int(the format specifier for unsigned long long int)?입니다. uint64_t 유형의 inttypes.h 라이브러리를 사용하지만 아직 제공하지 않습니다. 나에게 필요한 결과. 당신이 당신의 중간 계산을 위해 정기적 INT 변수를 사용하는 경우 64 비트 정수가 차이를하지 않습니다 반환하는 경우에도

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

void clear_input(void); 
uint64_t factorial(int x); 

int main() 
{ 
    // Printing 
    printf("This program computes the value of a given position in Pascal's Triangle.\n"); 
    printf("You will be asked for row and position of the value.\n"); 
    printf("Note that the rows and positions starts from 0.\n"); 
    printf("\n"); 
    printf("  1   * 0 \n"); 
    printf(" 1 1   * 1 \n"); 
    printf(" 1 2 1  * 2 \n"); 
    printf(" 1 3 3 1  * 3 \n"); 
    printf(" 1 4 6 4 1  * 4 \n"); 
    printf(" **************** \n"); 
    printf(" 0 1 2 3 4   \n"); 
    printf("\n"); 

    // Initializing 
    int row, pos; 

    // Input Row 
    printf("Enter the row: "); 
    scanf("%d", &row); 
    clear_input(); 

    // Input Position 
    printf("Enter the position in the row: "); 
    scanf("%d", &pos); 
    clear_input(); 

    // Initializing 
    uint64_t element, element_1, element_2, element_3, element_4; 

    // Previously written as -> element = (factorial(row))/(factorial(pos) * factorial(row - pos)); 
    // Doesn't fix the problem 
    element_1 = factorial(row); 
    element_2 = factorial(pos); 
    element_3 = factorial(row - pos); 
    element_4 = element_2 * element_3; 

    element = element_1/element_4; 

    // Print result 
    printf("\n"); 
    printf("%"PRIu64"\n", element_1); // Temporary output 
    printf("%"PRIu64"\n", element_2); // Temporary output 
    printf("%"PRIu64"\n", element_3); // Temporary output 
    printf("%"PRIu64"\n", element_4); // Temporary output 
    printf("\n"); 
    printf("The element is %"PRIu64"", element); 
    printf("\n"); 

    return 0; 
} 

void clear_input(void)           // Temporary function to clean input from the keyboard 
{ 
    while(getchar() != '\n'); 
} 

uint64_t factorial(int x)          // Function to calculate factorial 
{ 
    int f = 1, i = x; 
    if (x == 0) { 
     return 1; 
    } 
    while (i != 1) { 
     f = f * i; 
     i = i - 1; 
    } 
    return f; 
} 
+1

당신이 다음 사용하여 많은 수의 (> 32 비트)를 사용하는 경우 'int' 당신에게 잘못된 결과를 얻을 것입니다. 'uint64_t' 데이터 타입을 사용하여 리턴 타입을 지정한다면, 같은 데이터 타입을 사용하여 계산을 계산해야합니다. 함수는 이제 내부적으로 'int'를 사용하고 결과는 암시 적으로'uint64_t'로 캐스팅되어 실제로 도움이되지 않습니다. –

답변

1

Factorials는 really big really fast입니다 (목록을 보려면 조금 아래로 스크롤하십시오). 64 비트 숫자도 20!까지만 유효합니다. 따라서 곱셈을 시작하기 전에 약간의 사전 처리 작업을 수행해야합니다.

일반적인 생각은 분자와 분모를 분해하고 모든 공통 요인을 제거하는 것입니다. Pascal 's Triangle의 결과는 항상 정수이므로 모든 일반적인 요소가 제거 된 후에 분모가 1이된다는 보장을받습니다.

예를 들어 row=35position=10이 있다고합시다. 이어서 계산 그래서 제 단순화 분모의 큰 요인이 분자의 작은 조건을 모두 취소이다

35 * 34 * 33 * ... * 26 * 25 * 24 * ... * 3 * 2 * 1 
--------------------------------------------------- 
    10!    * 25 * 24 * ... * 3 * 2 * 1 

이다

element = 35!/10! * 25! 

이다. 어느 나뭇잎인가

35 * 34 * 33 * ... * 26 
----------------------- 
10 * 9 * 8 * ... * 1  

이제 분자와 분모의 나머지 일반 요인을 제거해야합니다. 분자의 모든 수를 배열에 넣는 것이 도움이됩니다. 그런 다음 분모의 각 숫자에 대해 greatest common divisor (gcd)을 계산하고 분자와 분모를 gcd로 나눕니다.

다음 코드는이 기술을 보여줍니다.

array[10] = { 35, 34, 33, 32, 31, 30, 29, 28, 27, 26 }; 

for (d = 10; d >= 2; d--) 
{ 
    temp = d; 
    for (i = 0; i < 10 && temp > 1; i++) 
    { 
     common = gcd(array[i], temp); 
     array[i] /= common; 
     temp /= common; 
    } 
} 

여기에, 모든 말과 배열을 완료되면 코드가

array[10] = { 1, 17, 11, 1, 31, 1, 29, 14, 3, 26 } 

곱하기에게 함께 그 숫자로 끝과 대답이 183579396 스텝

d=10 i=0 temp=10 array[0]=35 ==> gcd(35,10)=5, so array[0]=35/5=7 and temp=10/5=2 
d=10 i=1 temp=2 array[1]=34 ==> gcd(34, 2)=2, so array[1]=34/2=17 and temp=2/2=1 
inner loop breaks because temp==1 
d=9 i=0 temp=9 array[0]=7 ==> gcd(7,9)=1, so nothing changes 
d=9 i=1 temp=9 array[1]=17 ==> gcd(17,9)=1, so nothing changes 
d=9 i=2 temp=9 array[2]=33 ==> gcd(33,9)=3, so array[2]=11 and temp=3 
d=9 i=3       ==> gcd(32,3)=1 
d=9 i=4       ==> gcd(31,3)=1 
d=9 i=5 temp=3 array[5]=30 ==> gcd(30,3)=3, so array[5]=10 and temp=1 
inner loop breaks 

에 의해 단계를 않습니다 무엇 전체 계산은 32 비트 정수를 사용하여 수행 할 수 있습니다. 일반적으로 대답이 32 비트에 적합하면 32 비트 계산을 수행 할 수 있습니다.

1

당신은 계승을 계산하는 경우.

uint64_t factorial(uint64_t x) 
{ 
    uint64_t f = 1, i = x; 
    if (x == 0) { 
     return 1; 
    } 
    while (i != 1) { 
     f = f * i; 
     i = i - 1; 
    } 
    return f; 
} 

또한 수식을 재 배열하여 실제로 큰 중간 값을 계산할 필요가없는 방법에 대해 생각해보십시오. 예를 들어 다음과 같이 재정렬 할 수 있습니다.

요소 = (계승 (행)/계승 (포지))/계승 (행 - 위치);

그러면 두 계승을 함께 곱해서 정말 큰 숫자를 얻지 않을 것입니다.

또한 계승 (행)/계승 (포지셔너)을 계산할 때 계승 (행) 및 계승 (포지셔닝) 조건을 제거 할 수 있으므로 전체 계승을 계산할 필요가 없습니다.

3

는 (내 C 녹슨, 그래서이 슈퍼 정확하지 않을 수 있습니다) 귀하의 계승 함수는 uint64_t를 반환하지만 일반의 int로 계산을하고있어

. f와 i를 uint64_t로 변경했다면 현재 정수 오버 플로우 문제를 피할 수있을 것이라고 생각합니다.

그러나 여전히 오버 플로우가 발생하기 쉽습니다 (uint64_t는 약 21 오버플로!). 이것을 피하려면 알고리즘을 조금 더 똑똑하게 사용할 수 있습니다. 행 = 16 및 위치 = 3이면 16이 필요합니다!/(3! * 13!). 대부분의 조건 (16!/13!은 14 * 15 * 16)을 취소하고 14 * 15 * 16/(1 * 2 * 3)로 끝낼 수 있습니다. 이렇게하면 행 21보다 훨씬 많은 프로그램을 사용할 수 있습니다.

0

이 작동합니다 :

#include <stdio.h> 

int main() 
    { 
    printf ("\n"); 
    int n = 10; 
    int i; 
    int j; 
    int x[n]; 

    for (i = 0; i < n; i++) 
     x[i] = 0; 

    for (i = 1; i <= n; i++) 
     { 
     for (j = n - 1; j >= 1; j--) 
       x[j] = x[j-1] + x[j]; 

     x[0] = 1; 

     int s = n - i; 

     for (j = 0; j < s; j++) 
       printf (" "); 

     for (j = 0; j < n; j++) 
       { 
       if (x[j] != 0) 
        printf (" %3d", x[j]); 
       } 

     printf ("\n"); 
     } 

    printf ("\n"); 
    return 0; 
    } 
관련 문제