想法:
偽隨機。
你的音樂列表里有一些歌整胃,每首歌的初始隨機因數(shù)為1颗圣。
每次你點擊下一首時,每首歌的隨機因數(shù)都會加1屁使,然后隨機到的那首歌隨機因數(shù)變?yōu)?在岂。
隨機因數(shù)越大,被隨機到的幾率就越高蛮寂。
比如有4首歌蔽午,那么下表是一種可能出現(xiàn)的情況:
- | Love Story | 東風破 | Refrain | Tassel | - |
---|---|---|---|---|---|
第幾次 | 隨機因數(shù) | 隨機因數(shù) | 隨機因數(shù) | 隨機因數(shù) | 隨機到 |
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 這首歌連續(xù)6次沒有出現(xiàn)酬蹋,它的隨機因數(shù)累加到了6及老,那么第十次它被隨機到的概率是6/(1+2+6)抽莱,即三分之二。
上面使用的是隨機因數(shù)累加骄恶,其實我們還可以讓隨機因數(shù)累乘等等...
Demo及實現(xiàn)
Demo中的的大圖截圖自網(wǎng)易云音樂食铐。
前往GitHub Star/Fork/Compile
如何使用
快速開始:
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);
}
/*設(shè)置累乘積數(shù)*/
public void setMultiplyNumber(int multiplyNumber) {
mMultiplyNumber = multiplyNumber;
}
/*設(shè)置累加積數(shù)*/
public void setAddNumber(int addNumber) {
mAddNumber = addNumber;
}
/*指定下一次選中的位置*/
public void setNextPick(int pickedPosition) {
mNextPickPosition = pickedPosition;
}
/*是否允許連續(xù)兩次出現(xiàn)同一個位置*/
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();
}
/*獲得當前條目數(shù)*/
public int getSize() {
return mOriginWeightList.size();
}
/*獲取歷史條目的位置列表*/
public ArrayList<Integer> getHistoryList() {
return mHistoryList;
}
/*上為配置參數(shù)*/
/*下為邏輯實現(xiàn)*/
/*獲得下一個隨機條目的位置*/
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);
}
/*執(zhí)行隨機算法*/
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;
}
// 預(yù)先算好下一次的比重
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;
}
}
每次調(diào)用next()的時候,都要做兩次for循環(huán)遍歷列表寞秃,列表里12首歌倒是毫無壓力斟叼,列表要是有500首歌的話肯定會延遲了,此處待改進春寿。
p.s. 如果你有更好的建議朗涩,請留言或者在GitHub fork并提交pull請求。