我正在寻找一个随机数字生成器,它偏向于给出数字"最远的地方"来自一组已经选定的数字。例如,如果我的范围是[1,50]并且我传入了一组数字,例如(1,20,40),那么我希望生成器能够"更喜欢"产生数字远离1,20和40.因此,50或30之类的数字更可能被抽取而不是20或40.我怀疑这可能已经存在。有谁知道我可以用于Java的这种实现?
答案 0 :(得分:1)
听起来你想要一个weighted random distribution,其中一些数字比其他数字更有可能。
您必须做出的决定是随机分布曲线的外观,即所选数字的避免程度。
当您选择新号码时,您还需要决定避免持续多长时间。例如。对于特定数量的选秀权,前一个数字是否同样被避免,或者随着时间的推移逐渐消失?
分布曲线示例
最后一个显示淡出,其中40
前三次选择,1
前两次选择,20
是最后一次选择,其中数字18
为22
(包括)下一次选择的概率为0%。
答案 1 :(得分:1)
这是一种可以手动完成的方法。基本上我们的想法是我们接受一些我们不想生成的数字,然后我们生成一个随机数,如果该数字在数字列表中,我们不希望我们再次尝试最大数量重试。
public static void main(String[] args) {
int times = 25;
int[] listOfNumbers = {1, 2, 3};
int max = 5, min = 1;
while(times-- > 0)
{
System.out.print(GeneratePreferredNumbers(listOfNumbers, max, min) + " ");
}
}//main method
public static Integer GeneratePreferredNumbers(int[] listOfNotPreffered, int max, int min)
{
Random rand = new Random();
int randomNum;
int retry = 1; //increasing this lessons the likely of our non-preferred numbers to show up
HashSet<Integer> notPrefer = new HashSet<>();
//add all the numbers we don't want to generate into a HashSet for easy lookup
for(int index = 0; index < listOfNotPreffered.length; index++)
notPrefer.add(listOfNotPreffered[index]);
do {
randomNum = rand.nextInt((max - min) + 1) + min;
if(notPrefer.contains(randomNum))
{
retry--;
}
//we found a good value, let's return it
else{
retry = 0;
}
} while (retry > 0);
return randomNum;
}
输出:
重试= 0(只是随机)
2 2 4 4 4 2 1 2 2 4 3 3 4 5 4 4 1 5 3 2 1 2 3 3 1
重试= 1
1 2 5 3 3 4 3 1 2 1 4 1 3 3 1 1 5 3 5 4 2 1 3 4 5
重试= 2
3 3 2 4 4 2 2 1 4 5 5 5 4 2 1 4 5 1 4 5 1 4 4 2 2
重试= 3
5 5 5 5 4 4 4 4 2 4 5 5 1 4 5 4 3 5 4 4 4 5 3 1 2
重试= 4
5 4 5 4 4 4 5 5 4 4 5 1 5 2 5 5 5 2 4 5 5 2 4 4 4
重试= 5
4 4 4 4 4 4 4 4 5 5 4 4 5 4 5 4 4 5 4 5 4 5 5 5 5
注意:我们允许算法重试的次数越多,我们的输出就越有可能包含我们想要的数字。这使您可以控制希望显示非首选数字的可能性。这是有道理的,因为如果我们要将重试增加到无限,那么只有当生成的数字不包含在我们的列表非首选数字中时,这才会停止。
希望这有帮助!
答案 2 :(得分:0)
我最终做了一些不同的事情,发现以下代码很有用:
package util.math.random;
import math.MersenneTwisterFast;
public class SigmoidBiasedDistribution {
private MersenneTwisterFast mtf;
private int min;
private int max;
private double minsigmoidscalefactor;
private double maxsidmoidscalefactor;
private double maxoffsetfactor;
public SigmoidBiasedDistribution(int min, int max, double minsigmoidscalefactor, double maxsidmoidscalefactor, double maxoffsetfactor) {
this.mtf = new MersenneTwisterFast();
this.minsigmoidscalefactor = minsigmoidscalefactor;
this.maxsidmoidscalefactor = maxsidmoidscalefactor;
this.maxoffsetfactor = maxoffsetfactor;
this.min = min;
this.max = max;
}
public int[] getPoints(int points) {
int[] pts = new int[points];
double lowoffset = mtf.nextDouble(true, true) * maxoffsetfactor;
double highoffset = mtf.nextDouble(true, true) * maxoffsetfactor;
double randomsigmoidscalefactor = mtf.nextDouble(true, true) * (maxsidmoidscalefactor - minsigmoidscalefactor) + minsigmoidscalefactor;
System.out.println(randomsigmoidscalefactor);
double pointsoffset = ((1 - highoffset) - lowoffset) / (points - 1);
for(int i = 0; i < points; i++) {
double offset = (i * pointsoffset) + lowoffset;
double scalefactor = getHalfSigmoid(offset, randomsigmoidscalefactor);
pts[i] = (int) (scalefactor * (max - min)) + min;
}
return pts;
}
//sigmoid scale factor can range from -1 to 1 (-1 represents most aggressive initial slope, 1 represents least aggressive initial slope, 0 is linear)
//https://www.desmos.com/calculator/tswgrnoosy
public double getHalfSigmoid(double value, double sigmoidscalefactor) {
return (value - value * sigmoidscalefactor) / (sigmoidscalefactor - 2 * Math.abs(value) * sigmoidscalefactor + 1);
}
public static void main(String[] args) {
SigmoidBiasedDistribution sbd = new SigmoidBiasedDistribution(5, 80, 5, 0, 0.5, 0.2);
int[] pts = sbd.getPoints(3);
for(int i = 0; i < pts.length; i++) {
System.out.println(pts[i]);
}
}
}
基本上,代码只适合N个等距点到半S形曲线。或者,我也写了下面的解决方案,但我更喜欢上面的解决方案。
package util.math.random;
import java.util.ArrayList;
import java.util.List;
import math.MersenneTwisterFast;
public class WeightedRandomGenerator {
private double sigmoidscalefactor;
private MersenneTwisterFast mtf;
private List<Integer> points;
private int min;
private int max;
private int numcompletelyrandom;
public WeightedRandomGenerator(int min, int max, int numcompletelyrandom, double sigmoidscalefactor) {
this.mtf = new MersenneTwisterFast();
this.points = new ArrayList<Integer>();
this.numcompletelyrandom = numcompletelyrandom;
this.sigmoidscalefactor = sigmoidscalefactor;
this.min = min;
this.max = max;
clear();
}
public int nextInt() {
if(points.size() - 2 < numcompletelyrandom) {
int nextint = mtf.nextInt(max - min + 1) + min;
points.add(nextint);
return nextint;
}
else {
int maxsep = getMaxSeparation();
while(true) {
int nextint = mtf.nextInt(max - min + 1) + min;
int nearestneighbor = getNearestNeighbor(nextint);
double sepfrac = (double) nearestneighbor / (double) maxsep;
if(mtf.nextBoolean(getHalfSigmoid(sepfrac))) {
points.add(nextint);
return nextint;
}
}
}
}
private int getNearestNeighbor(int nextint) {
int delta = Integer.MAX_VALUE;
for(int i = 0; i < points.size(); i++) {
delta = Math.min(delta, Math.abs(nextint - points.get(i)));
}
return delta;
}
private int getMaxSeparation() {
int maxsep = 0;
for(int i = 1; i < points.size(); i++) {
int delta = points.get(i) - points.get(i-1);
maxsep = Math.max(delta, maxsep);
}
return maxsep;
}
private void clear() {
points.clear();
points.add(min);
points.add(max);
}
//sigmoid scale factor can range from -1 to 1 (-1 represents most aggressive initial slope, 1 represents least aggressive initial slope, 0 is linear)
//https://www.desmos.com/calculator/tswgrnoosy
public double getHalfSigmoid(double value) {
return (value - value * sigmoidscalefactor) / (sigmoidscalefactor - 2 * Math.abs(value) * sigmoidscalefactor + 1);
}
public static void main(String[] args) {
WeightedRandomGenerator wrg = new WeightedRandomGenerator(1, 80, 2, 0.95);
for(int i = 0; i < 5; i++) {
System.out.println(wrg.nextInt());
}
}
}