【问题标题】:Pascal's Triangle in CC语言中的帕斯卡三角形
【发布时间】:2015-03-19 10:18:25
【问题描述】:

我是一名计算机工程专业的学生,​​下学期我将开始 C 课程。因此,为了让自己做好一点准备,我开始自学 C,偶然发现了一个有趣的任务,专为我乍看之下的 C 设计,而不是非常高级的水平。

任务是编写一个程序来计算帕斯卡三角中给定位置的值。计算它的公式写为 element = row! /(位置!*(行 - 位置)!)

我编写了一个简单的控制台程序,它似乎可以正常工作,直到我用 large 数字对其进行测试。

在第 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 库,但它仍然没有给我所需的结果。

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

【问题讨论】:

  • 如果您使用大数(>32 位),那么使用int 会得到不正确的结果。如果您使用uint64_t 数据类型来指定返回类型,那么您需要使用相同的数据类型来计算您的计算。您的函数现在在内部使用int,结果被隐式转换为uint64_t,这对您没有帮助。

标签: c largenumber pascals-triangle


【解决方案1】:

当您计算阶乘时,即使您返回一个 64 位整数,如果您使用常规 int 变量进行中间计算,也不会产生影响。改为:

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

另外,想想如何重新排列方程,这样就不必计算非常大的中间值。例如,您可以重新排列为:

元素=(阶乘(行)/阶乘(位置))/阶乘(行-位置);

这样你就不会将两个阶乘相乘并得到一个非常大的数字。

此外,当您计算 factorial(row) / factorial(pos) 时,您可以消除同时在 factorial(row) 和 factorial(pos) 中的项,因此您不需要计算整个 factorials。

【讨论】:

    【解决方案2】:

    (我的 C 生锈了,所以这可能不太准确)

    您的阶乘函数返回一个 uint64_t,但它使用常规整数进行计算。如果您将 f 和 i 更改为 uint64_t,我认为您将避免当前的整数溢出问题。

    但是,您仍然会很快遇到溢出(uint64_t 将在 21 左右溢出!)。为避免这种情况,您可以使用该算法更聪明一些。如果 row=16 和 position=3,你需要 16! /(3!* 13!)。您可以取消大部分条款(16!/13!只是 14*15*16)并最终得到 14*15*16 / (1*2*3)。这将使您的程序比第 21 行更进一步。

    【讨论】:

      【解决方案3】:

      因子获得really big really fast(向下滚动一点查看列表)。即使是 64 位数字也只能达到20!。因此,在开始相乘之前,您必须进行一些预处理。

      总的思路是把分子和分母分解,去掉所有的公因子。由于帕斯卡三角的结果始终是整数,因此可以保证在去除所​​有公因数后分母为 1。

      例如,假设您有row=35position=10。那么计算就是

      element = 35! / 10! * 25!
      

      这是

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

      所以第一个简化是分母中较大的阶乘消除了分子中的所有较小项。离开了

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

      下面是代码一步一步的作用

      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
      

      当一切都说完了,数组结束了

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

      将这些数字相乘,答案是183579396,整个计算可以使用 32 位整数进行。一般来说,只要答案适合 32 位,就可以用 32 位进行计算。

      【讨论】:

        【解决方案4】:

        这将起作用:

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

        【讨论】:

          猜你喜欢
          • 2011-07-07
          • 1970-01-01
          • 2021-05-13
          • 2012-10-24
          • 1970-01-01
          • 1970-01-01
          • 2014-11-12
          相关资源
          最近更新 更多