Java 生成非均匀分布的随机整数数组
我想编写Java代码来生成[1,4]范围内的随机整数数组。数组的长度为N,在运行时提供。问题是范围[1,4]不是均匀分布的: 这意味着如果我创建N=100的数组,数字“1”将在数组中平均出现40次,数字“2”将出现10次,依此类推 目前,我正在使用此代码生成[1,4]范围内的均匀分布随机数:Java 生成非均匀分布的随机整数数组,java,arrays,random,probability-density,non-uniform-distribution,Java,Arrays,Random,Probability Density,Non Uniform Distribution,我想编写Java代码来生成[1,4]范围内的随机整数数组。数组的长度为N,在运行时提供。问题是范围[1,4]不是均匀分布的: 这意味着如果我创建N=100的数组,数字“1”将在数组中平均出现40次,数字“2”将出现10次,依此类推 目前,我正在使用此代码生成[1,4]范围内的均匀分布随机数: public static void main(String[] args) { int N; System.out.println(); Syst
public static void main(String[] args)
{
int N;
System.out.println();
System.out.print("Enter an integer number: ");
N = input.nextInt();
int[] a = new int[N];
Random generator = new Random();
for(int i = 0; i < a.length; i++)
{
a[i] = generator.nextInt(4)+1;
}
}
我如何用非均匀分布实现它,如上图所示 以下是一种方法,从您的代码开始:
public static void main(String[] args){
int N;
System.out.println();
System.out.print("Enter an integer number: ");
N = input.nextInt();
int[] a = new int[N];
Random generator = new Random();
for (int i = 0; i < a.length; i++) {
float n = generator.nextFloat();
if (n <= 0.4) {
a[i] = 1;
} else if (n <= 0.7) {
a[i] = 3;
} else if (n <= 0.9) {
a[i] = 4;
} else {
a[i] = 2;
}
}
}
更新:在@pjs的建议下,按照去密度概率的顺序选择数字,以便您倾向于提前退出if块
public static void main(String[] args){
int N;
System.out.println();
System.out.print("Enter an integer number: ");
N = input.nextInt();
int[] a = new int[N];
Random generator = new Random();
for (int i = 0; i < a.length; i++) {
float n = generator.nextFloat();
if (n <= 0.4) {
a[i] = 1;
} else if (n <= 0.7) {
a[i] = 3;
} else if (n <= 0.9) {
a[i] = 4;
} else {
a[i] = 2;
}
}
}
更新:在@pjs的建议下,按照去密度概率的顺序选择数字,以便您倾向于提前退出if块另一个简单的解决方案是使用nextDouble,它会生成一个随机双精度[0,1.如果值小于0.4,则选择1;如果值小于0.4+,则选择2,以此类推,最后一个分支始终选择最后一个选项。这很容易使用for循环进行推广。另一个简单的解决方案是使用nextDouble,它会生成一个随机双精度输入[0,1.如果值小于0.4,则选择1;如果值小于0.4+,则选择2,以此类推,最后一个分支始终选择最后一个选项。这很容易使用for循环进行推广。设a1、a2、a3和a4为指定相对概率的双倍数,s=a1+a2+a3+a4 这意味着1的概率是a1/s,2的概率是a2/s 然后使用generator.nextDouble创建一个随机双d 如果0设a1、a2、a3和a4为指定相对概率的双倍数,s=a1+a2+a3+a4 这意味着1的概率是a1/s,2的概率是a2/s 然后使用generator.nextDouble创建一个随机双d
如果0Miquel的扩展性稍高一点的版本以及Teresa的建议:
double[] distro=new double[]{.4,.1,.3,.2};
int N;
System.out.println();
System.out.print("Enter an integer number: ");
Scanner input = new Scanner(System.in);
N = input.nextInt();
int[] a = new int[N];
Random generator = new Random();
outer:
for(int i = 0; i < a.length; i++)
{
double rand=generator.nextDouble();
double val=0;
for(int j=1;j<distro.length;j++){
val+=distro[j-1];
if(rand<val){
a[i]=j;
continue outer;
}
}
a[i]=distro.length;
}
Miquel的一个稍微扩展的版本,也是Teresa建议的:
double[] distro=new double[]{.4,.1,.3,.2};
int N;
System.out.println();
System.out.print("Enter an integer number: ");
Scanner input = new Scanner(System.in);
N = input.nextInt();
int[] a = new int[N];
Random generator = new Random();
outer:
for(int i = 0; i < a.length; i++)
{
double rand=generator.nextDouble();
double val=0;
for(int j=1;j<distro.length;j++){
val+=distro[j-1];
if(rand<val){
a[i]=j;
continue outer;
}
}
a[i]=distro.length;
}
对于更通用的方法,可以使用分布概率填充NavigableMap:
double[] probs = {0.4, 0.1, 0.2, 0.3};
NavigableMap<Double, Integer> distribution = new TreeMap<Double, Integer>();
for(double p : probs) {
distribution.put(distribution.isEmpty() ? p : distribution.lastKey() + p, distribution.size() + 1);
}
要查询地图,首先生成范围为0到1的均匀分布双精度。使用ceilingEntry方法查询地图并传递随机数将返回,例如传递范围为2的值。使用返回地图条目上的getValue将返回2。对于更通用的方法,可以填充Navig分布概率为:
double[] probs = {0.4, 0.1, 0.2, 0.3};
NavigableMap<Double, Integer> distribution = new TreeMap<Double, Integer>();
for(double p : probs) {
distribution.put(distribution.isEmpty() ? p : distribution.lastKey() + p, distribution.size() + 1);
}
要查询地图,首先生成一个范围为0到1的均匀分布双精度。使用ceilingEntry方法查询地图并传递随机数将返回值,例如传递范围为2的值。因此,在返回的地图条目上使用getValue将返回2。对于上面给出的特定问题,解决方案由其他人提供的s工作得很好,可能会有些过分。但是,您在评论中说,您实际上打算在范围更大的发行版中使用它。在这种情况下,设置别名表的开销可能值得获得O1行为以实际生成值 以下是Java的源代码。如果您不想使用Mersenne Twister,可以很容易地将其恢复为使用Java的stock Random:
/*
* Created on Mar 12, 2007
* Feb 13, 2011: Updated to use Mersenne Twister - pjs
*/
package edu.nps.or.simutils;
import java.lang.IllegalArgumentException;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.Stack;
import java.util.PriorityQueue;
import java.util.Random;
import net.goui.util.MTRandom;
public class AliasTable<V> {
private static Random r = new MTRandom();
private static DecimalFormat df2 = new DecimalFormat(" 0.00;-0.00");
private V[] primary;
private V[] alias;
private double[] primaryP;
private double[] primaryPgivenCol;
private static boolean notCloseEnough(double target, double value) {
return Math.abs(target - value) > 1E-10;
}
/**
* Constructs the AliasTable given the set of values
* and corresponding probabilities.
* @param value
* An array of the set of outcome values for the distribution.
* @param pOfValue
* An array of corresponding probabilities for each outcome.
* @throws IllegalArgumentException
* The values and probability arrays must be of the same length,
* the probabilities must all be positive, and they must sum to one.
*/
public AliasTable(V[] value, double[] pOfValue) {
super();
if (value.length != pOfValue.length) {
throw new IllegalArgumentException(
"Args to AliasTable must be vectors of the same length.");
}
double total = 0.0;
for (double d : pOfValue) {
if (d < 0) {
throw new
IllegalArgumentException("p_values must all be positive.");
}
total += d;
}
if (notCloseEnough(1.0, total)) {
throw new IllegalArgumentException("p_values must sum to 1.0");
}
// Done with the safety checks, now let's do the work...
// Cloning the values prevents people from changing outcomes
// after the fact.
primary = value.clone();
alias = value.clone();
primaryP = pOfValue.clone();
primaryPgivenCol = new double[primary.length];
for (int i = 0; i < primaryPgivenCol.length; ++i) {
primaryPgivenCol[i] = 1.0;
}
double equiProb = 1.0 / primary.length;
/*
* Internal classes are UGLY!!!!
* We're what you call experts. Don't try this at home!
*/
class pComparator implements Comparator<Integer> {
public int compare(Integer i1, Integer i2) {
return primaryP[i1] < primaryP[i2] ? -1 : 1;
}
}
PriorityQueue<Integer> deficitSet =
new PriorityQueue<Integer>(primary.length, new pComparator());
Stack<Integer> surplusSet = new Stack<Integer>();
// initial allocation of values to deficit/surplus sets
for (int i = 0; i < primary.length; ++i) {
if (notCloseEnough(equiProb, primaryP[i])) {
if (primaryP[i] < equiProb) {
deficitSet.add(i);
} else {
surplusSet.add(i);
}
}
}
/*
* Pull the largest deficit element from what remains. Grab as
* much probability as you need from a surplus element. Re-allocate
* the surplus element based on the amount of probability taken from
* it to the deficit, surplus, or completed set.
*
* Lather, rinse, repeat.
*/
while (!deficitSet.isEmpty()) {
int deficitColumn = deficitSet.poll();
int surplusColumn = surplusSet.pop();
primaryPgivenCol[deficitColumn] = primaryP[deficitColumn] / equiProb;
alias[deficitColumn] = primary[surplusColumn];
primaryP[surplusColumn] -= equiProb - primaryP[deficitColumn];
if (notCloseEnough(equiProb, primaryP[surplusColumn])) {
if (primaryP[surplusColumn] < equiProb) {
deficitSet.add(surplusColumn);
} else {
surplusSet.add(surplusColumn);
}
}
}
}
/**
* Generate a value from the input distribution. The alias table
* does this in O(1) time, regardless of the number of elements in
* the distribution.
* @return
* A value from the specified distribution.
*/
public V generate() {
int column = (int) (primary.length * r.nextDouble());
return r.nextDouble() <= primaryPgivenCol[column] ?
primary[column] : alias[column];
}
public void printAliasTable() {
System.err.println("Primary\t\tprimaryPgivenCol\tAlias");
for(int i = 0; i < primary.length; ++i) {
System.err.println(primary[i] + "\t\t\t"
+ df2.format(primaryPgivenCol[i]) + "\t\t" + alias[i]);
}
System.err.println();
}
}
对于您上面给出的特定问题,其他人提供的解决方案效果很好,可能会有些过头。但是,您在评论中说,您实际上将在范围更大的发行版中使用此解决方案。在这种情况下,设置别名表的开销可能值得为实际的g获得O1行为生成价值 以下是Java的源代码。如果您不想使用Mersenne Twister,可以很容易地将其恢复为使用Java的stock Random:
/*
* Created on Mar 12, 2007
* Feb 13, 2011: Updated to use Mersenne Twister - pjs
*/
package edu.nps.or.simutils;
import java.lang.IllegalArgumentException;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.Stack;
import java.util.PriorityQueue;
import java.util.Random;
import net.goui.util.MTRandom;
public class AliasTable<V> {
private static Random r = new MTRandom();
private static DecimalFormat df2 = new DecimalFormat(" 0.00;-0.00");
private V[] primary;
private V[] alias;
private double[] primaryP;
private double[] primaryPgivenCol;
private static boolean notCloseEnough(double target, double value) {
return Math.abs(target - value) > 1E-10;
}
/**
* Constructs the AliasTable given the set of values
* and corresponding probabilities.
* @param value
* An array of the set of outcome values for the distribution.
* @param pOfValue
* An array of corresponding probabilities for each outcome.
* @throws IllegalArgumentException
* The values and probability arrays must be of the same length,
* the probabilities must all be positive, and they must sum to one.
*/
public AliasTable(V[] value, double[] pOfValue) {
super();
if (value.length != pOfValue.length) {
throw new IllegalArgumentException(
"Args to AliasTable must be vectors of the same length.");
}
double total = 0.0;
for (double d : pOfValue) {
if (d < 0) {
throw new
IllegalArgumentException("p_values must all be positive.");
}
total += d;
}
if (notCloseEnough(1.0, total)) {
throw new IllegalArgumentException("p_values must sum to 1.0");
}
// Done with the safety checks, now let's do the work...
// Cloning the values prevents people from changing outcomes
// after the fact.
primary = value.clone();
alias = value.clone();
primaryP = pOfValue.clone();
primaryPgivenCol = new double[primary.length];
for (int i = 0; i < primaryPgivenCol.length; ++i) {
primaryPgivenCol[i] = 1.0;
}
double equiProb = 1.0 / primary.length;
/*
* Internal classes are UGLY!!!!
* We're what you call experts. Don't try this at home!
*/
class pComparator implements Comparator<Integer> {
public int compare(Integer i1, Integer i2) {
return primaryP[i1] < primaryP[i2] ? -1 : 1;
}
}
PriorityQueue<Integer> deficitSet =
new PriorityQueue<Integer>(primary.length, new pComparator());
Stack<Integer> surplusSet = new Stack<Integer>();
// initial allocation of values to deficit/surplus sets
for (int i = 0; i < primary.length; ++i) {
if (notCloseEnough(equiProb, primaryP[i])) {
if (primaryP[i] < equiProb) {
deficitSet.add(i);
} else {
surplusSet.add(i);
}
}
}
/*
* Pull the largest deficit element from what remains. Grab as
* much probability as you need from a surplus element. Re-allocate
* the surplus element based on the amount of probability taken from
* it to the deficit, surplus, or completed set.
*
* Lather, rinse, repeat.
*/
while (!deficitSet.isEmpty()) {
int deficitColumn = deficitSet.poll();
int surplusColumn = surplusSet.pop();
primaryPgivenCol[deficitColumn] = primaryP[deficitColumn] / equiProb;
alias[deficitColumn] = primary[surplusColumn];
primaryP[surplusColumn] -= equiProb - primaryP[deficitColumn];
if (notCloseEnough(equiProb, primaryP[surplusColumn])) {
if (primaryP[surplusColumn] < equiProb) {
deficitSet.add(surplusColumn);
} else {
surplusSet.add(surplusColumn);
}
}
}
}
/**
* Generate a value from the input distribution. The alias table
* does this in O(1) time, regardless of the number of elements in
* the distribution.
* @return
* A value from the specified distribution.
*/
public V generate() {
int column = (int) (primary.length * r.nextDouble());
return r.nextDouble() <= primaryPgivenCol[column] ?
primary[column] : alias[column];
}
public void printAliasTable() {
System.err.println("Primary\t\tprimaryPgivenCol\tAlias");
for(int i = 0; i < primary.length; ++i) {
System.err.println(primary[i] + "\t\t\t"
+ df2.format(primaryPgivenCol[i]) + "\t\t" + alias[i]);
}
System.err.println();
}
}
通过按概率的降序进行搜索,您可以稍微提高效率,因此提前终止的可能性更大。相应的阈值分别为[0.4,0.7,0.9,否则]以返回[1,3,4,2]。@Miquel您需要返回[1,3,4,2]中的数字为了符合pjs的想法。谢谢@Duncan my bad。现在修复。从均匀分布生成离散非均匀分布的好主意。答案已被接受。我只需要使用@Teresa和Vandale循环方法进一步改进代码。通过按概率的降序搜索,您可以稍微提高效率因此提前终止的可能性更大。相应的阈值分别为[0.4,0.7,0.9,else]以返回[1,3,4,2]。@Miquel您需要返回[1,3,4,2]中的数字为了符合pjs的想法而订购。谢谢@Duncan my bad。现在修复。从均匀分布生成离散非均匀分布的好主意。回答a
接受。我只需要使用@Teresa和Vandale循环方法来进一步改进代码。注意:不需要声明int N;方法顶部。侧注可能重复:无需声明int N;在方法的顶部。可能重复的非常好。此外,@pjs的建议仍然适用。您可以将distro转换为一个映射,并按降序概率排序,因此您可以在前面点击continue+1。我必须使用loop,因为我的实际射程远大于[1,4],非常好。此外,@pjs的建议仍然适用。您可以将distro转换为一个映射,并按降序概率排序,因此您可以在前面点击continue+1。我必须使用循环,因为我的实际射程远大于[1,4]+1。我不完全理解你的方法,但会调查的。它似乎比其他的短得多。tnxsame的想法和出色的实现。由于这一点,我不必手动排序分布值。+1。我不完全理解你的方法,但会调查的。它似乎比其他的短得多。tnxsame的想法和出色的实现。由于这一点,我不必手动排序分布值。我不知道别名方法,并且发现它非常有趣,谢谢!如果有人想阅读它,维基百科的页面是相当不完整的,所以你可能想检查我不知道别名方法,并发现它真的很有趣,谢谢!如果有人想阅读,维基百科的页面是相当不完整的,所以你可能想检查一下