一个关于Android音乐随机播放的算法

pasd1866 8年前
   <h2>想法</h2>    <p>伪随机。</p>    <p>你的音乐列表里有一些歌,每首歌的初始随机因数为1。</p>    <p>每次你点击下一首时,每首歌的随机因数都会加1,然后随机到的那首歌随机因数变为0。</p>    <p>随机因数越大,被随机到的几率就越高。</p>    <p>比如有4首歌,那么下表是一种可能出现的情况:</p>    <table>     <thead>      <tr>       <th>-</th>       <th>Love Story</th>       <th>东风破</th>       <th>Refrain</th>       <th>Tassel</th>       <th>-</th>      </tr>     </thead>     <tbody>      <tr>       <td>第几次</td>       <td>随机因数</td>       <td>随机因数</td>       <td>随机因数</td>       <td>随机因数</td>       <td>随机到</td>      </tr>      <tr>       <td>1</td>       <td>1</td>       <td>1</td>       <td>1</td>       <td>1</td>       <td>东风破</td>      </tr>      <tr>       <td>2</td>       <td>2</td>       <td>0</td>       <td>2</td>       <td>2</td>       <td>Love Story</td>      </tr>      <tr>       <td>3</td>       <td>0</td>       <td>1</td>       <td>3</td>       <td>3</td>       <td>Refrain</td>      </tr>      <tr>       <td>4</td>       <td>1</td>       <td>2</td>       <td>0</td>       <td>4</td>       <td>Tassel</td>      </tr>      <tr>       <td>5</td>       <td>2</td>       <td>3</td>       <td>1</td>       <td>0</td>       <td>Love Story</td>      </tr>      <tr>       <td>6</td>       <td>0</td>       <td>4</td>       <td>2</td>       <td>1</td>       <td>Tassel</td>      </tr>      <tr>       <td>7</td>       <td>1</td>       <td>5</td>       <td>3</td>       <td>0</td>       <td>东风破</td>      </tr>      <tr>       <td>8</td>       <td>2</td>       <td>0</td>       <td>4</td>       <td>1</td>       <td>Love Story</td>      </tr>      <tr>       <td>9</td>       <td>0</td>       <td>1</td>       <td>5</td>       <td>2</td>       <td>Tassel</td>      </tr>      <tr>       <td>10</td>       <td>1</td>       <td>2</td>       <td>6</td>       <td>0</td>       <td>...</td>      </tr>     </tbody>    </table>    <p>...</p>    <p>可以看到,Refrain 这首歌连续6次没有出现,它的随机因数累加到了6,那么第十次它被随机到的概率是6/(1+2+6),即三分之二。</p>    <p>上面使用的是随机因数累加,其实我们还可以让随机因数累乘等等...</p>    <h2>Demo及实现</h2>    <p><img src="https://simg.open-open.com/show/62f752564f52a4325173e406970784ba.gif"></p>    <p>RandomPicker</p>    <p>Demo中的的大图截图自网易云音乐。</p>    <p><a href="/misc/goto?guid=4959713906811661709" rel="nofollow,noindex">前往GitHub Star/Fork/Compile</a></p>    <h2>如何使用</h2>    <p>快速开始:</p>    <pre>  <code class="language-java">RandomPicker randomPicker = new RandomPicker(12);  int nextPos = randomPicker.next();</code></pre>    <p>更多方法:</p>    <pre>  <code class="language-java">randomPicker.setMultiplyNumber(3);  randomPicker.setAddNumber(2);  randomPicker.setNextPick(5);  randomPicker.add();  randomPicker.changeOriginWeight(0,3);  randomPicker.getHistoryList();</code></pre>    <p>更多更多:</p>    <p><a href="/misc/goto?guid=4959713906811661709" rel="nofollow,noindex">请下载项目查看源码</a></p>    <h2>RandomPicker源码</h2>    <pre>  <code class="language-java">package top.wefor.randompicker;    import java.util.ArrayList;  import java.util.Random;    /**   * Created on 16/8/26.   * <p/>   * 适用于音乐随机播放等   * GitHub: https://github.com/XunMengWinter   * <p/>   * latest edited date: 2016-08-26   *   * @author ice   */  public class RandomPicker {        private ArrayList<Integer> mOriginWeightList = new ArrayList<>();      private ArrayList<Integer> mCurrentWeightList = new ArrayList<>();      private ArrayList<Integer> mHistoryList = new ArrayList<>();        private int mMultiplyNumber = 1;      private int mAddNumber = 1;      private int mPickedPosition;      private boolean isRepeatable;      private Integer mNextPickPosition;      Random mRandom = new Random();        public RandomPicker() {          //默认一个,避免报错。          new RandomPicker(1);      }        public RandomPicker(int size) {          initSize(size);      }        /*设置累乘积数*/      public void setMultiplyNumber(int multiplyNumber) {          mMultiplyNumber = multiplyNumber;      }        /*设置累加积数*/      public void setAddNumber(int addNumber) {          mAddNumber = addNumber;      }        /*指定下一次选中的位置*/      public void setNextPick(int pickedPosition) {          mNextPickPosition = pickedPosition;      }        /*是否允许连续两次出现同一个位置*/      public void setRepeatable(boolean repeatable) {          isRepeatable = repeatable;      }        /*初始化列表长度*/      public void initSize(int size) {          mOriginWeightList.clear();          mCurrentWeightList.clear();          mHistoryList.clear();          for (int i = 0; i < size; i++)              add();      }        /*获得当前条目数*/      public int getSize() {          return mOriginWeightList.size();      }        /*获取历史条目的位置列表*/      public ArrayList<Integer> getHistoryList() {          return mHistoryList;      }                   /*上为配置参数*/               /*下为逻辑实现*/          /*获得下一个随机条目的位置*/      public int next() {          random();          mHistoryList.add(mPickedPosition);          return mPickedPosition;      }        public void add() {          // 默认每个条目的比重为1.          add(getSize(), 1);      }        /*添加一个条目*/      public void add(int index, int weight) {          mOriginWeightList.add(index, weight);          mCurrentWeightList.add(index, calculateWeight(0, weight));      }        /*修改一个条目的比重*/      public void changeOriginWeight(int index, int weight) {          mOriginWeightList.set(index, weight);          int currentWeight = mCurrentWeightList.get(index);          mCurrentWeightList.set(index, currentWeight / mOriginWeightList.get(index) * weight);      }        /*移除一个条目*/      public void remove(int index) {          mOriginWeightList.remove(index);          mCurrentWeightList.remove(index);      }        /*执行随机算法*/      private void random() {          // 算出下一次选中的位置          if (mNextPickPosition != null) {              mPickedPosition = mNextPickPosition;              mNextPickPosition = null;          } else {              long allCount = 0;              for (int i = 0; i < mCurrentWeightList.size(); i++) {                  allCount += mCurrentWeightList.get(i);              }                long randomLong = (long) (mRandom.nextDouble() * allCount);              long currentLong = 0;              for (int i = 0; i < mCurrentWeightList.size(); i++) {                  currentLong += mCurrentWeightList.get(i);                  if (currentLong > randomLong) {                      mPickedPosition = i;                      break;                  }              }          }            // 若列表长度小于2,则下一次位置必为0.          if (mCurrentWeightList.size() < 2) {              mPickedPosition = 0;              return;          }            // 预先算好下一次的比重          for (int i = 0; i < mCurrentWeightList.size(); i++) {              int weight = calculateWeight(mCurrentWeightList.get(i), mOriginWeightList.get(i));              mCurrentWeightList.set(i, weight);          }          if (isRepeatable)              mCurrentWeightList.set(mPickedPosition, calculateWeight(0, mOriginWeightList.get(mPickedPosition)));          else              mCurrentWeightList.set(mPickedPosition, 0);      }        /*计算下一次的比重*/      private int calculateWeight(int currentWeight, int originWeight) {          return (currentWeight + mAddNumber) * mMultiplyNumber * originWeight;      }    }</code></pre>    <p>每次调用next()的时候,都要做两次for循环遍历列表,列表里12首歌倒是毫无压力,列表要是有500首歌的话肯定会延迟了,此处待改进。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/472fed76690a</p>    <p> </p>