一种打乱顺序的算法,比如,洗牌,随机播放音乐(虽然有时候使用random)。
以洗牌为例,假设我们使用数组存放一副扑克:
比较容易的方法,就是每次产生一个随机数,然后用对应的元素与数组中的最后一个元素进行交换,若干次后便可将序列打乱。
for(i = 0; i< n; i++)
{
d= rand();
swap( card[d], card[n-1] );
}
这个算法有些像插入排序~简单易懂,效率也高,但是过程不可逆。
—————————————————————————————————————————————————————————————
有了random()算法在,随机播放已经不成问题了。可是,使用random算法,还是有很大可能性连续播同一首歌曲。但是,如果使用,shuffle算法,则可以保证在一段时间内,我们能听到不同的歌曲,因为我们一直使用整个列表来播放(做成循环的)。
我们使用一个隐含的shuffle播放列表(一个循环队列)来储存歌曲的顺序,并用一个指针表示正在播放的歌曲(记作"^"),比如当前的播放列表是这样的:
ABCDEFGHIJKLMN ^
即现在有14首歌,将要播放位置1的歌曲(正在播放位置14的歌曲),我们认为队列头和尾是相连的,即N后面的元素是A,那么这样够成了一个循环队列。在播放之前,我们在前7(7=14*0.5,这个比例可以随便选,当然越大随机性越大,但能后退的次数越少)个位置中,随机取一个一首歌,把它和将要播放的那个位置的歌曲交换。假设我们选的是E,则队列变成这样:
EBCDAFGHIJKLMN^
然后播放E。E播放完了以后(或者选择下一首时),重复刚才的动作,即在BCDAFGH中随机选一个,交换,比如选到H,则队列变成:EHCDAFGBIJKLMN ^
然后播放H。这样,一个shuffle算法初步完成了。
比如某一时刻播放器的状态是这样:EHCDAFGBIJKLMN ^则我们在LMNEHCD中选择一个,比如选择到H,那么交换并播放,成为:ELCDAFGBIJKHMN ^但是如果用户选择上一首怎么办呢?我们可以再记录一个指针指向最新shuffle选择出来的那首歌曲(记作"*"),没有选择过前一首的时候,它与播放指针指向同一个位置。当选择前一首的时候,仅移动指针^,而不移动*,比如上一个例子播放的时候按下前一首以后,成为:
ELCDAFGBIJKHMN ^*
这时候播放的K正好是刚才播放的那一首,当然这达到了我的目的,即可以选到刚才播放的曲目,当然如果再一次选择上一首,就会变成:
ELCDAFGBIJKHMN ^ *
这时候如果按下一首,应该判断^指向的是不是和*指向的相同,如果相同,就按照最早介绍的shuffle算法进行随机选取,不相同就简单的移动^,即成为:
ELCDAFGBIJKHMN ^*
示例代码:
if(key == next)
{
if(p1 == p2)
{
p1 = p1 + 1;
p2 = p2 + 1;
k = rand()%(length / 2);
swap(p1, (p1 + k)%length );
play(p2);
}
else
{
p1 = (p2 + 1)%length;
play(p2); }
if(key == prev)
{
p2 = (p2 + length - 1)%length;
play(p2);
}
}
—————————————————————————————————————————————————————————————
#include <iostream.h>#include <string.h>#include <stdlib.h>#include <stdio.h>
#define PNULL -99#define PERROR -98#define PLEN 254
long HexToDec( char *pbuff){ if(pbuff == NULL) return -1; char *ptmp; return strtol(pbuff,&ptmp,16); //将十六进制转换成十进制 }
//shuffle 算法//pSData -- shuffle前的数据,长度为16位//pOData -- shuffle后的数据,输出为16位//pSKey -- shuffle的密钥,8位长//pBSKey -- shuffle的基密码,16位长//返回 // 0 -- 成功// othre -- 失败
int shuffle(const char *pSData,char *pOData,const char *pSKey,const char *pBSKey){ if(pSData == NULL || pOData == NULL || pSKey == NULL || pBSKey == NULL) return -PNULL; if(strlen(pSKey)<8 || strlen(pBSKey) < 16) return PERROR; int m=0; int jlen=0; int j=0; int sk=0; int sklen=0; int v=0; int s=0; int y=0; int l=0; int z=0; int p=0; int p1=0; int w = 0;
char tmpbuf[PLEN]={0};
jlen = strlen(pSData); j = jlen - 1; sklen = strlen(pSKey); sk = sklen - 1;
do { memset(tmpbuf,0,sizeof(tmpbuf)); sprintf(tmpbuf,"%c",pSData[j]); p = HexToDec(tmpbuf); if( p == -1) return PNULL; memset(tmpbuf,0,sizeof(tmpbuf)); sprintf(tmpbuf,"%c",pSKey[sk]); p1 = HexToDec(tmpbuf); if( p1 == -1) return PNULL; v = p+p1+m; s = v/10; //取商 m = s; y = v; //取余数 memset(tmpbuf,0,sizeof(tmpbuf)); sprintf(tmpbuf,"%c",pBSKey[j]); l = HexToDec(tmpbuf); if( l == -1) return PNULL;
z = l^y; memset(tmpbuf,0,sizeof(tmpbuf)); sprintf(tmpbuf,"%c",z); pOData[w] =tmpbuf[0]+0x30; j--; sk--; w++; if(sk < 0) sk = 0; }while(j >= 0);
return 0;}
void main(){
char p[20]={0};
char data[17]={0}; strcpy(data,"0123456789123456");
char key[9]={0}; strcpy(key,"12345678");
char bkey[17]={0}; strcpy(bkey,"19F2C8276AD0839B");
int r = shuffle(data,p,key,bkey); cout<<p<<endl;
}
—————————————————————————————————————————————————————————————