shuffle 算法

    技术2022-05-19  45

    一种打乱顺序的算法,比如,洗牌,随机播放音乐(虽然有时候使用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;

     

    }

     

    —————————————————————————————————————————————————————————————

     

     


    最新回复(0)