音乐随机播放算法

来源:互联网 发布:论坛域名购买 编辑:程序博客网 时间:2024/05/17 23:09

最近做的项目是个跟音乐相关的App,里面涉及到音乐随机播放。看到这篇文章,写得不错,便转了过来。

本文转自http://blog.csdn.net/asce1885/article/details/7582735


常见的音乐随机播放算法主要有两种:一是Shuffle算法;二是Random算法。

一 Shuffle算法

Shuffle算法和排序算法正好相反,是从有序到乱序的一个过程,俗称洗牌算法。它将播放列表中的歌曲顺序打乱,变成一个和原来歌曲顺序没有任何关系的乱序的播放列表,之后进行歌曲的播放,并支持当用户点击“上一首”时,能够回到刚刚播放的那一首歌曲。

二 Random算法

Random算法是在选取即将播放的歌曲时,进行一个随机数的运算,得到即将播放的歌曲在播放列表中的索引,播放列表本身并没有被打乱,只是利用随机函数从播放列表中选取一首歌曲进行播放而已。

现在比较普遍的随机数生成算法是基于线性同余算法实现的,例如C语言标准库函数rand()就是利用它产生随机数的。线性同余算法能够产生均匀分布的随机数,但是它依赖于给定的随机数的上限,如果上限越小,产生的随机数重复的概率就越大。

Random算法另一个缺陷是当点击“上一首”时,跟“下一首”功能完全一样,都是重新生成随机数,并利用它从播放列表中选取歌曲进行播放,而不会回到刚刚播放的那一首歌。当然,这个缺陷可以通过提供历史记录来弥补,只是需要花费额外的空间。

三 随机函数

上面两种算法的关键都是随机函数,下面介绍Java和C两种语言中随机函数的使用。

3.1)Java中的随机函数

Java是采用线性同余算法产生随机数的,优点是随机性好,周期长,速度快,易于计算机软件实现,缺点是产生的随机数受数学规律的制约,具有周期性和相关性,只能产生伪随机序列。JDK提供的随机数生成方式有两种:

3.1.1)Math.random()函数

该函数返回0到1区间中的某个double值,源码实现如下:

[java] view plain copy
 print?
  1.    private static Random randomNumberGenerator;  
  2.   
  3.    private static synchronized Random initRNG() {  
  4.        Random rnd = randomNumberGenerator;  
  5.        return (rnd == null) ? (randomNumberGenerator = new Random()) : rnd;  
  6. }  
  7.   
  8.    public static double random() {  
  9.        Random rnd = randomNumberGenerator;  
  10.        if (rnd == null) rnd = initRNG();  
  11.        return rnd.nextDouble();  
  12.    }  
可见,该函数底层是调用Random类实现的,首次调用random函数时,静态成员变量randomNumberGenerator为空,因此将调用Random类的构造函数进行初始化,随后每次调用random函数randomNumberGenerator不再为空,将不会再实例化Random类,因此,每次调用Math.random()函数用到的都是同一个种子,也就是首次调用的系统时间产生的种子。

3.1.2)Random类

位于Java.util包中,该类有两个构造函数,实现如下:

[java] view plain copy
 print?
  1.    public Random() {  
  2.        this(seedUniquifier() ^ System.nanoTime());  
  3. }  
  4.    public Random(long seed) {  
  5.        this.seed = new AtomicLong(initialScramble(seed));  
  6.    }  
可见,构造函数默认使用当前的系统时间产生种子,用于初始化Random对象。之后就可以调用各种next*函数来获取各种类型的随机数,如nextBytes,nextInt,nextDouble,nextGaussian等等。

3.2)C/C++中的随机函数

C/C++中最常用的生成伪随机数的方法是C标准库提供的rand()函数,它定义在stdlib.h文件中,能够返回0~RAND_MAX之间均匀分布的伪随机数(RAND_MAX至少为32767,一般默认为32767)。

直接调用rand(),每次生成的伪随机序列是相同的,因为rand()在生成随机数时会使用一个种子(默认值是1),作为计算随机数的初始值,如果种子相同,那么生成的伪随机序列也将是一样的。解决的办法很简单,就是每次使用不同的种子来调用rand()函数,srand()函数就是用来设置rand()函数产生随机数时使用的种子的。

srand()函数原型如下:

[cpp] view plain copy
 print?
  1. void srand(  
  2.    unsigned int seed   
  3. );  
srand()和rand()配合使用的示例如下:

[cpp] view plain copy
 print?
  1. #include <stdlib.h>  
  2. #include <stdio.h>  
  3. #include <time.h>  
  4.   
  5. int main( void )  
  6. {  
  7.    int i;  
  8.   
  9.    /* Seed the random-number generator with current time so that 
  10.     * the numbers will be different every time we run. 
  11.     */  
  12.    srand( (unsigned)time( NULL ) );  
  13.   
  14.    /* Display 10 numbers. */  
  15.    for( i = 0;   i < 10;i++ )  
  16.       printf( "  %6d\n", rand() );  
  17. }  
需要注意的一点是,上面代码在生成多个随机数时,要将srand()放到for循环的外面,否则上面输出的10个随机数都将是一样的,因为计算机运行速度太快,导致time()函数执行的结果没来得及改变。详见下图。




四 音乐随机播放算法的Shuffle实现

4.1)C/C++版本

[cpp] view plain copy
 print?
  1. #include <cstdlib>    
  2. #include <iostream>    
  3. using namespace std;    
  4.    
  5. //得到范围start~end之间的随机数   
  6. int rand(int start, int end)    
  7. {    
  8.     return rand()%(end - start) + start;    
  9. }    
  10.     
  11. void swap(int* a, int* b)    
  12. {    
  13.     *a = *a ^ *b;    
  14.     *b = *a ^ *b;    
  15.     *a = *a ^ *b;    
  16. }    
  17. void shuffle(int a[], int len)    
  18. {    
  19.     int key;    
  20.     srand((unsigned int)time(NULL));//srand应该放在for循环之外   
  21.     for(int i = 0; i< len; i++)    
  22.     {    
  23.         key = rand(0, len);    
  24.         printf("key = %d\n", key);  
  25.         swap(a[i], a[key]);  //乱序   
  26.     }    
  27. }    
  28.     
  29. int main(int argc, char *argv[])    
  30. {    
  31.     int a[8]={3, 5, 7, 2, 12, 1, 8, 6};    
  32.     shuffle(a, 8);    
  33.     for(int i = 0; i < 8; i++)    
  34.     {    
  35.         printf("%d\n", a[i]);    
  36.     }    
  37.     system("pause");    
  38.     return 0;    
  39. }  

4.2)Java版本
[java] view plain copy
 print?
  1. import java.util.Random;  
  2.   
  3. public class ASCE {  
  4.   
  5.     public static void main(String arg[]) {  
  6.         String[] musicUrl = { "/music/1.mp3""/music/2.mp3""/music/3.mp3",  
  7.                 "/music/4.mp3""/music/5.mp3""/music/6.mp3""/music/7.mp3",  
  8.                 "/music/8.mp3""/music/9.mp3""/music/10.mp3" };  
  9.   
  10.         shuffle(musicUrl);  
  11.         for (String music : musicUrl) {  
  12.             System.out.println(music);  
  13.         }  
  14.     }  
  15.   
  16.     public static void shuffle(String[] musicUrl) {  
  17.         int key;  
  18.         String temp;  
  19.         Random rand = new Random();  
  20.   
  21.         for (int i = 0; i < musicUrl.length; i++) {  
  22.             key = rand.nextInt(musicUrl.length - 1);  
  23.             temp = musicUrl[i];  
  24.             musicUrl[i] = musicUrl[key];  
  25.             musicUrl[key] = temp;  
  26.         }  
  27.     }  
  28. }  

 现成的Shuffle库算法

其实无论在C++还是Java中,都已经实现了Shuffle算法,在实际项目开发中,如果不是有特殊要求的话,完全没有必要自己去实现Shuffle算法,下面就来看看已有的Shuffle算法。

5.1)C++中的Shuffle算法

C++ STL中的函数random_shuffle()就是用来对一个元素序列进行随机重新排序的算法,函数原型如下:

[cpp] view plain copy
 print?
  1. template<class RandomAccessIterator>  
  2.    void random_shuffle(  
  3.       RandomAccessIterator _First, //指向序列首元素的迭代器  
  4.       RandomAccessIterator _Last  //指向序列最后一个元素的下一个位置的迭代器  
  5.    );  
  6. template<class RandomAccessIterator, class RandomNumberGenerator>  
  7.    void random_shuffle(  
  8.       RandomAccessIterator _First,  
  9.       RandomAccessIterator _Last,  
  10.       RandomNumberGenerator& _Rand //调用随机数产生器的函数  
  11.    );  

具体用法可参见:http://blog.csdn.net/ACE1985/article/details/5868682 。


5.2)Java中的Shuffle算法

Collections类中实现了Shuffle算法,这个类位于java.util包中,有两个重载函数,下面直接看源码:

[java] view plain copy
 print?
  1. public static void shuffle(List<?> list) {  
  2.     Random rnd = r;  
  3.     if (rnd == null)  
  4.         r = rnd = new Random();  
  5.     shuffle(list, rnd);  
  6. }  
  7. private static Random r;  
  8.   
  9. public static void shuffle(List<?> list, Random rnd) {  
  10.     int size = list.size();  
  11.     if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {  
  12.         for (int i=size; i>1; i--)  
  13.             swap(list, i-1, rnd.nextInt(i));  
  14.     } else {  
  15.         Object arr[] = list.toArray();  
  16.   
  17.         // Shuffle array  
  18.         for (int i=size; i>1; i--)  
  19.             swap(arr, i-1, rnd.nextInt(i));  
  20.   
  21.         // Dump array back into list  
  22.         ListIterator it = list.listIterator();  
  23.         for (int i=0; i<arr.length; i++) {  
  24.             it.next();  
  25.             it.set(arr[i]);  
  26.         }  
  27.     }  
  28. }  

参考文献:

1)http://www.ifanr.com/29498 从随机播放算法看iPod的细节之美

2)http://blog.csdn.net/cstn_kdlx/article/details/7326516 随机数生成问题小结

3)http://blog.csdn.net/hcy0727/article/details/7581671 洗牌算法

4)http://blog.csdn.net/chosen0ne/article/details/6129315 随机播放CD

5)http://blog.csdn.net/ACE1985/article/details/5868682 random_shuffle()和transform算法

0 0
原创粉丝点击