有偏随机数生成器-Java

有偏随机数生成器-Java,java,random,Java,Random,我正在寻找一个随机数生成器,它倾向于从一组已经选择的数字中给出“最远”的数字。例如,如果我的范围是[1,50],并且我传入一组数字,例如(1,20,40),那么我希望生成器“更喜欢”生成远离1,20和40的数字。因此,像50或30这样的数字比20或40更可能被抽取。我怀疑这可能已经存在。有人知道这样一个我可以用于Java的实现吗?听起来你想要一个,其中一些数字比其他数字更可能 你必须做出的决定是,随机分布曲线应该是什么样子的,也就是说,选择的数字避免的力度有多大 当你选择新号码时,你还需要决定这

我正在寻找一个随机数生成器,它倾向于从一组已经选择的数字中给出“最远”的数字。例如,如果我的范围是[1,50],并且我传入一组数字,例如(1,20,40),那么我希望生成器“更喜欢”生成远离1,20和40的数字。因此,像50或30这样的数字比20或40更可能被抽取。我怀疑这可能已经存在。有人知道这样一个我可以用于Java的实现吗?

听起来你想要一个,其中一些数字比其他数字更可能

你必须做出的决定是,随机分布曲线应该是什么样子的,也就是说,选择的数字避免的力度有多大

当你选择新号码时,你还需要决定这种回避会持续多久。例如,对于特定数量的拣选,先前的数字是否被同等地避免,还是会随着时间的推移逐渐消失

分布曲线示例



最后一个显示淡出,其中
40
是三个选择之前,
1
是两个选择之前,
20
是最后一个选择,其中数字
18
22
(含)下一个选择的概率为0%。

听起来像是你想要一个选择,其中一些数字比其他数字更可能

你必须做出的决定是,随机分布曲线应该是什么样子的,也就是说,选择的数字避免的力度有多大

当你选择新号码时,你还需要决定这种回避会持续多久。例如,对于特定数量的拣选,先前的数字是否被同等地避免,还是会随着时间的推移逐渐消失

分布曲线示例




最后一个显示淡出,其中
40
是三个选择之前,
1
是两个选择之前,
20
是最后一个选择,其中数字
18
22
(含)下一个选择的概率为0%。

以下是一种手动操作的方法。基本上,我们的想法是,我们接收一些我们不想生成的数字,然后我们生成一个随机数,如果这个数字在我们不想生成的数字列表中,我们将重试一次,最多重试一次

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;
    }
重试=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 
注意:我们允许算法重试的次数越多,我们的输出就越可能包含我们想要的数字。这允许您控制希望这些非首选号码出现的可能性。这是有意义的,因为如果我们将重试次数增加到无穷大,那么只有当生成的号码不包含在非首选号码列表中时,重试才会停止


希望这有帮助

这里有一种方法可以手工完成。基本上,我们的想法是,我们接收一些我们不想生成的数字,然后我们生成一个随机数,如果这个数字在我们不想生成的数字列表中,我们将重试一次,最多重试一次

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;
    }
重试=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 
注意:我们允许算法重试的次数越多,我们的输出就越可能包含我们想要的数字。这允许您控制希望这些非首选号码出现的可能性。这是有意义的,因为如果我们将重试次数增加到无穷大,那么只有当生成的号码不包含在非首选号码列表中时,重试才会停止


希望这有帮助

我最终做了一些不同的事情,发现以下代码很有用:

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]);
    }
}   
}
package util.math.random;
导入math.MersenneTwisterFast;
公共类SigmoidBiasedDistribution{
私人梅森快速mtf;
私用int min;
私人int max;
私有双指数指数因子;
专用双maxsidmoidscalefactor;
私人双maxoffsetfactor;
公共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;
}
公共整数[]获取点数(整数点数){
int[]pts=新的int[点];
双低偏移=mtf.nextDouble(真,真)*最大偏移系数;
double highoffset=mtf.nextDouble(真,真)*maxoffsetfactor;
双随机SigmoidScaleFactor=mtf.nextDouble(真,真)*(maxsidmoidscalefactor-minsigmoidscalefactor)+minsigmoidscalefactor;
System.out.println(随机SigmoidScaleFactor);
双点OFSET=((1-高偏移)-低偏移)/(点-1);
对于(int i=0;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());
    }
}
}
package util.math.random;
导入java.util.ArrayList;
导入java.util.List;
导入math.MersenneTwisterFast;
公共类围板