实验二各种有趣的排序(2.3猴子排序,2.4算珠排序,2.5面条排序)

(1)问题描述
(以下都是题目老师可能怕我们看不懂详细的就差给我们写出来了)
一只猴子一直在打印机上胡乱打字,只要有无限的时间,总有一天可以恰好打印出一部莎士比亚的著作。这个理论同样可以用在排序上面。如果我们给数组随机排列顺序,每一次排列之后验证数组是否有序,只要次数足够多,总有一次数组刚好被随机成有序数组。
在这里插入图片描述
这样的排序算法和猴子用打印机打出莎士比亚著作的理论很相似,所以被称为“猴子排序”。
(2)设计思路
如何对数组元素进行随机排列?一个可行的方法是随机挑选数组中的两个元素,交换它们的位置,重复多次就得到一个随机后的数组。使用C语言和C++语言的标准库函数都可以实现。
C方法:
如果使用C语言系统库,可以使用rand()函数。库函数rand()可以产生在[0, RAND_MAX]范围内的随机整数,可在程序中直接使用,其中RAND_MAX是系统库内定义的宏,代表rand()函数可以产生的最大的整数。通过取余操作可以把生成的整数变换到你想要的范围。例如:rand() % n 会生成[0, n-1]之间的整数。以上函数均在C的标准库stdlib.h中,如果在标准C++项目中,应该使用#include 这个预处理指令。
需要注意的一点是,计算机产生的随机数都是伪随机数,即看起来是随机的,但是如果不做好随机数产生器的初始化,那么每次得到的随机数序列都是相同的。为了程序每次运行得到不同的随机数序列,需要在每次程序运行时给随机数产生器一个不同的种子(基本上就是一个无符号整数)。当然你可以每次让用户输入一个整数作为种子,但是这样肯定会让用户抓狂。比较通用的办法是使用当前系统的时间,比如把当前日期的年月日时分秒的数字加在一起作为种子,这样遇到相同种子的概率是很低的。C标准库里有time函数,该函数返回当前时间,作为随机数种子传给srand函数:srand((unsigned int)time(0)),这样每次程序运行时rand函数就会产生不同的伪随机数序列。

1.	srand((unsigned int)time(0));  
3.	unsigned int a[5];  
4.		for (int i = 0; i < 5; i++)  
5.	    a[i] = rand();  

每次运行上面代码时,数组a里的元素都是不同的随机整数。如果没有srand那行,那么每次运行程序数组a里面的随机数都是相同的,可以自己注释掉试试看。
有了随机数就可以利用它产生随机的数组下标,多次随机交换2个数据元素,得到一个随机排列的数组。
C++的方法:
C++标准库提供了将容器元素随机排序的shuffle方法。关于什么是“容器”等我们学习C++的泛型编程时会讲,现在可以简单地理解,数组就是一种存放数据的容器。
如果你使用的是比较老版的C++标准,比如C++0x,那么可以使用random_shuffle函数。需要添加如下头文件:

  1. #include // std::random_shuffle
  2. #include // std::time
  3. #include // std::rand, std::srand
    在有些标准库实现版本里(Windows下的MinGW64,v6.0),random_shuffle函数的随机种子可以由srand函数设定,而有些实现版本不行,那么可以自己定义一个基于rand函数的随机数产生函数:
1.	// random generator function:  
2.	int myrandom (int i) { return std::rand()%i;}  

在主函数里,可以调用random_shuffle函数对数组进行乱序:

1.	std::srand ( unsigned ( std::time(0) ) );  
2.	
3.	int a[5] = {1, 2, 3, 4, 5};  
4.	
5.	// using built-in random generator:  
6.	std::random_shuffle ( a, a+5 );  
7.	  
8.	// using myrandom:  
9.	std::random_shuffle ( a, a+5, myrandom);  

上面2种不同的random_shuffle函数调用方法,第二个是使用了我们自己定义的随机数产生器,可以根据自己编译器的实现看看两种方法的不同。
如果你使用的是比较新的C++标准,比如C++17删除了C++0x的random_shuffle函数,程序应该使用C++11添加进去的std::shuffle函数。首先需要添加下面的头文件:

1.	#include <algorithm>    // std::shuffle  
2.	#include <random>       // std::default_random_engine  
3.	#include <chrono>       // std::chrono::system_clock  

这样就可以把一个数组里的元素进行随机排列。

1.	int a[5] = {1, 2, 3, 4, 5};  
2.	// 利用系统时间获得一个随机数种子  
3.	unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();  
4.	shuffle(a, a+5, std::default_random_engine(seed)); // 数组a里面的元素已被乱序  

以上都是老师给的题目里的……

以下是我的

//包含的头文件和声明啥的
#include<iostream>
#include<algorithm>
#include<random>
#include<chrono>
#define N 10
int large;
using std::cout;
using std::cin;
using std::endl;

main函数

int main()
{
	int num[N];
	input_data(num);
	while (1)
	{
		unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
		shuffle(num, num + large, std::default_random_engine(seed));
		//上面介绍的随机排序的方法
		if (judge(num))//若成功就输出耶
		{
			for (int j = 0; j < large; j++)
				cout << num[j]<<"  ";
			return(0);
		}
	}return(0);
}

不再多说这个数据输入函数

void input_data(int num[])
{
	num[100];
	int i = 0;
	char ch;
	do
	{
		cin >> num[i++];
		ch = cin.get();

	} while (ch != '\n');
	large = i;
}

判断一下是否已经排序完成啦?

int judge(int num[])//传入数组num
{
	for (int i = 0; ; )
	{
		while (num[i] <= num[i + 1])//依此判断是不是顺序的
		{
			i++;
			if (i == large - 1)//i到了倒数第二个(也就是和倒数第一个已经比较过了)
				return 1;
		}
		if (i < large - 1)//如果没比到倒数第二个那么就返回0,失败了
			return 0;
	}
	return 0;

}

这个函数最大的缺点就是没办法输入多个数比较,判断的次数太多了 ,要好久好久好久……

2.4 珠排序
这个排序算法和算盘相似。见过算盘的人都知道,算盘上有许多圆圆的珠子被串在细杆上,就像下面这样:
在这里插入图片描述

如果把算盘竖起来,会发生什么呢?算盘上的小珠子会在重力的作用下滑到算盘底部,就像下面这样:
在这里插入图片描述

这里有一个很神奇的细节:如果统计每一横排珠子的个数,你会发现下落后每一排珠子数量恰好是下落前珠子数量的升序排列!
比如上面的例子,下落前后每一横排的珠子数量:
在这里插入图片描述
在这里插入图片描述

那么,我们可以模拟珠子下落的原理,对一组正整数进行排序。用二维数组来模拟算盘,有珠子的位置设为1,没有珠子的位置设为0。那么,一个无序的整型数组就可以转化成下面的二维数组:
在这里插入图片描述
接下来,我们模拟算盘珠子掉落的过程,让所有的元素1都落到二维数组的最底部:

在这里插入图片描述
最后,把掉落后的 “算盘” 转化成一维有序数组:
在这里插入图片描述

这样,排序就完成了。这个排序算法有一个非常形象的名字:珠排序。请你用代码实现这个算法吧。

这老师也太用心良苦了……这图解……

#include<iostream>
#define N 10
using std::cout;
using std::cin;
using std::endl;

int aba[N][N] = {0};
int large ;
void swap(int *a, int *b)//交换函数
{
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;

}
void input_data(int num[])//输入函数
{
	int i = 0;
	char ch;
	do
	{
		cin >> num[i];
		ch = cin.get();
		i++;
	} while (ch != '\n');
	large = i;
	/*cout << large;*/
}
int main()主函数
{
	int count = 0;
	int num[N];
	input_data(num);
	//二维数组赋值,变成算盘
	for (int i = 0; i < large; i++)
	{
		for (int j = 0; j < num[i]; j++)
		{
			aba[i][j] = 1;
		}
	}
	//输出二维数组
	//for (int i = 0; i < large; i++)
	//{
	//	for (int j = 0; j < N; j++)
	//		cout << aba[i][j];
	//	cout << endl;
	//}
	//cout << endl;
	int p = 0;
	//主要的算法
	//方法是:一个指针从上到下遍历,另一个从下到上遍历,上面的遇到1,下面的遇到0,就交换,写着还挺麻烦的
	//另一种方法:从上到下统计一共有几个1,(也就是几个珠子),在从下到上赋值这n个珠子,写起来简单好多,但是我没想到TAT是别人告诉我滴
	for (int j = 0; j < N; j++)
	{
		for (int i = large-1,p=0; p<i; i--,p++)
		{
			while (aba[p][j] == 0)
			{
				p++;
				if (i == p)
					break;
			}
			while (aba[i][j] == 1)
			{
				i--;
				if (i == p)
					break;
			}
			if (aba[i][j] == 0 && aba[p][j] == 1)
			{
				swap(&aba[i][j],&aba[p][j]);
				p++;
				i--;
			}
			
		}
	}
	//for (int i = 0; i < large; i++)
	//{
	//	for (int j = 0; j < N; j++)
	//		cout << aba[i][j];
	//	cout << endl;
	//}
	for (int i = 0; i < large; i++)//统计珠子数
	{
		for (int j = 0; j < 20; j++)
		{
			if (aba[i][j] == 0)
				break;
			if(aba[i][j] == 1)
				count++;
		}
		num[i] = count;
		count = 0;
	}
	cout << endl;
	for (int i = 0; i < large; i++)
		cout << num[i]<<"  ";
}

贴一下结果叭
在这里插入图片描述
**

代码勘误:!!!

**
//修改了原来的写法,增加了几个判断;又增加了一种新的写法;感谢勘误

#include<iostream>
#define N 10
using std::cout;
using std::cin;
using std::endl;

int aba[N][N] = {0};
int large ;
void swap(int *a, int *b)
{
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;

}
void input_data(int num[])
{
	int i = 0;
	char ch;
	do
	{
		cin >> num[i];
		ch = cin.get();
		i++;
	} while (ch != '\n');
	large = i;
}
int main()
{
	int count = 0;
	int num[N];
	input_data(num);
	//二维数组赋值
	for (int i = 0; i < large; i++)
	{
		for (int j = 0; j < num[i]; j++)
		{
			aba[i][j] = 1;
		}
	}
	//输出二维数组
	//for (int i = 0; i < large; i++)
	//{
	//	for (int j = 0; j < N; j++)
	//		cout << aba[i][j];
	//	cout << endl;
	//}
	//cout << endl;
	
	int p = 0;				//修改了原来的写法
	for (int j = 0; j < N; j++)
	{
		for (int i = large-1,p=0; p<=i; i--,p++)
		{
			while (aba[p][j] == 0)
			{
				if (i == p)			//先判断
					break;
				p++;
				
			}
			while (aba[i][j] == 1)
			{
				if (i == p)			//先判断
					break;
				i--;
				
			}
			for (;aba[i][j] == 0 && aba[p][j] == 1;)
			{
				swap(&aba[i][j],&aba[p][j]);
				if (i == p || p+1==i )//增加判断
					break;
				p++;
				i--;
			}
			
		}
	}
	//更新一种写法
	/*int i = 0;
	for (int j = 0; j < N; j++)
	{
		int n = 0;				//n是一排算珠的个数
		for (; i < large; i++)
		{
			if (aba[i][j] == 1)
				n++;
		}
		n -= 1;
		for (i = large - 1; i >= 0 && n >= 0; i--, n--)//从下到上给算珠付n个值
		{
			aba[i][j] = 1;
		}
		for (; i >= 0; i--)//上面的付为0
		{
			aba[i][j] = 0;
		}

	}*/
//输出二维代码
	//for (int i = 0; i < large; i++)
	//{
	//	for (int j = 0; j < N; j++)
	//		cout << aba[i][j];
	//	cout << endl;
	//}
	for (int i = 0; i < large; i++)
	{
		for (int j = 0; j < 20; j++)
		{
			if (aba[i][j] == 0)
				break;
			if(aba[i][j] == 1)
				count++;
		}
		num[i] = count;
		count = 0;
	}
	cout << endl;
	for (int i = 0; i < large; i++)
		cout << num[i]<<"  ";
}

2.5 面条排序
如果桌子上有一把长短不一的面条,此时你将面条立起来,下端平放在桌面上,此时你用手掌在空中从上往下缓慢移动,慢慢的,你的手掌触碰到了一根面条(这根面条是最高的),你将这根面条抽走(抽走的面条当然想怎么吃就怎么吃),手继续慢慢向下移动,这时,你又碰到了倒数第二高的面条,你又将其抽走,。。。。
算法中,我们用一个数模拟手,每次-1,为了不至于手掌无处安放,我们将手直接放在最高的面条的顶端。
请你用代码实现这个算法吧。

最后一个没给啥东西,我还理解错题目了(丢人(lll¬ω¬)),我以为是挑出最大值,然后放到最后一个,这不就是选择排序么???
但其实这道题就是手每次向下挪一位,然后看看有木有面条~
十分短小的代码

#include<iostream>
#include<algorithm>//头文件
using namespace std;
int main()
{
	int num[6] = { 6,5,4,3,2,1 };
	int tmp[6] = { 0 };
	int large = *max_element(num,num+6);//找最大值
	for (int j = 5; j >= 0; j--)
	{
		for (int i = 0; i < 6; i++)
			if (num[i] == large)//摸到面条
			{
				tmp[j] = num[i];
				num[i] = 0;
			}
		large--;//手向下挪
	}
	for (int i = 0; i < 6; i++)
		cout << tmp[i];
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值