有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

基于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) 个答案

  1. # 1 楼答案

    看起来您的测试在您的平台上实现Random时发现了一个错误。根据documentation,在没有参数的情况下调用new Random()应该

    set the seed of the random number generator to a value very likely to be distinct from any other invocation of this constructor.

    实际上,这是通过向当前时间添加一个“随机化系数”(以纳秒为单位),然后根据一些简单的算法将随机化系数更改为一个新值来实现的。然而,具体的实现是特定于Java平台的

    由于在丢弃Random对象之前只获得第一个伪随机数,因此测试取决于不同随机算法的结果:与测试Random本身的好坏不同,您的算法实际上是在测试其随机种子选择器的好坏

    由于使Random成为static类的User成员解决了这个问题,并且由于在另一个平台(link to code on ideone showing different results)上的结果不同,似乎

    • 在平台上生成伪随机数的算法优于在平台上为伪随机数生成器生成种子的算法
    • 以上是特定于您的平台的,因为在其他平台上,种子生成器也是合理随机的