`
doujiu
  • 浏览: 90270 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

求组合数的几种方法

阅读更多
C++求组合数 - johnchangbo的专栏 - CSDN博客

【问题】 组合问题
问题描述:找出从自然数1、2、... 、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:

1,2,3
1,2,4
1,3,4
2,3,4
1,2,5
1,3,5
2,3,5
1,4,5
2,4,5
3,4,5

用程序实现有几种方法:
1)穷举法

程序如下
【程序】
#include<stdio><br>const int n=5,r=3;<br>int i,j,k,counts=0;<br><br>int main()<br>{<br> for(i=1;i&lt;=r ;i++)<br> for(j=i+1;j&lt;=r+1;j++)<br> for( k=j+1;k&lt;=r+2;k++){<br> counts++;<br> printf("%4d%4d%4d\n",i,j,k);<br> }<br>printf("%d",counts);<br>return 0;<br>}<br>但是这个程序都有一个问题,当r变化时,循环重数改变,这就影响了这一问题的解,即没有一般性。<br><br><br>2)递归法<br>分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。<br>设函数为void comb(int m,int k)为找出从自然数1、2、... 、m中任取k个数的所有组<br>合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这<br>就将求m个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引<br>入工作数组a[ ]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放<br>在a[k]中,当一个组合求出后,才将a[ ]中的一个组合输出。第一个数可以是m、m-1、<br>...、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组<br>合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。细节<br>见以下程序中的函数comb。<br>【程序】<br>#include <time><br>#include <iostream><br><br>using namespace std;<br><br># define MAXN 100<br>int a[MAXN];<br>int counts=0;<br><br>void printtime(void) //打印当前时间的函数<br>{<br> char tmpbuf[128];<br> time_t ltime;<br> struct tm *today;<br><br> time(&amp;ltime);<br> today = localtime(&amp;ltime );<br> strftime(tmpbuf,128,"%Y-%m-%d %H:%M:%S",today);<br> cout&lt;<tmpbuf>&lt;<endl><br>}<br><br><br>void comb(int m,int k)<br>{ int i,j;<br> for (i=m;i&gt;=k;i--)<br> { a[k]=i;<br> if (k&gt;1)<br> comb(i-1,k-1);<br> else<br> { <br> counts++;<br> /*<br> for (j=a[0];j&gt;0;j--)<br> printf("%4d",a[j]);<br> printf("\n");<br> */<br> }<br> }<br>}<br><br>int main()<br>{ <br><br> int m,r;<br> cout&lt;&lt;"m"&lt;<endl><br> cin&gt;&gt;m;<br> cout&lt;&lt;"r"&lt;<endl><br> cin&gt;&gt;r;<br> counts=0;<br> a[0]=r;<br> printtime();<br> comb(m,r);<br> cout&lt;<counts>&lt;<endl><br> printtime();<br> return 0;<br>}<br><br><br>这是我在网上找到的程序,稍微修改了一下。程序写的很简洁,也具有通用性,解决了问题。<br><br>3)回溯法<br><br>采用回溯法找问题的解,将找到的组合以从小到大顺序存于a[0],a[1],…,a[r-1]<br>中,组合的元素满足以下性质:<br><br>(1) a[i+1]&gt;a[i],后一个数字比前一个大;<br>(2) a[i]-i&lt;=n-r+1。<br>按回溯法的思想,找解过程可以叙述如下:<br> 首先放弃组合数个数为r的条件,候选组合从只有一个数字1开始。因该候选<br>解满足除问题规模之外的全部条件,扩大其规模,并使其满足上述条件(1),候选组合<br>改为1,2。继续这一过程,得到候选组合1,2,3。该候选解满足包括问题规模在内的全<br>部条件,因而是一个解。在该解的基础上,选下一个候选解,因a[2]上的3调整为4,以<br>及以后调整为5都满足问题的全部要求,得到解1,2,4和1,2,5。由于对5不能再作调<br>整,就要从a[2]回溯到a[1],这时,a[1]=2,可以调整为3,并向前试探,得到解1,3,<br>4。重复上述向前试探和向后回溯,直至要从a[0]再回溯时,说明已经找完问题的全部<br>解。<br><br>在网上我始终没有找到可以正常执行的完整程序,所以我只好花了一天的时间来自己来写这个程序,并且改变输出从0开始而不是从1开始,这样做的目的是 为了扩展程序的用途,适应c/c++语言的需要,这样输出就可以当作要选择的组合数组的地址序列,可以对长度为n任意类型数组找出r个组合。我对它进行了 优化,如果你认为还有可以优化的地方,请不惜赐教,。^_^<br><br>#include <time><br>#include <iostream><br>#include <iomanip><br>using namespace std;<br><br># define MAXN 100<br>int a[MAXN]; //定位数组,用于指示选取元素集合数组的位置,选取元素集合数组0 起始<br>void comb(int m,int r)<br>{ <br> int cur;//指示定位数组中哪个成员正在移进<br><br> unsigned int count=0;<br><br> //初始化定位数组,0 起始的位置 ,开始的选择必是位置 0,1,2<br> for(int i=0;i<r><br> a[i]=i;<br><br> cur=r-1;//当前是最后一个成员要移进<br><br> do{<br> if (a[cur]-cur&lt;=m-r ){ <br><br> count++;<br> /*<br> for (int j=0;j<r><br> cout&lt;<setw>&lt;<a><br> cout&lt;<endl><br> */<br> a[cur]++;<br><br> continue;<br> }<br> else{<br> if (cur==0){<br> cout&lt;<count>&lt;<endl><br> break;<br> }<br><br> a[--cur]++;<br> for(int i=1;i<r-cur><br> a[cur+i]=a[cur]+i;<br> }<br><br> if(a[cur]-cur<m-r><br> cur=r-1; <br> }<br> }while (1);<br>}<br><br>void printtime(void) //打印当前时间的函数<br>{<br> char tmpbuf[128];<br> time_t ltime;<br> struct tm *today;<br><br> time(&amp;ltime);<br> today = localtime(&amp;ltime );<br> strftime(tmpbuf,128,"%Y-%m-%d %H:%M:%S",today);<br> cout&lt;<tmpbuf>&lt;<endl><br>}<br><br>int main (int argc, char *argv[])<br>{<br><br> int m,r;<br> cout&lt;&lt;"m"&lt;<endl><br> cin&gt;&gt;m;<br> cout&lt;&lt;"r"&lt;<endl><br> cin&gt;&gt;r;<br> printtime();<br> comb(m,r); <br> printtime();<br> return(0);<br>}<br><br>同上面的递归的程序进行比较,同样用g++ o2优化。当n=40,r=11,屏蔽掉输出,得到的结果都是2311801440项,递归程序用了23至24秒,回溯用了19至20秒。<br><br>4)利用数组<br><br> 定义:从n个数中取出m个数的组合。<br> 实现机理:先创建一个字符串数组,其下标表示 1 到 n 个数,数组元素的值为1表示其下标代表的数被选中,为0则没选中。 <br> 然后初始化,将数组前 m 个元素置 1,表示第一个组合为前 m 个数。 <br> 然后从左到右扫描数组元素值的 10 组合,找到第一个 "10" 后交换 1 和 0 的位置,变为 01,而后将该10组合前的1和0重新组合(1放在前边,其个数为10组合前1的个数,0放在后边,其个数为10前0的个数,而后接10的倒转组合 01)。当m 个 1 全部移动到最右端时,就得到了最后一个组合。 <br> 例如求 5 中选 3 的组合: <br> 1 1 1 0 0 //1,2,3 <br> 1 1 0 1 0 //1,2,4 <br> 1 0 1 1 0 //1,3,4 <br> 0 1 1 1 0 //2,3,4 <br> 1 1 0 0 1 //1,2,5 <br> 1 0 1 0 1 //1,3,5 <br> 0 1 1 0 1 //2,3,5 <br> 1 0 0 1 1 //1,4,5 <br> 0 1 0 1 1 //2,4,5 <br> 0 0 1 1 1 //3,4,5 <br><br></endl></endl></endl></tmpbuf></m-r></r-cur></endl></count></endl></a></setw></r></r></iomanip></iostream></time></endl></counts></endl></endl></endl></tmpbuf></iostream></time></stdio>


分享到:
评论

相关推荐

    6位数,共有几种排列组合的算法java实现

    6位数,共有几种排列组合的算法,java实现

    C++中求组合数的各种方法总结详解

    用程序实现有几种方法: 1)穷举法 程序如下【程序】#include&lt;stdio&gt;const int n=5,r=3;int i,j,k,counts=0; int main(){ for(i=1;i&lt;=r ;i++) for(j=i+1;j&lt;=r+1;j++) for( k=j+1;k&lt;=r+2;k

    易语言源码易语言取元素组合数源码.rar

    5. **动态规划**:另一种常见方法是使用动态规划,创建一个数组来存储已计算过的组合数,避免重复计算。 6. **错误处理**:源码可能包含了错误处理机制,以确保输入的n和k是合法的非负整数,且k小于等于n。 7. **...

    组合数学 组合数 整数划分 递推 方程 多项式定理

    - **二项式定理**:表达式 \((x + y)^n\) 的展开形式,其中 \(n\) 是非负整数,该定理提供了计算其各项系数的方法,这些系数即为组合数。 - **组合数的整数性质**:包括组合数的定义、性质及其与整数的关系。例如,\...

    高中数学排列组合难题十一种方法.doc

    ]种方法,C(n,k)称为组合数。 解决排列组合问题的一些常见策略包括: - **特殊元素和特殊位置优先策略**:当题目中存在特殊要求的元素或位置时,应优先处理,避免不符合条件的元素占用这些位置。 - **相邻元素捆绑...

    排列组合21种方法.docx

    可先把这几个元素与其他元素一起进展排列,然后用总排列数除以这几个元素之间的全排列数,那么共有不同排法种数是:(空位法)设想有7把椅子让除甲乙丙以外的四人就坐共有种方法。 排列组合问题的解决需要认真审题...

    基及维数的几种求法.doc

    以下是几种求解基和维数的方法: 方法一:根据定义求解 在给定的线性空间V中,如果有一组向量,它们线性无关(即没有一个向量可以表示为其他向量的线性组合)并且能生成V中的所有向量,那么这组向量就是V的基,其...

    易语言数字组合例程

    在这个“易语言数字组合例程”中,我们可以深入探讨几个关键概念,这些概念对于理解和创建数字处理程序至关重要。 首先,我们来看“数字组合”。在编程中,数字组合通常指的是对一组数字进行各种排列和组合的操作。...

    组合数学习题答案

    在解题过程中,可能会遇到以下几种类型的题目: - **直接计算**:给定n和k,直接求C(n, k)。 - **递推关系应用**:通过递推关系解决复杂问题,比如计算大型组合数或找出特定模式。 - **组合恒等式证明**:如...

    38译码器实现的几种方法

    根据给定的信息,本文将详细解析“38译码器实现的几种方法”。38译码器是一种常见的数字电路组件,用于将输入信号转换为多个输出信号中的一个,本篇文章将会探讨三种不同的实现方法。 ### 一、基础知识 在深入讨论...

    易语言组合6位不重复数字

    这可能是为了实现某种特定的验证或者筛选机制,比如检查生成的数字是否满足某个条件(比如能被某个数整除)或者计算特定组合的总和。 易语言的源码文件中,可能会包含以下几个关键部分: 1. 数字集合定义:初始化0-...

    三菱PLC控制变频器的几种方法.pdf

    三菱PLC控制变频器的几种方法 PLC(Programmable Logic Controller,程序逻辑控制器)是一种常用的工业自动化控制系统。在工业自动化控制系统中,PLC和变频器的组合应用非常常见。变频器是一种通过改变电机的频率来...

    8位数字和字母密码组合大全

    8位数字和字母组合大全,里含有四位六位八位的数组和密码的组合,密码批量测试神器,批量测试网站登陆密码测试,压力承受能力测试,大神必备神器!

    基与维数的几种求法.pdf

    方法二则是在已知空间维数的情况下,直接选取n个向量,只要它们线性无关,就能构成空间的基。例如,在实系数多项式空间中,选取n个线性无关的多项式作为基。 方法三利用了线性空间的同构性质。如果两个有限维线性...

    2021_2022学年新教材高中数学第三章排列组合与二项式定理3.1.3.2组合数的应用课件新人教B版选择性必修第二册20210

    例如,将3张游园票分给10人中的3人,共有120种分法,这可以通过计算组合数C(10, 3)得到。又如,甲、乙、丙三位同学从4门课程中选修,甲选2门,乙和丙各选3门,不同的选修方案有96种,这是通过分步计数原理计算得出的...

    测试用例的几种设计方法

    本文将详细介绍几种常见的测试用例设计方法,包括等价类划分、边界值分析、错误推测法、因果图法、判定表驱动法、正交试验法、功能图法以及场景法,并探讨每种方法的应用场景、设计步骤及其优缺点。 #### 一、等价...

    用几种排序方法对随机产生的数据排序

    以下是关于这几种排序方法的详细介绍: 1. **堆排序(Heap Sort)** 堆排序是一种基于比较的原地排序算法,它利用了完全二叉树的特性。首先构建大顶堆或小顶堆,然后将堆顶元素与末尾元素交换,再调整堆,重复此...

    基及维数的几种求法.pdf

    首先,求线性空间基和维数的一种常见方法是直接应用定义。例如,在线性方程组AX=0的情况下,如果A是一个m×n矩阵,其秩为r,那么非零解的空间V(齐次线性方程组的解空间)的维数是n-r,且该空间的基由AX=0的任意一个...

    windows8开启屏幕键盘的几种方法.docx

    下面将详细介绍这几种开启屏幕键盘的方法。 方法一:通过“所有程序” 在Windows 8的“开始屏幕”上,用户可以右键点击(触屏用户可从屏幕底部边缘向上滑动)空白区域,选择“所有程序”。在展开的列表中,你可以...

    单片机RAM测试故障方法有几种?

    由于存储器的正常工作对于整个系统的稳定性至关重要,因此这里详细介绍了几种测试RAM的方法,并提出了基于种子和逐位倒转的改进方法。 一、RAM测试方法回顾 1. 方法1:一种基础的RAM测试方法分为两个步骤,首先向...

Global site tag (gtag.js) - Google Analytics