疑问:关于swap和next_permutation

本文通过三个不同的函数实现全排列,并对比其运行效率。测试结果显示,自定义交换函数的方法效率最高,而使用STL中的next_permutation函数效率最低。

奇怪了,做全排列的时候,在交换两个数时用到algorithm中的swap函数,效率竟然比自己写一个swap的效率差,是因为大量进行调用的原因吗?求解....

full_permutation 为我写的计算全排列数量的函数,没有使用algorithm中的swap函数,

nextPerm  使用了algorithm中的next_permutation函数,

full_permutation_swap 为我写的计算全排列数量的函数,使用了algorithm中的swap函数.

#include<iostream>
#include<algorithm>
#include<Windows.h>
using namespace std;
LARGE_INTEGER t1,t2,tc;
int arr[]={1,2,3,4,5,6,7,8,9,10},counter1=0,counter2=0,counter3=0;
void begin()
{
	QueryPerformanceFrequency(&tc);
	QueryPerformanceCounter(&t1);
}
void end()
{
	QueryPerformanceCounter(&t2);
	cout<<(double)(t2.QuadPart-t1.QuadPart)/tc.QuadPart<<endl;
}
void sw(int i,int j)
{
	int temp=arr[i];
	arr[i]=arr[j];
	arr[j]=temp;
}
void full_permutation(int left,int right)
{
	if(left==right)
		counter1++;
	else
	{
		for(int i=left;i<=right;i++)
		{
			sw(left,i);
			full_permutation(left+1,right);
			sw(left,i);
		}
	}
}
void nextPerm()//next_permutation function 
{
	do
	{
		counter2++;
	}
	while(next_permutation(arr,arr+10));
}
void full_permutation_swap(int left,int right)
{

	if(left==right)
		counter3++;
	else
	{
		for(int i=left;i<=right;i++)
		{
			swap(arr[left],arr[i]);
			full_permutation_swap(left+1,right);
			swap(arr[left],arr[i]);
		}
	}
}
int main()
{
	begin();
	full_permutation(0,9);
	cout<<"full_permutation() time: ";
	end();
	cout<<"counter1: "<<counter1<<endl<<endl;

	begin();
	nextPerm();
	cout<<"nextPerm() time: ";
	end();
	cout<<"counter2: "<<counter2<<endl<<endl;

	begin();
	full_permutation_swap(0,9);
	cout<<"full_permutation_swap() time: ";
	end();
	cout<<"counter3: "<<counter3<<endl;

	return 0;
}


Test result:

full_permutation() time: 0.521275
counter1: 3628800


nextPerm() time: 2.98096
counter2: 3628800


full_permutation_swap() time: 1.53132
counter3: 3628800
请按任意键继续. . .



### next_permutation 算法的使用与实现原理 `next_permutation` 是一种用于生成给定序列的下一个字典序排列的算法。它广泛应用于 C++ 标准模板库(STL)中的 `<algorithm>` 头文件中,同时也被许多其他编程语言所支持或模仿其实现方式。该算法的核心思想是通过交换重排的方式找到当前排列的下一个可能的排列。 #### 使用方法 在 C++ 中,`std::next_permutation` 的基本用法如下: ```cpp #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> nums = {1, 2, 3}; // 打印所有排列 do { for (int num : nums) { std::cout << num << " "; } std::cout << std::endl; } while (std::next_permutation(nums.begin(), nums.end())); return 0; } ``` 上述代码会输出 `{1, 2, 3}` 的所有排列,按字典序顺序排列: ``` 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 ``` `std::next_permutation` 接受两个迭代器作为参数,表示要处理的序列范围,并返回一个布尔值,指示是否成功找到了下一个排列。如果当前已经是最后一个排列,则返回 `false`,否则返回 `true` [^4]。 #### 实现原理 `next_permutation` 的实现基于以下步骤: 1. **查找第一个可以增加的元素**:从右向左遍历数组,找到第一个比其右侧元素小的元素索引 `i`。 2. **查找最小的大于 `nums[i]` 的元素**:继续从右向左遍历,找到第一个大于 `nums[i]` 的元素索引 `j`。 3. **交换这两个元素**:将 `nums[i]` `nums[j]` 交换。 4. **反转后面的子数组**:将 `i+1` 到末尾的所有元素反转,以确保得到的是下一个字典序排列。 以下是 `next_permutation` 的一个简化实现版本: ```cpp template <typename T> bool next_permutation(std::vector<T>& nums) { int n = nums.size(); int i = n - 2; // Step 1: Find the first element that can be increased while (i >= 0 && nums[i] >= nums[i + 1]) { --i; } if (i == -1) { return false; // Already the last permutation } // Step 2: Find the smallest element larger than nums[i] on the right int j = n - 1; while (nums[j] <= nums[i]) { --j; } // Step 3: Swap elements at i and j std::swap(nums[i], nums[j]); // Step 4: Reverse the suffix starting from i+1 std::reverse(nums.begin() + i + 1, nums.end()); return true; } ``` 此函数接受一个 `std::vector<T>` 类型的输入,并返回一个布尔值,表示是否成功生成了下一个排列 [^4]。 #### 时间复杂度分析 - 最坏情况下,每次调用 `next_permutation` 的时间复杂度为 O(n),其中 n 是序列长度。 - 如果需要生成所有排列,则总时间复杂度为 O(n!),因为共有 n! 种不同的排列 [^4]。 #### 应用场景 `next_permutation` 常用于以下场景: - 生成所有可能的排列组合。 - 解决某些组合优化问题,如旅行商问题(TSP)的小规模实例。 - 在测试中枚举所有可能的输入排列。 - 密码学中用于穷举攻击某些有限长度的密码空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值