正经学徒,佛系记录,不搞事情
随机数的定义:
其实随机数是分为真随机数和伪随机数的
所谓真随机数,根据百科的解释,真随机数必须依赖于物理现象,例如大乐透,抛硬币,公路上第五辆过来的车是单号还是双号
而伪随机数又区分为强伪随机和弱伪随机
弱随机数:首先要满足是随机的,但容易找出随机的规律性
强随机数:首先要满足是随机的,且无法预测规律
综上所述,平时项目中用纯算法实现的随机其实都属于伪随机数,跟是否使用种子等因素无关。而真随机数的生成,听闻有人使用鼠标移动的轨迹,排风扇的噪音大小等物理现象来控制随机数的生成。
实战:
这里以qq音乐为例(谁让网易云下架了杰伦的歌),分析qq音乐的随机播放功能
假设有ABCDEF六首歌,随机播放功能有以下规律:
通过上述规律,可以推测出一种随机的过程:
当播放第一首音乐开始,后台将会把所有歌曲的顺序打乱,并保存歌曲的顺序,如当前歌单顺序是ABCDEF,此时播放音乐C,后台会打乱音乐的顺序:CDABFE,
且C和E收尾相连,形成一个环状,此时点击下一首的顺序将会是DABFE,点击上一首的顺序是EFBAD,上下切换的顺序将会是DABADCEFEFB。
当播放的音乐最后一首是B时,此时点击下一首将不会是C,而是重新排序的一个数组,同理当播放的音乐最后一首是D时,此时点击上一首将不会是C,而是重新排序的一个数组。
知道随机的过程后,问题就落在了怎么去随机歌单,这里就涉及到了shuffle洗牌算法
java的Collections类已经内置提供了shuffle算法,代码如下
private static Random r;
public static void shuffle(List> list) {
Random rnd = r;
if (rnd == null)
r = rnd = new Random(); // harmless race.
shuffle(list, rnd);
}
public static void shuffle(List> list, Random rnd) {
int size = list.size();
if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
for (int i=size; i>1; i--)
swap(list, i-1, rnd.nextInt(i));
} else {
Object arr[] = list.toArray();
for (int i=size; i>1; i--)
swap(arr, i-1, rnd.nextInt(i));
ListIterator it = list.listIterator();
for (int i=0; i list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
private static void swap(Object[] arr, int i, int j) {
Object tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
可见这里重载了两个shuffle方法,如果不传递Random参数,则默认使用当前时间作为种子
接下来对集合进行替换,这里的洗牌算法是直接对集合本身进行替换,如ABCDEF,首先从F开始,随机与六位中的某一位替换位置,之后是E,与五位中的某一位替换位置,具体与哪一位通过Random的随机数来决定。
最后就是实现播放器随机播放功能的java程序:
创建一个实体类 MusicOrder,用于模拟存储歌单顺序等信息,实际项目应持久化到数据库中,如redis等。
//歌单
List order = new ArrayList<>(Arrays.asList("NO1.安静", "NO2.手写的从前", "NO3.以父之名", "NO4.一路向北", "NO5.你好吗"));
//当前播放下标
private int currentPlayIndex = 0;
//最上一首的下标
private int prevIndex = 0;
//最下一首的下标
private int nextIndex = 0;
创建一个工具类MusicShuffleUtil,用于打乱歌单顺序,切歌,播放。
public static MusicOrder musicOrder = new MusicOrder();
public static void startMusic(String name){
//获取歌单
List order = musicOrder.getOrder();
if(name != null && name.length()>0){
int index = order.indexOf(name);
order.remove(index);
//洗牌算法打乱除当前音乐的其它音乐顺序
Collections.shuffle(order);
//将当前播放的音乐置为第一位
order.add(0, name);
}else{
//洗牌算法打乱全部顺序
Collections.shuffle(order);
name = order.get(0);
}
//清零
musicOrder.setCurrentPlayIndex(0);
musicOrder.setNextIndex(0);
musicOrder.setPrevIndex(0);
System.out.println("洗牌后的歌曲顺序:"+musicOrder.getOrder());
//播放
playMusic(name);
}
public static void nextMusic(){
int currentPlayIndex = musicOrder.getCurrentPlayIndex()+1;
if(currentPlayIndex==musicOrder.getOrder().size()){
currentPlayIndex -= musicOrder.getOrder().size();
}
//当前下标大于下一首的最大下标则修改最大下一首下标
if(currentPlayIndex>musicOrder.getNextIndex()){
musicOrder.setNextIndex(currentPlayIndex);
}
//歌单已播完,从新刷新歌单顺序
if(currentPlayIndex == musicOrder.getPrevIndex()){
startMusic("");
}else{
musicOrder.setCurrentPlayIndex(currentPlayIndex);
//播放
playMusic(musicOrder.getOrder().get(currentPlayIndex));
}
}
public static void prevMusic(){
int currentPlayIndex = musicOrder.getCurrentPlayIndex()-1;
if(currentPlayIndex<0){
currentPlayIndex += musicOrder.getOrder().size();
}
//当前下标小于上一首的最小下标则修改最小上一首下标
if(currentPlayIndex
测试:以NO3音乐开始随机播放,控制输入1切换上一首,其它切换下一首
public static void main(String[] args) {
//开始播放
MusicShuffleUtil.startMusic("NO3.以父之名");
Scanner sc=new Scanner(System.in);
while(sc.hasNext())
{
if("1".equals(sc.next())){
MusicShuffleUtil.prevMusic();
}else{
MusicShuffleUtil.nextMusic();
}
}
}
结果:
项目地址:
https://pan.baidu.com/s/1cFrSBPZv4eh6NV8LpV1ZPg 提取码: xbaq