离散数学-趣味题之一

        这周接触了离散数学的第一章,也就是基础章节,主要是集合论、递推式、组合排列数方面的基础知识。这里含有关于递推式的一道题目。

        最本来的解法是采用了递推式进行求解,但是我想到了一种组合排列数的解法。

一、原始题目:

        使用多米诺骨牌,即1x2的小方格,覆盖2 x n的方格棋盘,有多少种不同的方式?

二、递推关系进行求解:

        从题目而言,我们发现的递推关系是:当为n时,只考虑最右边的竖着的两个方格,如果用一个竖立的多米诺骨牌进行填充,那么此时的填充方式跟n-1时的一致,如图a所示;而如果用两个横着的多米诺骨牌填充,那么此时的填充方式数目跟n-2时的一致,如图b所示。

图a

      图b

        因此我们可以得到这样的递推式子,即f(n)=f(n-1)+f(n-2),也就是2-阶常系数齐次递推关系式,更准确些,该式就是斐波那契数列,但是初值时f(1)=1,f(2)=2。

        我们采用2-阶常系数齐次递推关系式的通用解法,如下如所示:

图c. 2-阶常系数齐次递推关系式的通用解法

三、组合排列数解法

        组合排列数解法,其中心思想就是可重排组合数,以及本题目的一个重要特性:填充的多米诺骨牌中,横着的个数一定是2的倍数,即如图所d所示。因为图e所示的情况不会出现,出现的话,将导致无法填充满这个2 x n的区域。

图d

图e,无法完全填充的情况

        因此,我们可以如此进行数学说明:假设竖着排列的骨牌个数为a,横着排列的骨牌个数为b,那么如果填充满整个区域,则存在着这样的关系式a+2*(b/2)=n,即a+b=n,从容斥定理来看,这也是必然的。

        因此,如果a,b确定后,问题就变成了a个竖立的和b/2个横着的骨牌如何排列组合的问题,相当与把b/2个相同的苹果,放入(a+1)个不同的盒子的问题,即是可重排组合数,其组合数为C(a+1+b/2-1,b/2)=C(a+b/2, b/2)=C(n-b/2, b/2)。

        如此,我们就知道了当a,b确定后,此时的情况个数为多少了。

        而另一方面,我们知道,对于任何一个n,b的情况为1+(n//2)种 ,那么我们可以得到f(n)如下所示:

    四、验证

        看起来上述的两种方法得到了完全不同的结果,但是实际上两者是完全一致的。利用两种不同的解法,我通过python验证了两者的结果,我的验证结果如下所示,combine是采用第二种解法得到的曲线,facb是采用斐波那契数列的结果,x轴表示n,y轴表示2 x n时的有多少种填充方式:

五、python代码如下所示:

import math
import numpy as np
import matplotlib.pyplot as plt
# 通过斐波那契数列进行计算
def facb(n):
    if n == 1:
        return 1
    elif n == 2:
        return 2
    n -= 1
    a0 = (3*math.sqrt(5)+5)/10
    b0 = (3*math.sqrt(5)-5)/10
    a = (1+math.sqrt(5))/2
    b = (1-math.sqrt(5))/2
    return a0*math.pow(a,n)+b0*math.pow(b,n)

# 阶乘函数
def factorial(num):
    # 查看数字是负数,0 或 正数
    if num < 0:
        print("抱歉,负数没有阶乘")
        return None
    elif num == 0:
        print("0 的阶乘为 1")
        return 1
    else:
        factorial = 1
        for i in range(1, num + 1):
            factorial = factorial * i
        print("%d 的阶乘为 %d" % (num, factorial))
        return factorial


# 以下是自己通过组合数进行得到的结果
def comb(n):
    b = [i//2 for i in range(n+1) if i%2==0]
    res = 0
    for i in b:
        m = n - i
        res += factorial(m)/(factorial(m-i)*factorial(i))
    return res

def test_two(n):
    x = np.arange(1, n + 1)
    yc = []
    yf = []
    for i in x:
        yc.append(comb(i))
        yf.append(facb(i))
    yc = np.array(yc)
    yf = np.array(yf)
    plt.xlabel('n')
    plt.ylabel('number')
    plt.plot(x, yc, "x-", label='combine')
    plt.plot(x, yf, "+", label='facb')
    for xy in zip(x, yc):
        plt.annotate(str(int(xy[1])), xy=xy)
    plt.grid()
    plt.legend()
    plt.savefig("两者的结果.jpg")
    plt.show()

if __name__ == '__main__':
    test_two(10)

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值