Zufallswert mit Häufung in Intervall bestimmen

Moin, ich hab die Frage auch in einem C+±Forum gestellt, da ist sie aber in Beleidigungen ausgeartet…

Ich zeige euch mal den Code:

    /**
     * @param from lower bound
     * @param to   upper bound
     * @param a    factor between 0.0 and 1.0
     * @return     a random double value in bounds
     */
    public static double getRandomDouble(double from, double to, double a) {
        Random random = new Random();
        double g;
        do {
            g = (random.nextGaussian() + 1.0) / 2.0;
        } while (g < 0 || g > 1);
        if (g < 0.5) {
            g += (0.5 - g) * a * a;
        } else if (g > 0.5) {
            g -= (g - 0.5) * a * a;
        }
        return g * (to - from) + from;
    }

Ein Aufruf sieht dann beispielsweise so aus: getRandomDouble(5, 10, 0.1) == ein Wert zwischen 5 und 10 mit einer Häufung bei 7,5 und mit einer „etwas nach oben gezogenen Kurve“.

Ich finde die do-while-Schleife und das Verwerfen unschön. Lässt sich das vielleicht umgehen?

Wenn Du nichts verwerfen willst musst Du den Random-Wert auf das Intervall (-2,5; 2,5) aufweiten und dann den Mittelwert zu Deinem Zielwert verschieben:

g = 7.5 + 2.5* random.nextGaussian();

Was Du mit "etwas nach oben gezogenen Kurve“ meinst ist mir nicht klar.

bye
TT

Danke für deine Antwort! Das Problem ist, dass random.nextGaussian() theoretisch keine Beschränkung der Werte hat, also da kann zum Beispiel auch -999 bei herauskommen…

Die „normale“ Glockenkurve verläuft ungefähr so:
https://www.wolframalpha.com/input/?i=-x%5E2%2B1

Nach oben gezogen:
https://www.wolframalpha.com/input/?i=-x%5E2*2%2B2

Nach unten gezogen:
https://www.wolframalpha.com/input/?i=-x%5E2%2F2%2B0.5

Stell dir das wie einen Gummiball vor, den man drücken oder pressen kann…

Sorry, aber 'ne Normalverteilung mit 'ner Potenzfunktion zu vergleichen funktioniert nicht. Sieht man schon daran, die die Kurven, die Du da verlinkt hast, absolut identisch sind (kongruent) nur die Skala anders ist. Das klappt bei einer Verteilung aber nicht, weil das Maximum kein konkreter Wert sondern ein „Count“ ist und von der Anzahl der Sampels abhängt.

bye
TT

Was!? Ein Thread, den du eröffnest, und der dann in Beleidigungen ausartet? Das kann ich mir gar nicht vorstellen. Wo du doch immer so sachlich und themenfokussiert bist, vernünftige Fragen stellst, Eigeninitiative zeigst, und nie persönlich angreifend wirst. In diesem Forum, von dem du da redest, müssen dann ja schon seeeehr, sehr viele dieser „Geisterfahrer“ unterwegs sein.

Aber Einstiegspunkte:

Danke für die ganze Links! Dann könnte g = (random.nextGaussian() + 9.0) / 18.0; durchaus funktionieren… Aber dann werden Randwerte äußerst selten getroffen.

@Marco13 Hast du denn wenigstens verstanden, wozu der double a-Parameter da ist? Ich hab mir das zwar (sorgfältig) überlegt, aber so richtig weiß ich nicht Bescheid. :smiley:

Was? Du? Neeeein. Du bist doch der Experte hier.
Na gut, ich lass’ das mal.

Der Parameter a war vermutlich gedacht für „Ich rechne mal irgendwas rum, und ändere so lange an meinem Code, bis das richtige rauskommt“…

Wann man sich mal die Funktionen von Apache Math anschaut, sieht man, dass (ironischerweise) die BetaDistribution dem, was du suchst, recht nahe kommen könnte.

Hier ist mal ein Histogram mit 10000 Zahlen, die diese Funktion bei verschiedenen Werten für Alpha/Beta liefert:

RandomHistogram

Und wenn beide 2.0 sind, ist das die gewünschte Form: Ein Wert zwischen 0 und 1, mit einer Häufung bei 0.5. Das ganze dann auf einen Wert zwischen 5 und 10 mit einer Häufung bei 7.5 umzurechnen bleibt dem geneigten Leser als Übung überlassen.

Ja, aber weshalb…

Und die umgekehrte Form könnte ja auch entstehen:

Welchen Teil von Beta distribution - Wikipedia soll ich da jetzt ausbreiten?

BetaDistribution d = new BetaDistribution(2.0, 2.0);
for (int i=0; i<numElements; i++)
{
    double v = d.sample();
    double result = 5.0 + v * (10.0 - 5.0);
    ...
}

liefert „das gewünschte Ergebnis“. Was noch?

@Marco13 Entscheide du, was näher an der Parabelform wäre:

import org.apache.commons.math3.distribution.BetaDistribution;

import java.util.Arrays;
import java.util.Random;
import java.util.stream.IntStream;

public class Test1 {
    private static final int n = 10000;

    /**
     * @param from lower bound
     * @param to   upper bound
     * @param a    factor between 0.0 and 1.0
     * @return a random double value in bounds
     */
    public static double getRandomDouble(double from, double to, double a) {
        Random random = new Random();
        double g;
        do {
            g = (random.nextGaussian() + 1.0) / 2.0;
        } while (g < 0 || g > 1);
        if (g < 0.5) {
            g += (0.5 - g) * a * a;
        } else if (g > 0.5) {
            g -= (g - 0.5) * a * a;
        }
        return g * (to - from) + from;
    }

    public static double getRandomDouble2(double from, double to, double a) {
        double v = new BetaDistribution(2, 2).sample();
        if (v < 0.5) {
            v += (0.5 - v) * a * a;
        } else if (v > 0.5) {
            v -= (v - 0.5) * a * a;
        }
        return v * (to - from) + from;
    }

    public static double f(int x) {
        double x2 = (x - n / 2.0) / n;
        double y = -x2 * x2 * 4 + 1;
        return y * 2.0;
    }

    public static void main(String[] args) {
        double[] a = IntStream.range(0, n).mapToDouble(i -> getRandomDouble(20, 30, 0)).toArray();
        double[] b = IntStream.range(0, n).mapToDouble(i -> getRandomDouble2(20, 30, 0)).toArray();
        double[] c = IntStream.range(0, n).mapToDouble(Test1::f).toArray();
        int[] a2 = new int[10];
        int[] b2 = new int[10];
        int[] c2 = new int[10];
        IntStream.range(0, n).forEach(i -> {
            a2[(int) (a[i] - 20)]++;
            b2[(int) (b[i] - 20)]++;
        });
        for (int i = 0; i < 10; i++) {
            double sum = 0;
            for (int j = 0; j < n / 10; j++) {
                sum += c[i * (n / 10) + j];
            }
            c2[i] = (int) sum;
        }
        System.out.println("a2 = " + Arrays.toString(a2));
        System.out.println("b2 = " + Arrays.toString(b2));
        System.out.println("c2 = " + Arrays.toString(c2));
    }
}
n=100:
a2 = [5, 10, 3, 11, 13, 14, 18, 12, 7, 7]
b2 = [5, 11, 7, 10, 15, 15, 14, 16, 3, 4]
c2 = [3, 9, 14, 18, 19, 19, 18, 15, 10, 4]

n=10000:
a2 = [814, 896, 1010, 1091, 1176, 1167, 1136, 1044, 903, 763]
b2 = [284, 760, 1128, 1395, 1447, 1478, 1335, 1121, 756, 296]
c2 = [372, 1013, 1493, 1813, 1973, 1973, 1813, 1493, 1013, 373]

Was wurschtelst du denn da noch mit dem rum, was die Beta-Funktion liefert? :confused:
Aber eigentlich egal: Wenn du meinst, mit solchem Rumgerechne irgendein sinnvolles Ergebnis zu bekommen, dann rechne halt so rum…

Wollte eigentlich nur wissen, ob die Beta-Funktion tatsächlich die Gestalt einer Parabel hat. Das scheint so zu sein.