一个关于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>