我创建了一个给出第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
的较小值的“山脉范围”的样子
source
我的目标是能够创建一个程序,该程序:
给他们编号后,您就可以选择“山顶”的数量(<= number)
该程序将计算(给定数量的)多少个不同的山脉具有该数量的山顶。
通过pdf文件,我知道:
数量n = 3且“山顶” = 2->有3个(总共5个)山脉与2个山顶(两个不同的山脉“类型”)。
数量n = 4和“山顶” = 3-> 6个不同的山脉。
我的问题是,完成这项工作的最佳方法是什么?有什么公式吗?我曾考虑过Pascal Triangle和Fibonacci系列,但之间没有任何关系。我想知道什么是可能的解决方案。 任何帮助将不胜感激。谢谢。
答案 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消除通用术语和术语乘积辅助功能,由于术语取消而没有精度问题。)