全排列函数、组合函数

1

1、求一个全排列函数:如p([1,2,3])输出:

         [123],[132],[213],[231],[321],[312].

2、求一个组合函数如p([1,2,3])输出:

         [1],[2],[3],[1,2],[2,3],[1,3],[1,2,3]

这两问可以用伪代码。

void swap(int *a, int *b) //交换函数
{int tmp;tmp =*a;*a=*b;*b=tmp;
}

以为很简单,手写全排序第一遍就错了。

func1( a[], n, m)if(m>n-1) return; endprint(a);for(i=m+1 to n-1)swap(a[m], a[i]);func1(a, n, m+1);swap(a[m], a[i]);end
end

调用:func1(a, n, 0)

发现以a[0]开头的只有一组,其他可以满足,因为i=m+1。func1的理解就是输出a,并将a[m]分别与a[m+1]..a[n]交换后的后n-m-1个元素的排列组合。当调用了func1(a, n, 0)时,以a[0]开头的只有一组。改为for(i=m to n)可以吗?

No!这时候,却多出很多。由于m>n-1才是终止条件。即要进入n-1层,每层有n-m+1个分支,于而每进入一层就会print(a),这样就会输出的次数不等于n!了(一般情况下)

纠结了n久之后,再改,应该当m=n-1时才输出。Bingo!

 1 func1( a[], n, m)
 2   if(m==n-1) 
 3       print(a);
 4        return;
 5     end
 6    for(i=m+1 to n-1)
 7     swap(a[m], a[i]);
 8     func1(a, n, m+1);
 9     swap(a[m], a[i]);
10   end
11 end
12 
13 调用:func1(a, n, 0)

可是结果却和题目的不完全一致。因为是输出的位置的先后问题。换成尾递归即可。

 1 void func1(int a[], int m, int n)
 2 {        
 3     for (int j=m; j<n; j++)
 4     {
 5         swap(&a[m], &a[j]);
 6         func1(a, m+1, n);
 7         swap(&a[m], &a[j]);
 8     }
 9     if (m==n-1)
10     {
11         for (int i=0; i<n; i++)
12         {
13             cout<<a[i]<<" ";
14         }
15         cout<<endl;
16         return;
17     }
18 }

简单的题目也不见得简单啊~(其实个人水平问题。。。)

2、只考虑有最大有32个数的情况:

 1 void func2(int a[], int n)
 2 {
 3     int cnt=1;
 4 
 5     if (n>32 || n<=0)
 6     {
 7         return;
 8     }
 9     cnt = cnt<<n;
10 
11     unsigned int tmp;
12     for (unsigned int i=0; i<=cnt; i++)
13     {
14         tmp = 1;
15         for (int j=0; j<n; j++)
16         {
17             if (i & tmp)
18             {
19                 cout<<a[j]<< " ";
20             }
21             tmp = tmp<<1;
22         }
23         cout<<endl;
24     }
25 }

总结:

常用的排列组合算法,包括全排列算法,全组合算法,m个数选n个组合算法等。

2. 排列算法

常见的排列算法有:

(A)字典序法

(B)递增进位制数法

(C)递减进位制数法

(D)邻位对换法

(E)递归法

介绍常用的两种

(1) 字典序法

对给定的字符集中的字符规定了一个先后关系,在此基础上按照顺序依次产生每个排列。

[例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321。

生成给定全排列的下一个排列 所谓一个的下一个就是这一个与下一个之间没有字典顺序中相邻的字符串。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

算法思想

设P是[1,n]的一个全排列。

P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn , j=max{i|Pi<Pi+1}, k=max{i|Pi>Pj} ,对换Pj,Pk,将Pj+1…Pk-1PjPk+1…Pn翻转, P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个

例子:839647521的下一个排列.

从最右开始,找到第一个比右边小的数字4(因为4<7,而7>5>2>1),再从最右开始,找到4右边比4大的数字5(因为4>2>1而4<5),交换4、5,此时5右边为7421,倒置为1247,即得下一个排列:839651247.用此方法写出全排列的非递归算法如下

该方法支持数据重复,且在C++ STL中被采用。

(2) 递归法

设一组数p = {r1, r2, r3, … ,rn}, 全排列为perm(p),pn = p – {rn}。则perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), … , rnperm(pn)。当n = 1时perm(p} = r1。

如:求{1, 2, 3, 4, 5}的全排列

1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。

由于一个数的全排列就是其本身,从而得到以上结果。

2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。

即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.

以上的算法1的即是使用这个方法。

3组合算法

3.1 全组合

在此介绍二进制转化法,即,将每个组合与一个二进制数对应起来,枚举二进制的同时,枚举每个组合。如字符串:abcde

00000 <– –> null

00001<– –> e

00010 <– –> d

… …

11111 <– –> abcde

以上的算法2的即是使用这个方法。

3.2 从n中选m个数

(1) 递归

a. 首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,直到从n-(m-1)个数中选取1个数为止。

b. 从n个数中选取编号次小的一个数,继续执行1步,直到当前可选编号最大的数为m。

下面是递归方法的实现:

 1 /// 求从数组a[1..n]中任选m个元素的所有组合。
 2 
 3 /// a[1..n]表示候选集,n为候选集大小,n>=m>0。
 4 
 5 /// b[1..M]用来存储当前组合中的元素(这里存储的是元素下标),
 6 
 7 /// 常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。
 8 
 9 void combine( int a[], int n, int m,  int b[], const int M )
10 
11 {
12 
13   for(int i=n; i>=m; i--)   // 注意这里的循环范围
14 
15   {
16 
17     b[m-1] = i - 1;
18 
19     if (m > 1)
20 
21       combine(a,i-1,m-1,b,M);
22 
23     else                     // m == 1, 输出一个组合
24 
25     {
26 
27       for(int j=M-1; j>=0; j--)
28 
29       cout << a[b[j]] << " ";
30 
31       cout << endl;
32 
33     }
34 
35   }
36 
37 }
 1 void  func19(int m, int n) //非递归
 2 {
 3     int *arr;
 4     int i, pos;
 5 
 6     if (m<n)
 7     {
 8         return ;
 9     }
10 
11     arr = new int[n];
12 
13     for (i=0; i<n; ++i)
14     {
15         arr[i] = i+1;
16     }
17 
18     for (i=0; i<n; ++i)
19     {
20         cout<<arr[i]<<" ";
21     }
22     cout<<endl;
23 
24     pos = n-1;
25     while(1)
26     {
27         if (arr[n-1] == m )
28         {
29             pos--;
30         }
31         else
32         {
33             pos = n-1;
34         }
35 
36         arr[pos] ++;
37 
38         for (i=pos+1; i<n; i++)
39         {
40             arr[i] = arr[i-1]+1;
41         }
42 
43         for (i=0; i<n; ++i)
44         {
45             cout<<arr[i]<<" ";
46         }
47         cout<<endl;
48 
49         if (arr[0] == m-n+1)
50         {
51             break;
52         }
53     }
54 }

 

(2) 01转换法

本程序的思路是开一个数组,其下标表示1到n个数,数组元素的值为1表示其代表的数被选中,为0则没选中。

首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。

然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。

当第一个“1”移动到数组的n-m的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。

例如求5中选3的组合:

1 1 1 0 0 //1,2,31 1 0 1 0 //1,2,41 0 1 1 0 //1,3,40 1 1 1 0 //2,3,41 1 0 0 1 //1,2,51 0 1 0 1 //1,3,50 1 1 0 1 //2,3,51 0 0 1 1 //1,4,50 1 0 1 1 //2,4,50 0 1 1 1 //3,4,5

4. 参考资料

(1) http://www.cnblogs.com/nokiaguy/archive/2008/05/11/1191914.html

(2) http://comic.sjtu.edu.cn/thucs/GD_jsj_025y/text/chapter01/sec05/default.htm

(3) http://blog.csdn.net/sharpdew/archive/2006/05/25/755074.aspx

(4) http://xiaomage.blog.51cto.com/293990/74094

以上总结源于:董的博客

转载于:https://www.cnblogs.com/legendmaner/archive/2013/03/19/2969115.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中的XSL转换:一种简单的方法

XSL转换 &#xff08;XSLT&#xff09;是将一个XML文档转换为另一个XML文档的强大机制。 但是&#xff0c;在Java中&#xff0c;XML操作相当冗长和复杂。 即使是简单的XSL转换&#xff0c;也必须编写几十行代码—如果需要适当的异常处理和日志记录&#xff0c;甚至可能还要写更…

修改html页面的title,可以自定义

方式一&#xff1a; document.getElementsByTagName(“title”)[0].innerText ‘需要设置的值’; document.title方式 经过测试&#xff0c;还可通过document.title 设置title的值。方式二 console.log(document.title); # 可以获取title的值。 document.title ‘需要设置的值…

.Net环境下的缓存技术介绍 (转)

摘要: 介绍缓存的基本概念和常用的缓存技术&#xff0c;给出了各种技术的实现机制的简单介绍和适用范围说明&#xff0c;以及设计缓存方案应该考虑的问题&#xff08;共17页&#xff09; 1 概念 1.1 缓存能解决的问题 性能——将相应数据存储起来以避免数据的重复创…

antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function

问题描述&#xff1a; antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function 原因分析&#xff1a; 在From中渲染默认值&#xff0c;一般数据请求拿到返回值存在异步&#xff0c;会晚于渲染&#xff0c;因此日期转换不能放在DatePi…

集成CDI和WebSockets

考虑尝试一个简单的Java EE 7原型应用程序&#xff0c;该应用程序涉及JAX-RS&#xff08;REST&#xff09;&#xff0c;WebSockets和CDI。 注意 &#xff1a;不想让它成为破坏者-但这篇文章主要讨论了我在尝试使用Web套接字和使用CDI作为“胶水”的REST&#xff08;在Java EE应…

【记】jQuery中的选择器:visible对visibility:hidden的处理

首先先介绍visibility这个CSS属性。 visibility用于表示该元素是否显示&#xff0c;其取值有&#xff1a; visible&#xff1a;设置对象可视。  hidden&#xff1a;设置对象隐藏。  collapse&#xff1a;主要用来隐藏表格的行或列&#xff0c;隐藏的行或列能够被其他内容是…

React - antd4 中在form中为Switch赋值无效

项目场景&#xff1a; React - antd4; From表单 问题描述&#xff1a; Switch赋初始值无效 # 解决方案&#xff1a; let newCheck {};if (propsForm.type switch) {newCheck {...newCheck,valuePropName: "checked"}}<Form.Itemkey{propsForm.id}name{propsF…

在实践中重试HTTP标头

Retry-After是鲜为人知的HTTP响应标头。 让我引用RFC 2616&#xff08;HTTP 1.1规范&#xff09;的相关部分&#xff1a; 14.37重试后 Retry-After响应标头字段可与503 &#xff08; 服务不可用 &#xff09;响应一起使用&#xff0c;以指示请求该客户端的服务预计无法使用多…

Java中连接字符串的最佳方法

最近有人问我这个问题–在Java中使用运算符连接字符串是否对性能不利&#xff1f; 这让我开始思考Java中连接字符串的不同方法&#xff0c;以及它们如何相互对抗。 这些是我要研究的方法&#xff1a; 使用运算符 使用StringBuilder 使用StringBuffer 使用String.concat() …

时间转换以及公式

moment(item.updatedAt).fromNow() ;//距离今天多久了moment&#xff08;&#xff09;获得今天、明天和昨天的日期 let today moment(new Date());let tomorrow moment(new Date()).add(1,days);let yesterday moment(new Date()).add(-1, days);后续遇到继续补充

只不过是R.java文件的特性-----出错信息:R.java was modified manually! Reverting to generated version!...

出错信息:R.java was modified manually! Reverting to generated version! 出错原因&#xff1a;今天在res下建立了一个drawable的文件夹存放图片资源----图片名为1.jpg 后来R.java文件就报错了&#xff1a;R.java was modified manually! Reverting to generated version! 看…

十大最常见的Java性能问题

Java性能是所有Java应用程序开发人员都关心的问题&#xff0c;因为快速使应用程序与使其正常运行同等重要。 史蒂文海恩斯&#xff08;Steven Haines&#xff09;使用他在Java性能问题上的个人经验得出的结论是&#xff0c; 大多数问题都有共同的根本原因 。 因此&#xff0c;作…

antd form 初始化时间

initialValue: moment(record.showTime, YYYY-MM-DD HH:mm:ss)

uni-app 组件中的canvas转化为图片报错:errMsg:“canvasToTempFilePath:fail canvas is empty”

项目场景&#xff1a; uni-app,开发微信小程序 使用&#xff1a; wx.canvasToTempFilePath({canvasId: line,success: function(res) {console.log(canvasToImg, res);this.radarImg res.tempFilePath;},fail: function(res) {console.log(res);}});问题描述&#xff1a; ec…

正确获取Java事件通知

实现观察者模式以提供Java事件通知似乎是一件容易的事。 但是&#xff0c;容易陷入一些陷阱。 这是我在各种场合不慎造成的常见错误的解释…… Java事件通知 让我们从一个简单的bean StateHolder开始&#xff0c;它封装了带有适当访问器的私有int字段state &#xff1a; publ…

使用fn函数控制页面显示内容

在使用&#xff25;&#xff2c;的时候&#xff0c;不可避免的遇到&#xff0c;截取字符串&#xff0c;判断字符串长度等情况。这里给出简单的通过&#xff46;&#xff4e;函数操作字符串的deamon。 1、页面引入标签 <% taglib prefix"c" uri"http://java.s…

uni-app微信获取手机号,第一次解密总是失败

项目场景&#xff1a; uni-app; 获取code&#xff0c;后台解密手机号 问题描述&#xff1a; 每次第一次登陆&#xff0c;后台都会解密失败 原因分析&#xff1a; code获取错误&#xff1b;导致后台的解密key与code不对应 解决方案&#xff1a; 小程序获取手机号之前&#xf…

Java中不一致的操作会扩大规则

总览 当您在Java中执行一元或二进制操作时&#xff0c;标准行为是使用最宽的操作数&#xff08;或对byte &#xff0c; short和char使用更宽的操作数&#xff09;。 这很容易理解&#xff0c;但是如果考虑最佳类型可能会造成混淆。 乘法 当执行乘法运算时&#xff0c;您得到的…

圣诞节到了,用js给喜欢的人写一颗圣诞树吧

文章目录 1、效果预览2、代码2.1、定义数组写下祝福语2.2、模拟雪花落下的效果2.3、设置背景粒子2.4、操作动画效果2.5、定义闪烁效果2.6、定义粒子对象2.7、粒子对象播放2.8、绘制星星2.9、绘制圣诞树2.10、绘制星星背景动画2.11、定义初始化函数并调用 3、结尾 1、效果预览 圣…