全排列生成数字或者字母
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
p[i] = i + 1; showPermutation(p, n); while(nextPermutation(p, n)) {
showPermutation(p, n); } //delete[] p; return 0; } 主要的任务在 nextPermuation()中完成。这其中的思想是,提供一个已经有的全排列 P,求 出 P 的“下一个”全排列。这里“下一个”的意思是说,在 n 个数的 n!个全排列在数字上
再进行一次方向最大排列的问题。倒过来再倒过去从而达到了遍历。
下午偶尔翻到以前的写到的一些代码,不觉心血来潮,看了一下其中关于全排列算法的 一个实现.联想到高中时数学课上学到关于组合和排列的一些公式,于是在纸上涂鸦着一些 计算方法,突然灵感潮来,想借助小白鼠的那个思路也能将全排列解决出来~
下班回到家便赶紧吃饭玩一局 sc 后,开始实现,终于赶在睡觉之前可以写这篇 blog,介绍我的 这种还算奇妙的全排列算法: 1. 算法思路:
大概就是这样的 -------------------------------#include"stdio.h" int a[10],n,count=0; void perm(int k) { int p,t,j; if( k==n ) {count++; for(p=1;p<=k;p++) printf("%1d",a[p]);/*"%1d"中的数字是 1,而不是字母 l*/ printf(" "); if( count%3==0 ) printf("\n"); return;} for(j=k;j<=n;j++) {t=a[k];a[k]=a[j];a[j]=t; perm(k+1); t=a[k]; a[k]=a[j];a[j]=t;} }
(AK,A1) , (A1,AK). 如果我将 1 周围的两个位置用 0 和 1 来表示,如果放在左边,那么左边用 1 表示,反
之用 0 表示.反正只能放一个地方,那么这两个位置合起来不是 10,就是 01,化成十进制便是 2 或者 1.
的。实际上,原来的 pi…pn,已经是这一部分最大的一个排列了。但我们现在换了最高位 pi-1,
因此要让后面的数字变的最小。方法很简单,根据上面的推理,我们只须将 pi~pn 的数列倒置
即可(最大的排列倒置就变成了最小的排列)。
这样,我们计算出了准确的下一个排列。
比如有(1,2,3,4)这样一组数
1.先分成(1)和(2,3,4),然后对(2,3,4)全排列 2.把(1)分别和(2,3,4)中的数对调 3.比如一次调换(2),(1,3,4),然后对(2,3,4)全排列 4.调换的算完了,恢复,变成(1),(2,3,4),再调换下一个(3),(1,2,4)
第一次看到这个算法是在软件设计师的辅导书上。代码如下,在 VC++ 7.0 下调试通过。
// Permutation.cpp : 定义控制台应用程序的入口点。 // //N 个数全排列的非递归算法 #include “stdafx.h” void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; } /* 根据当前的排列 p,计算下一个排列。 原则是从 1234–>4321,若 p 已经是最后一个排列,传回 false,否则传回 true。 p 是一个 n 维向量。 */ bool nextPermutation(int *p, int n) { int last = n – 1; int i, j, k; //从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。 i = last; while (i > 0 && p[i] < p[i - 1]) i–; //若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回 false。 if (i == 0) return false; //从后查到 i,查找大于 p[i - 1]的最小的数,记入 k k = i; for (j = last; j >= i; j–) if (p[j] > p[i - 1] && p[j] < p[k]) k = j; //交换 p[k]和 p[i - 1] swap(p[k], p[i - 1]); //倒置 p[last]到 p[i] for (j = last, k = i; j > k; j–, k++) swap(p[j], p[k]);
就查找从 pi 到 pn 中大于 pi-1 的最小的数字。然后将找到的数字与 pi-1 交换。
3.
还没有结束。交换后,pi-1pi…pn 并不是准确的后一个排列。因
为根据第一步的查找,我们有 pi > pi+1 > … > pn,否则查找在 i~n 就会停
下来了。这样的一个排列显然不是最小的。实际上,原来的 pi…pn,已经是这
return true; } //显示一个排列 void showPermutation(int *p, int n) { for (int i = 0; i < n; i++) cout << p[i]; } int _tmain(int argc, _TCHAR *argv[]) { int n; int *p; cin >> n; p = new an>int[n]; for (int i = 0; i < n; i++) p[i] = i + 1; showPermutation(p, n); cout << endl; while(nextPermutation(p, n)) { showPermutation(p, n); cout << endl; } delete[] p; system(“pause”); return 0; }
– 1(1 < i < n – 1),到 p1’p2’…pn’,pi < pi – 1(1 < i < n – 1)。因此:
1.
从低位到高位(从后向前),找出“不符合趋势”的数字。即找到一个 pi,使 p百度文库 – 1
< pi。若找不到这样的 pi,说明我们已经找到最后一个全排列,可以返回了。
2.
把 pi - 1 替换成从 pn 到 pi 几个数字中“刚刚好大于 pi-1”的数字。这里的目的
< n – 1)。因此:
1.
从低位到高位(从后向前),找出“不符合趋势”的数字。即找到
一个 pi,使 pi – 1 < pi。若找不到这样的 pi,说明我们已经找到最后一个全
排列,可以返回了。
2.
把 pi - 1 替换成从 pn 到 pi 几个数字中“刚刚好大于 pi-1”的
数字。这里的目的是找出准确的 P 的下一个排列 Q。所谓“刚刚好大于”,我们
if(p[j]>p[i-1]&&p[j]<p[k]) k =j;
//交换 p[k]和 p[i - 1] swap(p[k],p[i-1]); //倒置 p[last]到 p[i] /* for (j =last,k =i; j>k; j--,k++)
swap(p[j],p[k]); */ return true; } //显示一个排列 void showPermutation(int *p, int n)
反复运行 nextPermuation()找出比 P 的“下一个”全排列,直到 Pn! = p1’p2’…pn’,pi <
pi – 1
(1 < i < n – 1)为止。所找到的所有的排列就是 n 的全排列。
下面要考虑的问题,是如何从一个已知的排列 P = p1p2…pn,找到它的下一个排列
Q = q1q2…qn。我们要让排列从小到大生成,简单说,要让排列的趋势从 p1p2…pn,pi > pi
{ for(int i=0; i<n; i++) printf("%d ",p[i]); printf("\n");
} int main(int argc, char *argv[]) {
int n; int *p; scanf("%d",&n); p = new int[n]; for (int i = 0; i < n; i++)
(1 < i < n – 1)为止。所找到的所有的排列就是 n 的全排列。
下面要考虑的问题,是如何从一个已知的排列 P = p1p2…pn,找到它的下一个
排列
Q = q1q2…qn。我们要让排列从小到大生成,简单说,要让排列的趋势从 p1p2…
pn,pi > pi – 1(1 < i < n – 1),到 p1’p2’…pn’,pi < pi – 1(1 < i
试想 N 个自然数 A1,A2,...,AN 的全排列是 N!个,那么,对于每种排列,我将其打印出 来,遍历一遍至少是 O(N!)的复杂度,那么能不能在就在这个复杂度内用非递归解决这个问题 呢?我的想法就是从这点开始成形.同时借助了建模的思想分析的.
无论如何,我首先选取一个自然数 A1 出来,放到存放地址的中间, 那么在 A1 的周 围存在两个位置,一个是 A1 的左边,一个是 A1 的右边,那么,我抽出另一自然数出来(假设是 AK),便有两种存放的可能性:
i=last; while(i>0&&p[i]<p[i-1])
i--; //若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回 false。 if(i==0)
return false; //从后查到 i,查找大于 p[i - 1]的最小的数,记入 k k=i; for(j=last; j>=i; j--)
这个算法挺难理解的。这里试图说明一下算法的意思。
主要的任务在 nextPermuation()中完成。这其中的思想是,提供一个已经有的全排列 P,求出
P 的“下一个”全排列。这里“下一个”的意思是说,在 n 个数的 n!个全排列在数字上从小到
大的一个序列中,p 的下一个数字上比它大的一个排列。
那么由此,提供一个初始的 n 个数的全排列 P1 = p1p2…pn,有 pi > pi – 1(1 < i < n – 1),
是找出准确的 P 的下一个排列 Q。所谓“刚刚好大于”,我们就查找从 pi 到 pn 中大于 pi-1 的
最小的数字。然后将找到的数字与 pi-1 交换。
3.
还没有结束。交换后,pi-1pi…pn 并不是准确的后一个排列。因为根据第一步的查找,
我们有 pi > pi+1 > … > pn,否则查找在 i~n 就会停下来了。这样的一个排列显然不是最小
main() {int i; printf("\nInput n:"); scanf("%d",&n);
for(i=1;i<=n;i++) a[i]=i; perm(1); }
#include <stdio.h> void swap(int &a, int &b) {
int temp; temp = a; a = b; b = temp; } /* 根据当前的排列 p,计算下一个排列。 原则是从 1234–>4321,若 p 已经是最后一个排列,传回 false,否则传回 true。 p 是一个 n 维向量。 */ bool nextPermutation(int *p, int n) { int last=n-1; int i,j,k; //从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。
从小到大的一个序列中,p 的下一个数字上比它大的一个排列。
那么由此,提供一个初始的 n 个数的全排列 P1 = p1p2…pn,有 pi > pi – 1(1
< i < n – 1),反复运行 nextPermuation()找出比 P 的“下一个”全排列,直
到 Pn! = p1’p2’…pn’,pi < pi – 1
一部分最大的一个排列了。但我们现在换了最高位 pi-1,因此要让后面的数字
变的最小。方法很简单,根据上面的推理,我们只须将 pi~pn 的数列倒置即可
(最大的排列倒置就变成了最小的排列)。
这样,我们计算出了准确的下一个排列。
ps:这个主要的思想就是,当每次我们交换完之后,交换之后的后面的部分是一
个符合趋势的排列,为了得到所有的排列,我们将其反转,那就相当于对其部分
showPermutation(p, n); } //delete[] p; return 0; } 主要的任务在 nextPermuation()中完成。这其中的思想是,提供一个已经有的全排列 P,求 出 P 的“下一个”全排列。这里“下一个”的意思是说,在 n 个数的 n!个全排列在数字上
再进行一次方向最大排列的问题。倒过来再倒过去从而达到了遍历。
下午偶尔翻到以前的写到的一些代码,不觉心血来潮,看了一下其中关于全排列算法的 一个实现.联想到高中时数学课上学到关于组合和排列的一些公式,于是在纸上涂鸦着一些 计算方法,突然灵感潮来,想借助小白鼠的那个思路也能将全排列解决出来~
下班回到家便赶紧吃饭玩一局 sc 后,开始实现,终于赶在睡觉之前可以写这篇 blog,介绍我的 这种还算奇妙的全排列算法: 1. 算法思路:
大概就是这样的 -------------------------------#include"stdio.h" int a[10],n,count=0; void perm(int k) { int p,t,j; if( k==n ) {count++; for(p=1;p<=k;p++) printf("%1d",a[p]);/*"%1d"中的数字是 1,而不是字母 l*/ printf(" "); if( count%3==0 ) printf("\n"); return;} for(j=k;j<=n;j++) {t=a[k];a[k]=a[j];a[j]=t; perm(k+1); t=a[k]; a[k]=a[j];a[j]=t;} }
(AK,A1) , (A1,AK). 如果我将 1 周围的两个位置用 0 和 1 来表示,如果放在左边,那么左边用 1 表示,反
之用 0 表示.反正只能放一个地方,那么这两个位置合起来不是 10,就是 01,化成十进制便是 2 或者 1.
的。实际上,原来的 pi…pn,已经是这一部分最大的一个排列了。但我们现在换了最高位 pi-1,
因此要让后面的数字变的最小。方法很简单,根据上面的推理,我们只须将 pi~pn 的数列倒置
即可(最大的排列倒置就变成了最小的排列)。
这样,我们计算出了准确的下一个排列。
比如有(1,2,3,4)这样一组数
1.先分成(1)和(2,3,4),然后对(2,3,4)全排列 2.把(1)分别和(2,3,4)中的数对调 3.比如一次调换(2),(1,3,4),然后对(2,3,4)全排列 4.调换的算完了,恢复,变成(1),(2,3,4),再调换下一个(3),(1,2,4)
第一次看到这个算法是在软件设计师的辅导书上。代码如下,在 VC++ 7.0 下调试通过。
// Permutation.cpp : 定义控制台应用程序的入口点。 // //N 个数全排列的非递归算法 #include “stdafx.h” void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; } /* 根据当前的排列 p,计算下一个排列。 原则是从 1234–>4321,若 p 已经是最后一个排列,传回 false,否则传回 true。 p 是一个 n 维向量。 */ bool nextPermutation(int *p, int n) { int last = n – 1; int i, j, k; //从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。 i = last; while (i > 0 && p[i] < p[i - 1]) i–; //若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回 false。 if (i == 0) return false; //从后查到 i,查找大于 p[i - 1]的最小的数,记入 k k = i; for (j = last; j >= i; j–) if (p[j] > p[i - 1] && p[j] < p[k]) k = j; //交换 p[k]和 p[i - 1] swap(p[k], p[i - 1]); //倒置 p[last]到 p[i] for (j = last, k = i; j > k; j–, k++) swap(p[j], p[k]);
就查找从 pi 到 pn 中大于 pi-1 的最小的数字。然后将找到的数字与 pi-1 交换。
3.
还没有结束。交换后,pi-1pi…pn 并不是准确的后一个排列。因
为根据第一步的查找,我们有 pi > pi+1 > … > pn,否则查找在 i~n 就会停
下来了。这样的一个排列显然不是最小的。实际上,原来的 pi…pn,已经是这
return true; } //显示一个排列 void showPermutation(int *p, int n) { for (int i = 0; i < n; i++) cout << p[i]; } int _tmain(int argc, _TCHAR *argv[]) { int n; int *p; cin >> n; p = new an>int[n]; for (int i = 0; i < n; i++) p[i] = i + 1; showPermutation(p, n); cout << endl; while(nextPermutation(p, n)) { showPermutation(p, n); cout << endl; } delete[] p; system(“pause”); return 0; }
– 1(1 < i < n – 1),到 p1’p2’…pn’,pi < pi – 1(1 < i < n – 1)。因此:
1.
从低位到高位(从后向前),找出“不符合趋势”的数字。即找到一个 pi,使 p百度文库 – 1
< pi。若找不到这样的 pi,说明我们已经找到最后一个全排列,可以返回了。
2.
把 pi - 1 替换成从 pn 到 pi 几个数字中“刚刚好大于 pi-1”的数字。这里的目的
< n – 1)。因此:
1.
从低位到高位(从后向前),找出“不符合趋势”的数字。即找到
一个 pi,使 pi – 1 < pi。若找不到这样的 pi,说明我们已经找到最后一个全
排列,可以返回了。
2.
把 pi - 1 替换成从 pn 到 pi 几个数字中“刚刚好大于 pi-1”的
数字。这里的目的是找出准确的 P 的下一个排列 Q。所谓“刚刚好大于”,我们
if(p[j]>p[i-1]&&p[j]<p[k]) k =j;
//交换 p[k]和 p[i - 1] swap(p[k],p[i-1]); //倒置 p[last]到 p[i] /* for (j =last,k =i; j>k; j--,k++)
swap(p[j],p[k]); */ return true; } //显示一个排列 void showPermutation(int *p, int n)
反复运行 nextPermuation()找出比 P 的“下一个”全排列,直到 Pn! = p1’p2’…pn’,pi <
pi – 1
(1 < i < n – 1)为止。所找到的所有的排列就是 n 的全排列。
下面要考虑的问题,是如何从一个已知的排列 P = p1p2…pn,找到它的下一个排列
Q = q1q2…qn。我们要让排列从小到大生成,简单说,要让排列的趋势从 p1p2…pn,pi > pi
{ for(int i=0; i<n; i++) printf("%d ",p[i]); printf("\n");
} int main(int argc, char *argv[]) {
int n; int *p; scanf("%d",&n); p = new int[n]; for (int i = 0; i < n; i++)
(1 < i < n – 1)为止。所找到的所有的排列就是 n 的全排列。
下面要考虑的问题,是如何从一个已知的排列 P = p1p2…pn,找到它的下一个
排列
Q = q1q2…qn。我们要让排列从小到大生成,简单说,要让排列的趋势从 p1p2…
pn,pi > pi – 1(1 < i < n – 1),到 p1’p2’…pn’,pi < pi – 1(1 < i
试想 N 个自然数 A1,A2,...,AN 的全排列是 N!个,那么,对于每种排列,我将其打印出 来,遍历一遍至少是 O(N!)的复杂度,那么能不能在就在这个复杂度内用非递归解决这个问题 呢?我的想法就是从这点开始成形.同时借助了建模的思想分析的.
无论如何,我首先选取一个自然数 A1 出来,放到存放地址的中间, 那么在 A1 的周 围存在两个位置,一个是 A1 的左边,一个是 A1 的右边,那么,我抽出另一自然数出来(假设是 AK),便有两种存放的可能性:
i=last; while(i>0&&p[i]<p[i-1])
i--; //若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回 false。 if(i==0)
return false; //从后查到 i,查找大于 p[i - 1]的最小的数,记入 k k=i; for(j=last; j>=i; j--)
这个算法挺难理解的。这里试图说明一下算法的意思。
主要的任务在 nextPermuation()中完成。这其中的思想是,提供一个已经有的全排列 P,求出
P 的“下一个”全排列。这里“下一个”的意思是说,在 n 个数的 n!个全排列在数字上从小到
大的一个序列中,p 的下一个数字上比它大的一个排列。
那么由此,提供一个初始的 n 个数的全排列 P1 = p1p2…pn,有 pi > pi – 1(1 < i < n – 1),
是找出准确的 P 的下一个排列 Q。所谓“刚刚好大于”,我们就查找从 pi 到 pn 中大于 pi-1 的
最小的数字。然后将找到的数字与 pi-1 交换。
3.
还没有结束。交换后,pi-1pi…pn 并不是准确的后一个排列。因为根据第一步的查找,
我们有 pi > pi+1 > … > pn,否则查找在 i~n 就会停下来了。这样的一个排列显然不是最小
main() {int i; printf("\nInput n:"); scanf("%d",&n);
for(i=1;i<=n;i++) a[i]=i; perm(1); }
#include <stdio.h> void swap(int &a, int &b) {
int temp; temp = a; a = b; b = temp; } /* 根据当前的排列 p,计算下一个排列。 原则是从 1234–>4321,若 p 已经是最后一个排列,传回 false,否则传回 true。 p 是一个 n 维向量。 */ bool nextPermutation(int *p, int n) { int last=n-1; int i,j,k; //从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。
从小到大的一个序列中,p 的下一个数字上比它大的一个排列。
那么由此,提供一个初始的 n 个数的全排列 P1 = p1p2…pn,有 pi > pi – 1(1
< i < n – 1),反复运行 nextPermuation()找出比 P 的“下一个”全排列,直
到 Pn! = p1’p2’…pn’,pi < pi – 1
一部分最大的一个排列了。但我们现在换了最高位 pi-1,因此要让后面的数字
变的最小。方法很简单,根据上面的推理,我们只须将 pi~pn 的数列倒置即可
(最大的排列倒置就变成了最小的排列)。
这样,我们计算出了准确的下一个排列。
ps:这个主要的思想就是,当每次我们交换完之后,交换之后的后面的部分是一
个符合趋势的排列,为了得到所有的排列,我们将其反转,那就相当于对其部分