C编程数字和山脉

时间:2018-09-27 22:19:58

标签: c math catalan

我创建了一个给出第n个加泰罗尼亚数字的C程序,到目前为止一切正常。在这里:

#include <stdio.h>

int catalan(int);

main()
{
    int number, catalannumber;

    printf("This is a program to find a given catalan number.\nIntroduce your desired number: ");
    scanf("%d", &number);

    while (number < 1)
    {
        printf("Number must be greater or equal to 1. Reintroduce: ");
        scanf("%d", &number);
    }

    catalannumber = catalan (number);

    printf("The %dth number corresponds to %d.\n", number, catalannumber);
}

int catalan(int n) {
  if (n == 1)
    return 1; 
  else
    return 2 * (2*n - 1) * catalan(n-1) / (n+1);
}

然后我发现了这个“经典”山脉问题,所有可能的山脉如图所示: http://www.maths.usyd.edu.au/u/kooc/catalan/cat3moun.pdf

这是n的较小值的“山脉范围”的样子 enter image description here source

我的目标是能够创建一个程序,该程序:

  1. 给他们编号后,您就可以选择“山顶”的数量(<= number)

  2. 该程序将计算(给定数量的)多少个不同的山脉具有该数量的山顶。

通过pdf文件,我知道:

  1. 数量n = 3且“山顶” = 2->有3个(总共5个)山脉与2个山顶(两个不同的山脉“类型”)。

  2. 数量n = 4和“山顶” = 3-> 6个不同的山脉。

我的问题是,完成这项工作的最佳方法是什么?有什么公式吗?我曾考虑过Pascal Triangle和Fibonacci系列,但之间没有任何关系。我想知道什么是可能的解决方案。 任何帮助将不胜感激。谢谢。

1 个答案:

答案 0 :(得分:3)

让我们首先看看暴力破解方法。

加泰罗尼亚语编号 c n 是人们可以组合 n 上击(╱)和 n 的方式的数量向下移动(╲),而不会超出地平线。根据定义, c n =(n + 2)/ 2·(n + 3)/ 3·...(n + n)/ n。

我们可以使用无符号2 n 位整数来描述每个组合。 (但是,并非所有2 n 位无符号整数值都描述有效的组合。)如果我们将非零或置位的比特视为上冲程,而将零比特的比特视为下冲程,则每当下冲程发生时,就会出现峰值中风。 (当下行然后是上行时,您将看到一个山谷。)

(请注意,并非所有无符号的2 n 位整数值都描述了有效的组合。要有效,第一位必须是向上笔划,并且必须精确地是 n 上冲。)

因此,首先编写一个函数,该函数可以计算由无符号整数描述的一个组合中的峰数。 (请注意,由于每个描述组合的无符号整数都已精确地设置了 n 个位,因此我们无需显式传递 n ,而只需传递描述组合的无符号整数。)例如,

unsigned int  peaks(unsigned int  description)
{
    unsigned int  count = 0;

    while (description) {
        count += ((description & 3) == 1);
        description >>= 1;
    }

    return count;
}

以上,从最低有效位开始读取组合。 (并且因为必须将山脉设置为可能超出地平线,所以没有偶数描述组合。)表达式(description & 3)隔离了最后两个有效位。四种可能的情况分别以数值递增的顺序对应于双下坡,峰,谷和双上坡。峰值情况对应于值1(二进制形式为01 b :向上笔划,然后向下笔划,以从右到左的重要性递增的顺序读取数字)。在C语言中,逻辑 False 的值为零,而逻辑 True 的值为1,因此在上述循环中,我们得到了设置位为接着(在较高位置)清楚一点。

Value  Mountains       n   Peaks
    1   ╱╲             1     1
    3   ╱╱╲╲           2     1
    5   ╱╲╱╲           2     2
    7   ╱╱╱╲╲╲         3     1
    9   ╱╲╲╱         Not a valid combination
   11   ╱╱╲╱╲╲         3     2
   13   ╱╲╱╱╲╲         3     2
   15   ╱╱╱╱╲╲╲╲       4     1
   17   ╱╲╲╲╱        Not a valid combination
   19   ╱╱╲╲╱╲         3     2
   21   ╱╲╱╲╱╲         3     3
   23   ╱╱╱╲╱╲╲╲       4     2
   25   ╱╲╲╱╱╲       Not a valid combination
   27   ╱╱╲╱╱╲╲╲       4     2
   29   ╱╲╱╱╱╲╲╲       4     2
   31   ╱╱╱╱╱╲╲╲╲╲     5     1
   33   ╱╲╲╲╲╱       Not a valid combination
   35   ╱╱╲╲╲╱       Not a valid combination
   37   ╱╲╱╲╲╱       Not a valid combination
   39   ╱╱╱╲╲╱╲╲       4     2

接下来,创建一个函数,该函数生成所有无符号整数值,这些整数值描述特定 n 的有效组合,并对对应于特定数量峰的计数进行计数。

一种暴力方式是编写峰值计数函数,以便对所有无效组合都返回0。例如:

static unsigned int  peaks(unsigned int  description)
{
    unsigned int  count = 0;
    int           height = 0;

    /* Description must start with an upstroke. */
    if (!(description & 1))
        return 0;

    while (description) {
        switch (description & 3) {
        case 0: /* Downslope; next is downslope. */
            if (--height < 0)
                return 0;
            break;

        case 1: /* Upslope; next is downslope. */
            count++;
            height++;
            break;

        case 2: /* Downslope; next is upslope. */
            if (--height < 0)
                return 0;
            break;

        default: /* 3: Upslope; next is upslope. */
            height++;

        }

        description >>= 1;
    }

    return count;
}

描述对应的 n (如果peak(description) > 0)是描述中设置的位数。算是一个漂亮的技巧

unsigned int  popcount(unsigned int  value)
{
    unsigned int  count = 0;
    while (value) {
        value &= value - 1;
        count++;
    }
    return count;
}

使用这两个函数,可以通过探索所有2 n 位无符号整数(从0到{ {1}}(含)。


为获得更好的方法,让我们检查每个 n 的峰数:

(1 << (2*n)) - 1

换句话说, n = 6具有132个有效组合。其中,一个带有一个峰,一个带有两个峰,15个带有三个峰,50个带有四个峰,一个带有六个峰。

如果我们只为峰计数形成整数序列,则可以将上面的表达式表示为

n Combs   Occurrences*Peaks
0    1     1*0
1    1     1*1
2    2     1*1,  1*2
3    5     1*1,  3*2,  1*3
4   14     1*1,  6*2,  6*3,  1*4
5   42     1*1, 10*2, 20*3, 10*4,  1*5
6  132     1*1, 15*2, 50*3, 50*4, 15*5, 1*6

依此类推,依次以1,21,105,175,105,21,1表示 n = 7,并以1,28,196,490,490,196,28, 1表示 n = 8,依此类推。

如果我们对该序列进行OEIS搜索,我们会发现这些实际上称为Narayana数字 T n k ),整个整数序列为OEIS A001263

(注意:我不知道是这样!我所知道的一切我都可以使用OEIS来找出序列是否已知,通常是这样。换句话说,我不仅向您展示了答案,这个特殊的问题,但是从蛮力数值方法开始,我如何有效地找到这种问题的解决方案(如果我可以这么说的话)。)

因此,数学答案是Narayana number T n k )告诉您不同山脉的数量对应于加泰罗尼亚语编号 c n ,具有恰好 k 个峰值。

如果将binomial coefficient实现为函数1, 1, 1, 1, 3, 1 1, 6, 6, 1, 1, 10, 20, 10, 1, 1, 15, 50, 50, 15, 1, ,则答案是binomial(n, k)

但是请注意,您可以更有效地实现T(n, k) = binomial(n, k) * binomial(n, k - 1) / n作为术语乘积之间的划分(即,使用两个术语数组,并使用greatest common divisor消除通用术语和术语乘积辅助功能,由于术语取消而没有精度问题。)