组合数(Combinatorial_Number)

article/2025/9/23 18:30:30

定义:

从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。

公式:

在线性写法中被写作C(m,n)。

c(m,n)=p(m,n)/n!=m!/((m-n)!*n!)

性质:

1.互补性质

组合数性质如右图所示:

即从m个不同元素中取出n个元素的组合数=从m个不同元素中取出(m-n)个元素的组合数组合数性质

这个性质很容易理解,例如C(9,2)=C(9,7),即从9个元素里选择2个元素的方法与从9个元素里选择7个元素的方法是相等的。

规定:C(m,0)=1

2.组合恒等式

若表示在n个物品中选取m个物品,则如存在下述公式: C(n,m)= C(n,n-m)= C(n-1,m-1)+C(n-1,m)

求法:

1、一般方法

直接套公式:

组合数性质

求出m!、n!、(m-n)!然后带人公式;

代码如下:

# include<stdio.h>
# include<math.h>
int f(int n){int i,m=1;for(i=1;i<=n;i++){m=m*i;}return m;
}
int main(){int n,m;scanf("%d %d",&n,&m);printf("%d\n",f(n)/(f(m)*f(n-m)));return 0;
}

稍微用点小技巧

long long C(int n,int m)
{if(m<n-m)    m=n-m;long long ans=1;for(int i=m+1;i<=n;++i)    ans*=i;for(int i=1;i<=n-m;++i)    ans/=i;return ans;
}

 

2、杨辉三角

就算是long long最多20!左右;再大就会爆了

那就用到我们大数学家杨辉的三角了

: C(n,m)= C(n,n-m)= C(n-1,m-1)+C(n-1,m)

#include<iostream>
using namespace std;
long long c[100][100];
inline void get_it(int n)
{c[0][0]=1;for (int i=1;i<=n;i++)for (int j=0;j<=i;j++)if (i==j ||j==0) c[i][j]=1;else c[i][j]=c[i-1][j]+c[i-1][j-1];
}
int main()
{get_it(60);for (int i=1;i<=60;i++){for (int j=1;j<=i;j++)cout<<c[i][j]<<" ";cout<<endl;}
}

理论上只要数组开的出来都可以,最多,emmm,反正比一般方法强。 

3、快速组合数

这个要用快速幂:https://blog.csdn.net/weixin_43272781/article/details/85058595

主要是用在一般方法;求阶乘的时候。

我就不写了。

4、求余组合数

以上方法最多也就到long long的极限,当然超过long long的我们也存储不下了,但是如果我们只需要一部分高阶组合数,用杨辉三角太浪费了吧。而且一般题目会有求余的要求,那么接下来就是大招了。

证明:https://blog.csdn.net/arrowlll/article/details/52629448

 1).扩展欧几里德:b*x+p*y=1 有解,x就是所求2).费马小定理:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)



1. n!/(m!*(n-m)! =x%p ,先对算出n!、m!、(n-m)!对p取模的余数,就转换为a/b=x%p;因为p为素数,所以等价于bx+py=a;然后用扩展的欧几里得定理算出 bx’+py’=1的解,x=x’*a,就得到了最终的x的值,即C(m,n)%p得值。

2.逆元 其实如果mod是素数 则b的逆元其实就是b^(mod-2) 
也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 ;

int inv(int a) {  //return fpow(a, MOD-2, MOD);  return a == 1 ? 1 : (long long)(MOD - MOD / a) * inv(MOD % a) % MOD;  
}  
LL C(LL n,LL m)  
{  if(m < 0)return 0;  if(n < m)return 0;  if(m > n-m) m = n-m;  LL up = 1, down = 1;  for(LL i = 0 ; i < m ; i ++){  up = up * (n-i) % MOD;  down = down * (i+1) % MOD;  }  return up * inv(down) % MOD;  
}  



3.当n和m比较大,mod是素数且比较小的时候(10^5左右),通过Lucas定理计算

Lucas定理:A、B是非负整数,p是质数。A B写成p进制:A=a[n]a[n-1]…a[0],B=b[n]b[n-1]…b[0]。 
则组合数C(A,B)与C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0]) mod p同余 
即:Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)

#include<iostream>
//#include<algorithm>
using namespace std;
typedef long long ll;
int quick_power_mod(int a,int b,int m){//pow(a,b)%mint result = 1;int base = a;while(b>0){if(b & 1==1){result = (result*base) % m;}base = (base*base) %m;b>>=1;}return result;
}
//计算组合数取模
ll comp(ll a, ll b, int p) {//composite num C(a,b)%pif(a < b)   return 0;if(a == b)  return 1;if(b > a - b)   b = a - b;int ans = 1, ca = 1, cb = 1;for(ll i = 0; i < b; ++i) {ca = (ca * (a - i))%p;cb = (cb * (b - i))%p;}ans = (ca*quick_power_mod(cb, p - 2, p)) % p;return ans;
}
ll lucas(ll n, ll m, ll p) {ll ans = 1;while(n&&m&&ans) {ans = (ans*comp(n%p, m%p, p)) % p;//also can be recusiven /= p;m /= p;}return ans;
}
int main(){ll m,n;while(cin>>n>>m){cout<<lucas(n,m,10007)<<endl;}return 0;
}


C(n % mod, m % mod) % mod; 如果太大还是利用上面的逆元来处理。

半预处理 
由于Lucas定理保证了阶乘的数均小于p,所以可以讲所有的阶乘先预处理,优化C(n,m) 
mod的要求:p<10^6,且为素数 
有效范围:1<=n,m<=10^9

//半预处理  
const ll MAXN = 100000;  
ll fac[MAXN+100];  
void init(int mod)  
{  fac[0] = 1;  for (int i=1; i<mod; i++){  fac[i] = fac[i-1] * i % mod;  }  
}  //半预处理逆元求C(n,m)%mod  
ll C(ll n, ll m)  
{  if ( m>n ) return 0;  return fac[n] * (GetInverse(fac[m]*fac[n-m], mod)) % mod;  
}  

typedef long long LL;
const LL maxn(1000005), mod(1e9 + 7);
LL Jc[maxn];void calJc()    //求maxn以内的数的阶乘
{Jc[0] = Jc[1] = 1;for(LL i = 2; i < maxn; i++)Jc[i] = Jc[i - 1] * i % mod;
}
/*
//拓展欧几里得算法求逆元
void exgcd(LL a, LL b, LL &x, LL &y)    //拓展欧几里得算法
{if(!b) x = 1, y = 0;else{exgcd(b, a % b, y, x);y -= x * (a / b);}
}LL niYuan(LL a, LL b)   //求a对b取模的逆元
{LL x, y;exgcd(a, b, x, y);return (x + b) % b;
}
*///费马小定理求逆元
LL pow(LL a, LL n, LL p)    //快速幂 a^n % p
{LL ans = 1;while(n){if(n & 1) ans = ans * a % p;a = a * a % p;n >>= 1;}return ans;
}LL niYuan(LL a, LL b)   //费马小定理求逆元
{return pow(a, b - 2, b);
}LL C(LL a, LL b)    //计算C(a, b)
{return Jc[a] * niYuan(Jc[b], mod) % mod* niYuan(Jc[a - b], mod) % mod;
}

例题

https://blog.csdn.net/weixin_43272781/article/details/85269419

 

 


http://chatgpt.dhexx.cn/article/H9LQ9DNp.shtml

相关文章

组合数的计算

本篇博客来自南昌理工学院acm集训队成员yyj 组合数 1.定义 组合数&#xff1a;从 n 个不同元素中每次取出 m 个不同元素 &#xff0c;不管其顺序合成一组&#xff0c;称为从 n 个元素中不重复地选取 m 个元素的一个组合。所有这样的组合的种数称为组合数。 2.性质与描述 2…

组合数的性质证明

性质 极其简单的证明 根据杨辉三角的性质&#xff0c;第x行第y个的值为C(x-1,y-1) 那么 一目了然。 严肃的证明

组合数性质--二项式系数之和等于2^n的证明

1.公式 首先我们都知道组合数的意义&#xff0c;就是说一共有n个样本&#xff0c;一次性从中取出m个样本&#xff0c;一共有多少种不同的取法。它的公式如下&#xff1a; 它有这么一个性质&#xff1a; 该性质有若干种证明方式&#xff0c;今天我在这边写出我觉得挺巧妙的一种…

组合数原理

资料来源&#xff1a;https://baike.baidu.com/ 组合数 从n个不同元素中&#xff0c;任取m(m≤n)个元素并成一组&#xff0c;叫做从n个不同元素中取出m个元素的一个组合&#xff1b;从n个不同元素中取出m(m≤n)个元素的所有组合的个数&#xff0c;叫做从n个不同元素中取出m个…

深入详细理解矩阵 (矩阵的加减乘、转置、共轭、共轭转置)

简介 矩阵:英文名Matrix。在数学名词中&#xff0c;矩阵用来表示统计数据等方面的各种有关联的数据。这个定义很好地解释了Matrix代码制造世界的数学逻辑基础。矩阵是数学中最重要的基本概念之一&#xff0c;是代数学的一个主要研究对象&#xff0c;也是数学研究及应用的一个重…

【线性代数之二】矩阵与行列式

一、矩阵 1.1 定义 由 m n 个数aij排成的m行n列的数表称为m行n列的矩阵&#xff0c;简称m n矩阵。记作&#xff1a; 这mn 个数称为矩阵A的元素&#xff0c;简称为元&#xff0c;数aij位于矩阵A的第i行第j列&#xff0c;称为矩阵A的(i,j)元&#xff0c;以数 aij为(i,j)元的…

CString类型转换为LPCSTR类型

今天编程遇到一个问题&#xff0c;就是openGL中某个函数需要传入LPCSTR类型的参数&#xff0c;而通过MFC对话框获取得到的是CString类型的参数&#xff0c;因此需要将CString转化为LPCSTR类型&#xff0c;网上有很多这样的强转类型&#xff0c;然而却发现在强转的时候没有用&am…

linux jstat 简介

本文目录一览&#xff1a; 1、Linux使用jstat命令查看jvm的GC情况2、linux怎么监控 jvm内存 jstat3、Linux系统监控要用到哪些命令4、linux上如何安装jstatd服务 Linux使用jstat命令查看jvm的GC情况 Linux 使用jstat命令查看jvm的GC情况 命令格式 jstat命令命令格式&#…

内存屏障与volatile(C语言版)

内存屏障与volatile&#xff08;C语言版&#xff09; 最有价值的写在最前面 内存屏障与volatile是高并发编程中比较常用的两个技术&#xff0c;无锁队列的时候就会用到这两项技术。然而这两项技术设计比较广的基础知识&#xff0c;所以比较难以理解&#xff0c;也比较不容易解…

WDM驱动

WDM英文Windows Driver Model(WDM)的缩写。 简介 WDM WDM是WINDOWS2000认证的驱动程序&#xff0c;WIN2000由NT发展而来&#xff0c;所以对于设备的支持功能有限&#xff0c;同时为了最大限度的保障稳定性&#xff0c;所以推崇WDM驱动&#xff0c;但同时WDM驱动也就是功能最少…

快速转换dBm与W

dBm是一个表示功率绝对值的值&#xff08;也可以认为是以1mW功率为基准的一个比值&#xff09;&#xff0c;计算公式为&#xff1a;10log&#xff08;功率值/1mw&#xff09;。 这里我们介绍一种将dBm转换为W的口算方法&#xff0c;这一方法总结起来就是 “1个基准”和“2个原则…

学内核之十一:ARM64屏障指令使用指南

关于屏障指令&#xff0c;主要是与乱序有关。特别是在内核开发中&#xff0c;这是一个非常重要的主题。屏障由乱序引起&#xff0c;乱序则是由优化引起。 自从摩尔定律不断被逼近极限&#xff0c;半导体的优化不再单纯通过提升频率来实现。多核心、并发执行变成了主流的优化思路…

WTM

WTM的由来 WalkingTec.Mvvm框架&#xff08;简称WTM&#xff09;最早开发与2013年&#xff0c;基于Asp.net MVC3 和 最早的Entity Framework, 当初主要是为了解决公司内部开发效率低&#xff0c;代码风格不统一的问题。经历了四年间数十个项目的考验&#xff0c;框架逐步的完善…

WMIC使用

目录 WMI 远程创建进程 wmiexec wmiexec.py wmiexec.vbs Invoke-WmiCommand.ps1 Invoke-WMIMethod WMI WMI全称“windows管理规范”&#xff0c;是一个windows服务。从win2003开始一直存在。它原本的作用是方便管理员对windows主机进行管理。因此在内网渗透中&#xff…

Arm64内存屏障

一、内存类型 ARMv8架构将系统中所有的内存&#xff0c;按照它们的特性&#xff0c;划分成两种&#xff0c;即普通内存和设备内存。并且它们是互斥的&#xff0c;也就是说系统中的某段内存要么是普通内存&#xff0c;要么是设备内存&#xff0c;不能都是。 1&#xff09;普通…

barrier(wmb,mb,rmb)和cache coherence

http://www.linuxforum.net/forum/gshowflat.php?Cat&BoardlinuxK&Number428239&page5&viewcollapsed&sb5&oall&fpart 注: 这里的barrier 指的是wmb, rmb, mb. 一 直找不到合适的资料说明barrier和 Cache coherence 之间的关系. 在<<ldd>…

mw与dbm换算

1. 基本概念 dbm&#xff1a;意即分贝毫X&#xff0c;可以表示分贝毫伏&#xff0c;或者分贝毫瓦。他是一个表示功率绝对值的单位。 功率/电平&#xff08;dBm&#xff09;&#xff1a;放大器的输出能力&#xff0c;一般单位为w、mw、dBm。dBm是取1mw作基准值&#xff0c;以分贝…

[architecture]-DBG、DMB、DSB 和 ISB指令介绍

快速链接: . &#x1f449;&#x1f449;&#x1f449; 个人博客笔记导读目录(全部) &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】: 【精选】ARMv8/ARMv9架构入门到精通-[目录] &#x1f448;&#x1f448;&#x1f448; 1、DBG、DMB、DSB 和 IS…