C++11 用next_permutation算法计算排列组合数

自定义一个函数 size_t permuteVect (vector<string>&s, string t, size_t i=0, bool combine=false)

功能:对字串t任取其中的i个字符进行排列或组合,结果存入vector,返回值即排列组合数。

对于全排列就是使用next_permutation()做do-while循环的结果,对全排列函数的执行时间做了一下测试:

  阶乘数量级           排列耗时
  8!= 40320           0.4s
  9!= 362880         0.6s
10!= 3628800       3s
11!= 39916800     30s
12!= 479001600   200s后内存不足退出

部分排列就是把全排列结果截短排序再删除重复的即可,7个数任取几个的排列耗时在0.7秒左右,8个数耗时8~9s左右,9个或9个以上耗时太长没有意义。

组合就是在部分排列的基础上不考虑顺序的结果,7个数任取几个的组合耗时在0.8秒左右,8个数在10~20s之间,同样9个或9个以上耗时太长没有意义,时间都主要耗在了对36万以上个字串的排序上。

因此,本函数可以用于长度小于8的字串排列组合,全排列可以10位以内。源代码及测试结果,如下:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define print_test_msg {\
	cout<<s.size()<<"个数任取"<<i<<"个的"; \
	cout<<(combine?"组合":"排列")<<"有"<<j<<"种:"<<endl; \
    for (auto v:m) cout<<v<<" "; cout<<endl<<endl; \
}

size_t permuteVect(vector<string>&s, string t, size_t i=0, bool combine=false)
{	//对字串t任取其中的i个字符进行排列或组合,结果存入vector,返回值即排列组合数 
	s.clear();
	sort(t.begin(),t.end());
    if (combine&&(i==0||i>=t.size())){
        s.push_back(t);
        return 1;
    }
    if (i==1){
    	reverse(t.begin(),t.end());
        while (t.size()) s.push_back(t.substr(t.size()-1,1)),t.pop_back();
        return s.size();
    }
 	do s.push_back(i==0||i>=t.size()?t:t.substr(0,i));
    	while (next_permutation(t.begin(),t.end()));
    if (combine) for (auto&y:s) sort(y.begin(),y.end());
    if (i>0){
        sort(s.begin(),s.end());
		for (auto r=s.begin();r<s.end()-1;r++)
			if (*r==*(r+1)) s.erase(r--); 
	}
    return s.size();
}

void test(void)
{
	string s="123456";
	size_t i,j;
    vector<string> m;
    bool combine;
    
    i=3; combine=true;
    j=permuteVect(m,s,i,combine);
	print_test_msg;

    i=3; combine=false;
    j=permuteVect(m,s,i,combine);
	print_test_msg;
	
    i=4; combine=true;
    j=permuteVect(m,s,i,1); //1==true
	print_test_msg;
    
    i=4; combine=false;
    j=permuteVect(m,s,i,0); //0==false
	print_test_msg;
    
    s.pop_back(); s.pop_back(); //字串s去掉后2个字符 
    i=3; combine=false;
    j=permuteVect(m,s,i); //缺省一个参数就是排列 
	print_test_msg;
	
    i=3; combine=true;
    j=permuteVect(m,s,i,true);
	print_test_msg;

    j=permuteVect(m,s); //缺省两个参数就是全排列 
	cout<<s.size()<<"个数的全排列有"<<j<<"种:"<<endl; 
    for (auto v:m) cout<<v<<" "; cout<<endl;   
}

int main(void)
{
	test();
	return 0;
}

测试结果:

6个数任取3个的组合有20种:
123 124 125 126 134 135 136 145 146 156 234 235 236 245 246 256 345 346 356 456


6个数任取3个的排列有120种:
123 124 125 126 132 134 135 136 142 143 145 146 152 153 154 156 162 163 164 165
213 214 215 216 231 234 235 236 241 243 245 246 251 253 254 256 261 263 264 265
312 314 315 316 321 324 325 326 341 342 345 346 351 352 354 356 361 362 364 365
412 413 415 416 421 423 425 426 431 432 435 436 451 452 453 456 461 462 463 465
512 513 514 516 521 523 524 526 531 532 534 536 541 542 543 546 561 562 563 564
612 613 614 615 621 623 624 625 631 632 634 635 641 642 643 645 651 652 653 654


6个数任取4个的组合有15种:
1234 1235 1236 1245 1246 1256 1345 1346 1356 1456 2345 2346 2356 2456 3456

6个数任取4个的排列有360种:
1234 1235 1236 1243 1245 1246 1253 1254 1256 1263 1264 1265 1324 1325 1326 1342
1345 1346 1352 1354 1356 1362 1364 1365 1423 1425 1426 1432 1435 1436 1452 1453
1456 1462 1463 1465 1523 1524 1526 1532 1534 1536 1542 1543 1546 1562 1563 1564
1623 1624 1625 1632 1634 1635 1642 1643 1645 1652 1653 1654 2134 2135 2136 2143
2145 2146 2153 2154 2156 2163 2164 2165 2314 2315 2316 2341 2345 2346 2351 2354
2356 2361 2364 2365 2413 2415 2416 2431 2435 2436 2451 2453 2456 2461 2463 2465
2513 2514 2516 2531 2534 2536 2541 2543 2546 2561 2563 2564 2613 2614 2615 2631
2634 2635 2641 2643 2645 2651 2653 2654 3124 3125 3126 3142 3145 3146 3152 3154
3156 3162 3164 3165 3214 3215 3216 3241 3245 3246 3251 3254 3256 3261 3264 3265
3412 3415 3416 3421 3425 3426 3451 3452 3456 3461 3462 3465 3512 3514 3516 3521
3524 3526 3541 3542 3546 3561 3562 3564 3612 3614 3615 3621 3624 3625 3641 3642
3645 3651 3652 3654 4123 4125 4126 4132 4135 4136 4152 4153 4156 4162 4163 4165
4213 4215 4216 4231 4235 4236 4251 4253 4256 4261 4263 4265 4312 4315 4316 4321
4325 4326 4351 4352 4356 4361 4362 4365 4512 4513 4516 4521 4523 4526 4531 4532
4536 4561 4562 4563 4612 4613 4615 4621 4623 4625 4631 4632 4635 4651 4652 4653
5123 5124 5126 5132 5134 5136 5142 5143 5146 5162 5163 5164 5213 5214 5216 5231
5234 5236 5241 5243 5246 5261 5263 5264 5312 5314 5316 5321 5324 5326 5341 5342
5346 5361 5362 5364 5412 5413 5416 5421 5423 5426 5431 5432 5436 5461 5462 5463
5612 5613 5614 5621 5623 5624 5631 5632 5634 5641 5642 5643 6123 6124 6125 6132
6134 6135 6142 6143 6145 6152 6153 6154 6213 6214 6215 6231 6234 6235 6241 6243
6245 6251 6253 6254 6312 6314 6315 6321 6324 6325 6341 6342 6345 6351 6352 6354
6412 6413 6415 6421 6423 6425 6431 6432 6435 6451 6452 6453 6512 6513 6514 6521
6523 6524 6531 6532 6534 6541 6542 6543

4个数任取3个的排列有24种:
123 124 132 134 142 143 213 214 231 234 241 243 312 314 321 324 341 342 412 413
421 423 431 432

4个数任取3个的组合有4种:
123 124 134 234

4个数的全排列有24种:
1234 1243 1324 1342 1423 1432 2134 2143 2314 2341 2413 2431 3124 3142 3214 3241
3412 3421 4123 4132 4213 4231 4312 4321

--------------------------------
Process exited after 0.4791 seconds with return value 0
请按任意键继续. . .

重复排列

比如从四个运算符+-*/中任取3个且可以重复取的排列数有:4x4x4=64,暂时没找到现成的库函数可以用,只有自己用多重循环来实现。

#include <iostream>
#include <iomanip>
#include <vector>
#include <array>
#include <string>
using namespace std;

void permuteSign(vector<string>&v)
{	//四个字符任取3个的重复排列 
	string t,s="+-*/";
	for (int i=0;i<s.size();i++)
		for (int j=0;j<s.size();j++)
			for (int k=0;k<s.size();k++){
				t.clear();
				t.push_back(s[i]);
				t.push_back(s[j]);
				t.push_back(s[k]);
				v.push_back(t);
			}
}

bool eliminateSign(string s)
{	//排除不想要的排列 
	array<string,14>signs{"---","///","//-","-//","/-/","//+",
							"+//","/+/","/--","-/-","--/","+--","-+-","--+"};
	for (auto a:signs) if (a==s) return true;
	return false;
}

int main(void) 
{
	int i=0;
	vector<string>vSgn;
	permuteSign(vSgn);
	
	cout<<"4种运算符中任取3种的可重复排列:"<<endl; 
	for (auto v:vSgn){
		cout<<v<<" ";
		if (++i%10==0) cout<<endl;
	}
	
	for (auto iter=vSgn.begin();iter!=vSgn.end();iter++)
		if (eliminateSign(*iter)) vSgn.erase(iter--); 
	
	i=0;
	cout<<"\n\n删除14种不符合要求的排列后:"<<endl; 
	for (auto v:vSgn){
		cout<<v<<" ";
		if (++i%10==0) cout<<endl;
	}
	
	return 0;
}

运行结果: 

/*
4种运算符中任取3种的可重复排列:
+++ ++- ++* ++/ +-+ +-- +-* +-/ +*+ +*-
+** +*/ +/+ +/- +/* +// -++ -+- -+* -+/
--+ --- --* --/ -*+ -*- -** -*/ -/+ -/-
-/* -// *++ *+- *+* *+/ *-+ *-- *-* *-/
**+ **- *** **/ */+ */- */* *// /++ /+-
/+* /+/ /-+ /-- /-* /-/ /*+ /*- /** /*/
//+ //- //* ///

删除14种不符合要求的排列后:
+++ ++- ++* ++/ +-+ +-* +-/ +*+ +*- +**
+*/ +/+ +/- +/* -++ -+* -+/ --* -*+ -*-
-** -*/ -/+ -/* *++ *+- *+* *+/ *-+ *--
*-* *-/ **+ **- *** **/ */+ */- */* *//
/++ /+- /+* /-+ /-* /*+ /*- /** /*/ //*

--------------------------------
Process exited after 3.89 seconds with return value 0
请按任意键继续. . .
*/ 

 

vector 相关文章:

C++ vector声明和赋值的相关函数

C++ vector容器的多种遍历方式

C++ vector 删除和排序的相关函数

C++ vector 赋值、删除、排序类之外的其他函数

C++ vector 容器的全排列算法 next_permutation

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hann Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值