一個關于Android音樂隨機播放的算法
想法
偽隨機。
你的音樂列表里有一些歌,每首歌的初始隨機因數為1。
每次你點擊下一首時,每首歌的隨機因數都會加1,然后隨機到的那首歌隨機因數變為0。
隨機因數越大,被隨機到的幾率就越高。
比如有4首歌,那么下表是一種可能出現的情況:
- | Love Story | 東風破 | Refrain | Tassel | - |
---|---|---|---|---|---|
第幾次 | 隨機因數 | 隨機因數 | 隨機因數 | 隨機因數 | 隨機到 |
1 | 1 | 1 | 1 | 1 | 東風破 |
2 | 2 | 0 | 2 | 2 | Love Story |
3 | 0 | 1 | 3 | 3 | Refrain |
4 | 1 | 2 | 0 | 4 | Tassel |
5 | 2 | 3 | 1 | 0 | Love Story |
6 | 0 | 4 | 2 | 1 | Tassel |
7 | 1 | 5 | 3 | 0 | 東風破 |
8 | 2 | 0 | 4 | 1 | Love Story |
9 | 0 | 1 | 5 | 2 | Tassel |
10 | 1 | 2 | 6 | 0 | ... |
...
可以看到,Refrain 這首歌連續6次沒有出現,它的隨機因數累加到了6,那么第十次它被隨機到的概率是6/(1+2+6),即三分之二。
上面使用的是隨機因數累加,其實我們還可以讓隨機因數累乘等等...
Demo及實現
RandomPicker
Demo中的的大圖截圖自網易云音樂。
如何使用
快速開始:
RandomPicker randomPicker = new RandomPicker(12);
int nextPos = randomPicker.next();
更多方法:
randomPicker.setMultiplyNumber(3);
randomPicker.setAddNumber(2);
randomPicker.setNextPick(5);
randomPicker.add();
randomPicker.changeOriginWeight(0,3);
randomPicker.getHistoryList();
更多更多:
RandomPicker源碼
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>
每次調用next()的時候,都要做兩次for循環遍歷列表,列表里12首歌倒是毫無壓力,列表要是有500首歌的話肯定會延遲了,此處待改進。
來自:http://www.jianshu.com/p/472fed76690a