上机实践报告
一、目的
1.熟悉算法设计的基本思想
2.掌握随机选择算法(Randomized Selection)
二、内容与设计思想
用oj随机生成的1000000~4500000的数据规模,使用随机选择算法找到中位数并数输出,并画图描述不同规模下的运行时间差异;
(选做)使用SELECT算法重复第一步,并介绍自己的思路以及两个算法的区别;(做出加分)
三、使用环境
推荐使用C/C++集成编译环境。
水杉id:10205501403刘佳凡
四、实验过程
源代码
Randomized-select
#include <bits/stdc++.h>
using namespace std;
class Random
{
public:
//随机化分割
int randomized_partition(vector<int> &a, int p, int r);
int randomized_select(vector<int> &a, int p, int r, int i);
};
//下标为[p, r]之间的元素
int Random::randomized_partition(vector<int> &a, int p, int r)
{
int q = rand() % (r - p + 1) + p; //就是说先随机选一个--在p-r之间选一个包含p r
int temp = a[q];
a[q] = a[r];
a[r] = temp;
int i = p - 1;
for (int j = p; j < r; j++)
{
if (a[j] < a[r])
{
i = i + 1;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
temp = a[i + 1];
a[i + 1] = a[r];
a[r] = temp;
return i + 1;
}
//迭代版本
int Random::randomized_select(vector<int> &a, int p, int r, int i)
{
if (p == r)
return a[p];
int q;
q = randomized_partition(a, p, r);//用来划分的那个数的下标
int k;
k = q - p + 1;//可以得到用来划分的那个数是第几大的
if (i == k)
return a[q];
else if (i < k)
return randomized_select(a, p, q - 1, i);
else
return randomized_select(a, q + 1, r, i - k);
}
int main()
{
Random c;
vector<int> a;
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
int b;
cin >> b;
a.push_back(b);
//cout<<a[i]<<endl;
}
cout << c.randomized_select(a, 0, n - 1, n / 2);
}
Select
#include<bits/stdc++.h>
using namespace std;
class middle
{
public:
void insert_sort(int array[], int left, int a);
int find_middle(int array[], int left, int right);
int find_index(int array[], int left, int right, int median);
int Select(int array[], int left, int right, int k);
private:
int middle_array[900000];//用一个中位数数组来储存产生的中位数
};
void middle::insert_sort(int array[], int left, int a)//left是数组开始的下标,a代表了数组还有几个元素
{
for (int j = left; j < left + a; j++)
{
int key = array[j];
int i = j - 1;
while (i > left && array[i] > key)
{
array[i + 1] = array[i];
i--;
}
array[i + 1] = key;
}
}
int middle::find_middle(int array[], int left, int right)
{
if (left == right)
return array[left];
int index;
for (index = left; index < right - 5; index += 5)
{
insert_sort(array, index, 4);
int num = index - left;
middle_array[num / 5] = array[index + 2];
}
// 处理剩余元素
int remain = right - index + 1;
if (remain > 0)
{
insert_sort(array, index, remain - 1);
int num = index - left;
middle_array[num / 5] = array[index + remain / 2];
}
//得到新数组的下标
int new_array = (right - left) / 5 - 1;
if ((right - left) % 5 != 0)
new_array++;
// 根据剩余条件来看是否递归
if (new_array == 0)
return middle_array[0];
else
return find_middle(middle_array, 0, new_array);
}
// 寻找中位数的所在位置
int middle::find_index(int array[], int left, int right, int median)
{
for (int i = left; i <= right; i++)
{
if (array[i] == median)
return i;
}
return -1;
}
int middle::Select(int array[], int left, int right, int k)
{
// 找到所有数的中位数
int median = find_middle(array, left, right);
// 将中位数的中位数与最右元素交换
int index = find_index(array, left, right, median);
swap(array[index], array[right]);
//partition
int i = left - 1;
for (int j = left; j < right; j++)
{
if (array[j] < array[right])
{
i = i + 1;
swap(array[i], array[j]);
}
}
swap(array[i + 1], array[right]);
//划分之后寻找
i = i + 1;
int m = i - left + 1;
if (m == k)
return array[i];
else if (m > k)
return Select(array, left, i - 1, k);
else
return Select(array, i + 1, right, k - m);
}
int main()
{
middle c;
int n;
cin>>n;
int array[n];
for(int i =0;i<n;i++)
{
cin>>array[i];
}
cout << c.Select(array, 0, n- 1, n/2 )<< endl;
return 0;
}
画图描述不同时间下不同规模的差异
五、总结
关于select算法思路介绍:
1.先将数组划分为每五个元素一组的小数组,然后分别对每个小数组进行插入排序,找出其中位数。
2.将每个小数组得到的中位数存入一个新的数组中,然后再对这个新数组进行第一步的操作,直到只剩下一个元素,也就找到了中位数的中位数。
3.然后将找到的这个数与原数组最右边的元素交换,也就是说以这个数作为划分标准,剩下的做法与Randomized-select算法相同。
两个算法的不同点:
Randomized-select划分时是以产生的随机数进行划分,也就是等概率地返回任何元素作为主元;而select算法也是来自快速排序时的确定性划分算法partition,但做了修改在于,也就是说把划分的主元也作为了输入参数,他的主元是被划分的每个数组的中位数的中位数。这样修改的好处在于,能够保证得到对数组的一个好的划分 。通过上次纸质作业和教材上的理论,再加上这次实验结果,可以得知 Randomized-select的平均时间复杂度为 ,但是,如果考虑最坏情况,即每次选取的种子点都为当时序列的最大值或最小值,那么,最终的时间复杂度是 ,这是需要避免的一种情况。Select的最坏运行时间为T(n)。所以可以看到图里两种算法的运行时间是相差不大的。
关于这次实验过程心得
首先是更加熟练地掌握了两种算法的应用,但是在写select算法时,问题出在对找中位数的中位数那块,一开始之间返回了中位数那个数组的中间值,忘记了这个数组也是无序,所以得一直递归直到只剩一个元素。
文档信息
- 本文作者:Liu Jiafan
- 本文链接:https://jiapang777777.github.io//2023/04/01/%E9%9A%8F%E6%9C%BA%E9%80%89%E6%8B%A9%E7%AE%97%E6%B3%95/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)