想法:
伪随机。
你的音乐列表里有一些歌,每首歌的初始随机因数为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及实现
Demo中的的大图截图自网易云音乐。
前往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);
}
/*设置累乘积数*/
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;
}
}
每次调用next()的时候,都要做两次for循环遍历列表,列表里12首歌倒是毫无压力,列表要是有500首歌的话肯定会延迟了,此处待改进。
p.s. 如果你有更好的建议,请留言或者在GitHub fork并提交pull请求。