基于java的随机数单元测试算法
我的目标是测试一个类是否将其属性之一设置为随机整数值。我在网上找到了一个卡方检验的算法,并决定使用它。结果让我非常惊讶:我的样本量越大,测试通过的可能性就越小。我应该说,我绝不是一个统计专家(我问这个问题可能是不言而喻的),所以我可能在这里弄错了一些事情
仅对最终整数SIZE
(in UserTest
)进行变异时的测试结果。每次测试运行30次:
SIZE avg results
11 25.4 26, 25, 22, 24, 30
20 25 26, 26, 24, 22, 27
30 24 24, 22, 24, 26, 24
100 19.4 17, 23, 20, 18, 19
200 16.2 15, 18, 18, 15, 15
1000 13.2 13, 13, 14, 13, 13
10000 10 14, 7, 8, 10, 11
虽然在这种情况下,我并没有绝对必要拥有真正的随机性,但我仍然好奇问题是什么。这是一个错误的算法本身,我对它的错误使用,是“让测试更难”的自然结果(记住,统计noob),还是我在挑战Java伪随机生成器的边界
域类:
public class User
{
public static final int MINIT = 20;
public static final int MAXIT = 50;
private int iterations;
public void setIterations()
{
Random random = new Random();
setIterations(MINIT+random.nextInt(MAXIT-MINIT));
}
private void setIterations(int iterations) {
this.iterations = iterations;
}
}
测试课程:
public class UserTest {
private User user = new User();
@Test
public void testRandomNumbers() {
int results = 0;
final int TIMES = 30;
for(int i = 0; i < TIMES; i++)
{
if (randomNumbersRun())
{
results++;
}
}
System.out.println(results);
Assert.assertTrue(results >= TIMES * 80 / 100);
}
private boolean randomNumbersRun()
{
ArrayList<Integer> list = new ArrayList<Integer>();
int r = User.MAXIT - User.MINIT;
final int SIZE = 11;
for (int i = 0; i < r*SIZE; i++) {
user.setIterations();
list.add(user.getIterations());
}
return Statistics.isRandom(list, r);
}
}
卡方算法:
/**
* source: http://en.wikibooks.org/wiki/Algorithm_Implementation/Pseudorandom_Numbers/Chi-Square_Test
* changed parameter to ArrayList<Number> for generalization
*/
public static boolean isRandom(ArrayList<? extends Number> randomNums, int r) {
//According to Sedgewick: "This is valid if N is greater than about 10r"
if (randomNums.size() <= 10 * r) {
return false;
}
//PART A: Get frequency of randoms
Map<Number, Integer> ht = getFrequencies(randomNums);
//PART B: Calculate chi-square - this approach is in Sedgewick
double n_r = (double) randomNums.size() / r;
double chiSquare = 0;
for (int v : ht.values()) {
double f = v - n_r;
chiSquare += f * f;
}
chiSquare /= n_r;
//PART C: According to Swdgewick: "The statistic should be within 2(r)^1/2 of r
//This is valid if N is greater than about 10r"
return Math.abs(chiSquare - r) <= 2 * Math.sqrt(r);
}
/**
* @param nums an array of integers
* @return a Map, key being the number and value its frequency
*/
private static Map<Number, Integer> getFrequencies(ArrayList<? extends Number> nums) {
Map<Number, Integer> freqs = new HashMap<Number, Integer>();
for (Number x : nums) {
if (freqs.containsKey(x)) {
freqs.put(x, freqs.get(x) + 1);
} else {
freqs.put(x, 1);
}
}
return freqs;
}
}
# 1 楼答案
看起来您的测试在您的平台上实现
Random
时发现了一个错误。根据documentation,在没有参数的情况下调用new Random()
应该实际上,这是通过向当前时间添加一个“随机化系数”(以纳秒为单位),然后根据一些简单的算法将随机化系数更改为一个新值来实现的。然而,具体的实现是特定于Java平台的
由于在丢弃
Random
对象之前只获得第一个伪随机数,因此测试取决于不同随机算法的结果:与测试Random
本身的好坏不同,您的算法实际上是在测试其随机种子选择器的好坏由于使
Random
成为static
类的User
成员解决了这个问题,并且由于在另一个平台(link to code on ideone showing different results)上的结果不同,似乎