Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NextDistinct() for SingleGenerator and DoubleGenerator is biased for weak precisions and small ranges #56

Open
carusology opened this issue Aug 7, 2018 · 0 comments

Comments

@carusology
Copy link
Contributor

Our initial implementations of IDistinctGenerator.NextDistinct() for the SingleGenerator and DoubleGenerator use the following approach to get a distinct value:

public TFloat NextDistinct(TFloat other) {
    var minInterval = CalculateMinInterval(other);

    var value = this.Next();

    if (Math.Abs(value - other) < minInterval) {
        // In the event that the generated value is within +/- minimum interval of the
        // original, round it up or down to either other + minInterval or other - minInterval
        if (other <= this.High - minInterval) {
            if (other >= this.Low + minInterval) {
                return value > other ?
                    other + minInterval :
                    other - minInterval;
            } else {
                return other + minInterval;
            }
        } else {
            return other - minInterval;
        }
    } else {
        return value;
    }
}

This guarantees that the newly generated value is outside of the minimum interval required to be considered distinct based upon the generator's precision, but it has two flaws:

  1. If the precision is weak enough and the range is small enough, the generator will dutifully generate values outside of the range of this.Low and this.High instead of throw an UnableToGenerateException. An example could be a precision of 0.1 on a range from 0.0 to 0.1.
  2. The values between 1 minimum intervals away from the other value are more likely to be returned, as any generated value between 0 and 1 minimum intervals away from the other value are instead pushed ignored in favor of the minimum interval. If the precision is weak enough and the range is small enough (say 0.01 precision for a range of 0.00 to 1.00 for percentages), this bias is noticeable, though mostly harmless.

This aren't causing any major problems downstream, but there certainly two things that could be improved. Item (1) is a simple boundary check in the constructor, but (2) is trickier because the minimum interval differs if you're using significant figures for precision.

When I brought this issue up with @sflanker, he had an idea expressed this way:

public TFloat NextDistinct(TFloat other) {
    var current = this.Next(this.Low, this.High - GetMinInterval(this.High) * 2);

    if (other > current - GetMinInterval(current)) {
        other = other + (GetMinInterval(other) * 2);
    }

    return other;
}

That seems promising. 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant