关于用进制类实现全排列算法

TR@SOE 2006-03-27 03:53:05
闲来无聊。写了个用进制类(当然,不用类也可以)实现全排列算法的库。

排列/组合是数学中非常常用的算法,当然也是计算彩票中奖几率的基础……

从n个不同的元素中,取r个不重复的元素,按次序排列,称为从n个中取r个的无重排列。排列的全体组成的集合用排列表示。当r=n时称为全排列。从n个不同元素中取r个不重复的元素组成一个子集,而不考虑其元素的顺序,称为从n个中取r个的无重组合。组合的全体组成的集合用组合表示。

用来计算给定n/r时排列、组合的可能数量的公式是很简单的。这里不再列出,但是实际问题往往是要在一个给定的集合中,给出各个可能的组合、排列的构成。例如:给定025689这六个数字(或者abikpq这六个字母),给出这六个数字(字母)的全排列等等。

从网络上的搜索结果来看,我个人的认为是比较复杂,而且思路和代码不是很简单明了。让我以P8/4(在0-7这八个数字中任选4个的排列)作为一个例子,给出我的思路。

首先,我认为网络上的思路的郁结之处就在于过分考虑了无重排列的要求。无重排列当然是排列的主流,但是不能排除重复排列,而且我认为无重排列是重复排列的一个子集。生成一个排列结果后,判断其是否为重复排列是非常简单的。

第二,如果我们观察一下人工进行排列结果计算的过程:0000->0001->0002->……->0007- >0010->0011……->7777,就可以发现这实际上就是一个8进制数的加1过程。这个集合的总数只有4096个(也就是8的 4次方)。如果要得到无重排列,那么数量会降到1680个。

所以,我的思路就是用进制的加法来模拟全排列。下面就是我的类的声明,由于只是为了解决全排列的问题,所以这个进制类并不完整,只实现了操作符+以及输出符号<<的重载。我将它编译成一个库,这样在需要使用的程序中可以随时调用。

#ifndef BaseMH
#define BaseMH

const int MAX_BASE=62;
const String BASE_DIGIT="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const int DEFAULT_LENGTH=8;
const int DEFAULT_BASE=16;

class BaseM
{
public:
BaseM();
BaseM(int Base);
BaseM(const BaseM &aNumber);
BaseM(long l, int Base);
BaseM(String s, int Base);
~BaseM();

// Operators
BaseM& operator = (const BaseM &);
BaseM operator + (const BaseM &);
BaseM operator + (const int &M);
friend ostream& operator<< (ostream& os, const BaseM &M);

private:
int _Base; // Base for the number
int _Digits; // How many digits to store
char * Digits; // String to store digits
void Allocate();

long _DecValue;
};
//-------------------------------------------------------------
#endif

当然,还需要一些辅助的函数来将十进制的数字换算成N进制的“字符串”以及反向转换的函数,以及判断字符串是否有重复的函数等。这些都比较简单,我将其包含在另一个Support单元中。用户可以自行扩充这个Support单元。

为了测试这个库,我编写了一个简单的测试单元:

#include <vcl.h>
#pragma hdrstop

#include "BaseM.h"
#include "Support.h"

//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char* argv[])
{
BaseM a(12345, 16);
BaseM b("0000", 8);

for (int i=1;i<=4096;i++)
{
b=b+1;
String s=FillString(b.StrPresentation(), 4, '0');
if(StringHasDuplicate(s))
continue;
else
cout<<s.c_str()<<" ";
}



return 0;
}

如果要进行无重排列,也只要简单修改上述代码即可。实际运行时,可以看到程序输出了从最小的0123到最大的7654。

在我的算法中,得到组合的难度反而要比得到排列的难度要高。所以获得组合不应该用进制类的算法,而应该用其它的组合生成算法。这里摘录的算法来自:http://www.faq-it.org/archives/structure/5ce188903ad4d8611fd22ab458d2a32d.php。我没有验证,读者自己验证即可:

组合生成:从n个元素中取r个元素的组合
由一个组合c[1]c[2]...c[r]生成下一个组合的算法:
a) i=max{j¦c[j]<n-r+j}
b) c[i]=c[i]+1
c) c[j]=c[j-1]+1,j=i+1,i+2,...,r

不过,我想应该也可以根据排列的进制算法来解决组合的问题。这个就留待有兴趣的朋友自行研究了。

另外,我这里的程序中,用到了VCL的库,这主要是为了能加快开发速度,就没有更多的考虑效率、速度的问题。用纯C/C++类库改写这个类和相应的支持库也是没有问题的。

完整的源代码可以向我来信索取。我的EMAIL?看这里:http://go4pro.org/member.asp!

100分给那些对我的思路有创新/补充并共享出来的朋友。
...全文
137 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
BlueDeepOcean 2006-03-29
  • 打赏
  • 举报
回复
也看看。
3996906 2006-03-27
  • 打赏
  • 举报
回复
不看完整代码实在看不出来啥。。不过算法貌似效率好高啊

封装一下,把BASE_DIGIT和P、R都透露出来就好了,俺就拿它买彩票了。。。HOHO
netsys2 2006-03-27
  • 打赏
  • 举报
回复
看看。。
lurel 2006-03-27
  • 打赏
  • 举报
回复
这么强,研究研究!!!
3996906 2006-03-27
  • 打赏
  • 举报
回复
我想买彩票,下班了看。。。
daydayup234 2006-03-27
  • 打赏
  • 举报
回复
帮老大顶

13,822

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧