奋斗的demon——基本计数方法(理论)

呆萌的Demon,今天想知道数学上的计数有哪些?你能告诉他吗?就让大白老师来告诉他!

数学上计数方法有两种基础原理——加法原理和乘法原理。

加法原理。假设demon完成作业需要n个方法,第i个方法有pi种方案,则一共有p1+p2+...+pn种方案。

乘法原理。假设demon完成一个动作需要n个步骤,第i个步骤有pi种方案,则一共有p1*p2*...*pn种方案。

乘法原理是加法原理的特殊情况,两者都可以用于递推。其中加法原理的关键的是分类,各类别之间必须没有重复,没有遗漏。如果出现了重复,可以用容斥原理。而按照常理来说一般加法原理的应用,大部分都是以容斥原理来实现的。

容斥原理。假如demon班级里有10个人喜欢数学,有10个人喜欢英语,有10个人喜欢编程,班级里一共有几个人?是10+10+10=30个人吗?事实当然 不是的,因为每个人的爱好不止一个,我们不排除有学生可能同时喜欢数学和英语,或者英语和编程,甚至还有可能三个都喜欢。为了方便,我们对这个例子进行抽象,把喜欢数学,英语,编程的学生集合用A,B,C表示,去除重复的学生,就是班级人数:

|A∪B∪C|=|A|+|B|+|C|-|A∩B|-|A∩C|-|B∩C|-|A∩B∩C|

关于容斥原理,大白要求demon先把核心的公式记住:

|A|+|B|=|A∪B|+|A∩B|

当然容斥原理还有很多变种,但是大白老师说思路万变不离其宗:都是加加减减,把重复的扣掉,再把扣多的加回来。如果一次很难一次性想清楚,可以先推导两个集合或者3个集合的例子,然后在深入递推。

下面我们为demon列举几个常见的问题:

问题1:排列问题。有n个不同的数,选k个排成一排,每个数最多选一次,问有几种排列方法?

分析:记答案为 P(n,k)。由乘法原理,我们可以得出P(n,k)=n(n-1)(n-2)...(n-k+1)=n!/(n-k)!。其中P(n,n)=n!.

 

问题2:组合问题。有n个不同的数,选k个(顺序无关),每个数最多选一次,问有几种不同的选法?

分析:记答案为C(n,k)。把n选k的排列问题看成两步:首先选出k个数的组合,然后把这个数全排列(从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列)(就是=>P(k,k))。所以我们得出

C(n,k)=P(n,k)/P(k,k)=n!/((n-k)!k!)

关于数学上C(m,n),有几个性质:

性质1:C(n,0)=C(n,n)=1

性质2:C(n,k)=C(n,n-k)

性质3:C(n,k)+C(n,k+1)=C(n+1,k+1)

性质4:见下一问题

 

 

这里demon童鞋要和大家分享一个巧妙的算法,就是阶乘算法:

高效算法 
斯特林公式(stirling)求阶乘的位数:

 

#include<stdio.h>  
#include<math.h>  
#define PI 3.1415926  
int main()  
{  
int cases,n,ans;  
scanf("%d",&cases);  
while(cases--)  
{  
   scanf("%d",&n);  
   if(n==1)  
    printf("1\n");  
   else  
   {  
     ans=ceil((n*log(n)-n+log(2*n*PI)/2)/log(10));  
      printf("%d\n",ans);  
   }  
}  
return 0;  
}  

 

 

 

问题3:(二项式展开):求(a+b)^n展开式的各项系数

分析:根据二项式定理:(a+b)^n=Σ(k=0-n)[C(n,k)*a^(n-k)*b^k],只需求出所有的C(n,k)。

那么问题来了,假如我们使用性质3 来求,时间复杂度是O(N^2)。有没有很好的呢?

性质4:C(n,k+1)=C(n,k)*(n-k)/(k+1)

证明:只需用C(n,k+1)/C(n,k)展开式相除,即可

这样的话,只需要O(n)时间,就可以求出问题3 。

这里demon童鞋还要分享一下小算法:

ln(C(n,m)) = ln(n!)-ln(m!)-ln((n-m)!);

这种算法可以在数据很大的情况下,高速运算。

 

double lnchoose(int n, int m)
{
    if (m > n)
    {
        return 0;
    }
    if (m < n/2.0)
    {
        m = n-m;
    }
    double s1 = 0;
    for (int i=m+1; i<=n; i++)
    {
        s1 += log((double)i);
    }
    double s2 = 0;
    int ub = n-m;
    for (int i=2; i<=ub; i++)
    {
        s2 += log((double)i);
    }
    return s1-s2;
}
double choose(int n, int m)
{
    if (m > n)
    {
        return 0;
    }
    return exp(lnchoose(n, m));
}

 

 

 

问题4:有重复元素的全排列。有k个元素,其中第i个元素有ni个,求全排列个数。

分析:令所有的ni之和为n,再设答案为x。首先做全排列,然后把所有元素编号,其中第s种元素编号为1~ns(比如,有3个a,2个b,先排列撑aabba,然后再编号为a1a2b2b1a3)。这样之后,由于编号后所有的元素均不相同,方案总数为n的全排列数n!(问题一)。根据乘法原理,我们可以等到n1!n2!n3!...nk!x=n!,求x即可。

 

问题5:可重复选择的组合。有n个不同元素,每个元素可以选多次,一共选k个元素,有多少种方法?例如n=3,k=2有6种:(1,1),(1,2),(1,3),(2,2),(2,3),(3,3).

分析:设第i个元素选xi个,问题转化为求方程x1+x2+...+xn=k的非负整数解的个数。令yi=xi+1,则答案为y1+y2+...+yn=k+n的正整数解的个数。想象有k+1个数字“1”排成一排,则问题等价于把这些“1”分成n个部分,有几种方法?这相当于在k+n-1个“候选分隔线”中选n-1个,即C(k+n-1,n-1)=(k+n-1,k)。(性质2 )


问题6:单色三角形。给定空间里的n(≤1000)个点,其中没有三点共线。每两个点之间都用红色或黑色线段连接。求三条边同色的三角形个数。

分析:直接统计需要的O(n^3)时间,需要优化。这道题,可以逆向思考,这些三角形的颜色情况只有2中,三线同色,两线同色,只要求出两线同色,就可解题。

在这种情况三角形中,恰好有两个定点连接两条不同颜色的边,而且有一个公共点的两条不同颜色的边总是唯一对应一个两线同色的三角形,因此如果第i个点连接了ai条红边n-1-ai条黑边,则这些边属于ai(n-1-ai)个两线同色的三角形。每个两线同色的三角形考虑了两次,因此最终为1/2Σ(i=1-n)[ai(n-1-ai)]

 

 

喜欢组合数的亲们,先要有深入了解:https://wenku.baidu.com/view/adf37d1525c52cc58ad6be0e.html

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值