Mit nextGaussian() positive Zahlen erzeugen?

slitec

Mitglied
Hallo.

Gibt es eine Möglichkeit mit der Methode nextGaussian() positive Zahlen zu erzeugen ?
Leider muss ich die Methode nextGaussian() verwenden, da diese normalverteilte Zufallszahlen erzeugt.


Vielen Dank schon mal.
 
Mal ein Vergleich der Lozenzkurven, soweit ich diese verstanden habe, einmal per Halbnormalverteilung (rot = Absolutwert) und einmal per "Normalverteilung" (blau = verwerfen negativer Werte)

lorenz.png.
 
Das ist ja der Mist 🙂

Man hat einen Erwartungswert und die Standardabweichung. Ca. 69 % der normalverteilten Werte liegen im Bereich des Erwartungswerts +/- Standardabweichung. Verdoppelt man die Standardabweichung, liegen etwa 95 % der Werte in dem sich ergebenden Bereich. Man kann das Intervall aber beliebig groß wählen, man wird nie 100 % erreichen.
 
Man kann das Intervall aber beliebig groß wählen, man wird nie 100 % erreichen.

Mathe war schon immer meine schwache Seite, aber der Bereich kann ja nicht unendlich sein, weil er durch Double sowieso eingeschränkt wird. Da der TO nur positive Zahlen möchte, schränkt er diesen Bereich nochmal ein. Es ist also nur eine Frage der Anwendung, ob es ausreichend ist, wenn er den Erwartungswert in die Mitte des positiven Bereiches verschiebt.

Oder lieg ich da jetzt ganz falsch?
 
Zuletzt bearbeitet:
Ich könnte mir vorstellen, dass sich ein Verfahren finden lässt, das die in der Praxis geltenden Grenzen ausnutzt. Einfach den Erwartungswert nach Double.MAX_VALUE/2 zu verschieben, funktioniert allerdings nicht: aufgrund der Präzision von double würdest Du (fast) immer den gleichen Wert erhalten, nämlich Double.MAX_VALUE/2.
 
Java:
import java.util.Random;

public class Gaussian {
    public static void main(String[] args) {
        Random rand = new Random(1L);

        int count = 0;
        do {
            double value;
            do {
                value = rand.nextGaussian();
            } while (Math.abs(value) <= 1.0);
            System.out.println(value);
            count++;
        } while (count < 10);
    }
}

liefert

Code:
1.561581040188955
-1.0912278829447088
-1.1182832102556484
-1.6583217791337177
-1.8821643777572246
-1.160868794354026
2.8307323206471837
-1.3604870442090895
-1.1629624729268742
-1.3049338775280348
 
Dann setzte ich auf das falsche Pferd...
Aber was wäre damit:
Java:
public static double ng(double a, double b) {
	double eps = 0.001;
	if (!(a + eps < b)) {
		throw new IllegalArgumentException("a must be lower than b");
	}
	Random r = new Random();
	double g = r.nextGaussian();
	while (g < a || g > b) {
		g = r.nextGaussian();
	}
	return g;
}

public static void main(String[] args) {
	for (int i = 0; i < 20; i++) {
		System.out.println(ng(0.10, 0.11));
	}
}


Bei ng(10, 11) hält es allerdings nicht an...
 
Sollte aber klappen, den Erwartungswert in die Mitte des gewünschten Intervalls zu "schieben" und die Standardabweichung anzupassen (zB 1/6 der Interfvallgröße). Wenn man alles außerhalb liegende abschneidet, verliert man ein paar Promille (je nach Standardabweichung), aber nutzen lassen sollte es sich trotzdem.
 
Bei ng(10, 11) hält es allerdings nicht an...
Die Wahrscheinlichkeit, dass Du eine Zufallsvariable > 4,09 erhältst, ist nahezu 0. Siehe https://de.wikipedia.org/wiki/Standardnormalverteilungstabelle.

Sollte aber klappen, den Erwartungswert in die Mitte des gewünschten Intervalls zu "schieben" und die Standardabweichung anzupassen (zB 1/6 der Interfvallgröße). Wenn man alles außerhalb liegende abschneidet, verliert man ein paar Promille (je nach Standardabweichung), aber nutzen lassen sollte es sich trotzdem.
So sehe ich das auch. Wenn man nur eine Seite abschneidet, dürfte sich der Erwartungswert verschieben. Wenn man sich überlegt, dass es fast unmöglich ist, Werte jenseits +/- 5 zu erhalten, spielt das für Erwartungswerte > 5 praktisch keine Rolle.

Falls es jemanden interessiert, hier der Octave Code (Achtung: hingerotzt) für den Graphen von oben:
Code:
function retval = incomings(n,m,s)
    X=zeros(n,1);
    for i = 1:n
        do
           v = randn()*s+m;
        until (v >= 0)
        X(i) = round(v);
    endfor
    retval = X;
endfunction


I=incomings(10000, 1500, 500);
X=sort(I);
total=sum(X)
C=cumsum(X)/total;
Y=[0;accumarray(ceil((1:rows(C)) / 1000)(:), C)];

I=abs(randn(10000,1))*500+1500;
X=sort(I);
total=sum(X)
C=cumsum(X)/total;
Z=[0;accumarray(ceil((1:rows(C)) / 1000)(:), C)];
hf = figure()
hold on 
plot(0:0.1:1, Y/max(Y), "markersize", 10, "o-")
plot(0:0.1:1, Z/max(Z), "markersize", 10, "xr-")
hold off
print(hf, "lorenz.png", "-dpng")

Wenn ich mich nicht vertan habe, sollte das ein normalverteiltes Zufallseinkommen von 10.000 Personen simulieren mit einem Durchschnittseinkommen von 1.500 €, wobei knapp 70 % im Bereich zwischen 1.000 € und 2.000 € verdienen.

Aber Achtung: ich kenne mich mit dem Zeug echt nicht aus.
 
Etwa so sollte das klappen:

Java:
import java.util.*;

public class NormalVerteilung {

    private final double from;

    private final double to;

    private final Random random = new Random();

    public NormalVerteilung(final double from, final double to) {
        this.from = from;
        this.to = to;
    }

    public double next() {
        final double expectation = from + (to - from) / 2;
        final double stdDev = (to - from) / 6; //knapp 2‰ liegen dann außerhalb, uU anpassen

        double result;
        do {
            result = random.nextGaussian() * stdDev + expectation;
        } while (result < from || result > to);

        return result;
    }

    public static void main(String[] args) {
        final NormalVerteilung normalVerteilung = new NormalVerteilung(5, 10);
        for (int i = 0; i < 1000; i++) {
            System.out.println(normalVerteilung.next());
        }
    }

}
 
Achtung Mathematik-DAU: Wofür ist nochmal das "stdDev" nötig?

Achtung, auch Mathematik-DAU:

Hier hat @mihe7 den Hintergrund erklärt.

Man hat einen Erwartungswert und die Standardabweichung. Ca. 69 % der normalverteilten Werte liegen im Bereich des Erwartungswerts +/- Standardabweichung. Verdoppelt man die Standardabweichung, liegen etwa 95 % der Werte in dem sich ergebenden Bereich. Man kann das Intervall aber beliebig groß wählen, man wird nie 100 % erreichen.

Will man dann zB, dass 69% aller Werte im Intervall liegen, muss das Intervall zwei Standardabweichungen groß sein (da Mitte und Erwartungswerts +/- Standardabweichung)
Oder andersrum, wenn das Intervall fest ist, müsste die Standardabweichung so gewählt sein, dass sie genau zwei mal in's Intervall passt.


Mit 3 Standardabweichungen hat man über 99,7% der Werte, deshalb sind's oben die 1/6 des Intervalls.
 
Juhu, fertig 🙂
Java:
	public static double ng(double a, double b) {
		double eps = 0.001;
		if (!(a + eps < b)) {
			throw new IllegalArgumentException("a must be lower than b");
		}
		double c = -(b - a) / 2;
		double d = +(b - a) / 2;
		Random r = new Random();
		double g = r.nextGaussian();
		while (g < c || g > d) {
			g = r.nextGaussian();
		}
		return g + d + a;
	}

	public static void main(String[] args) {
		double d = 0;
		for (int i = 0; i < 10000; i++) {
			d += ng(0.10, 0.11) / 10000;
		}
		System.out.println(d);
		d = 0;
		for (int i = 0; i < 10000; i++) {
			d += ng(10, 11) / 10000;
		}
		System.out.println(d);
		d = 0;
		for (int i = 0; i < 10000; i++) {
			d += ng(-11, -10) / 10000;
		}
		System.out.println(d);
	}

Code:
0.10501...
10.502...
-10.497...

(Muss ich mir mal in meinem Utility Kasten speichern 🙂
 
Da ist keine Abweichung um 1

Kann höchstens noch ein Streckungsfaktor hinzufügen... Je nachdem wie weich oder hart die Parabel sein soll...
 
Er meint, dass nextGaussian() einen Zufallswert mit einer Standardabweichung von 1 (um den Wert 0 herum) liefert. Deine Methode macht zwei Dinge: erstens den Erwartungswert in die Mitte des Intervalls verschieben, zweitens nur Werte innerhalb des Intervalls zurückgeben. Wenn Du also z. B. ein Intervall von [0, 20] angibst, werden sich die Zufallswerte mit ebenfalls ca. 70 % im Intervall [9, 11] bewegen.
 
Aber entspricht ersterer Punkt nicht dieser "Kurve"?

Ja, eben genau dieser Kurve (bis ins unendliche je zu beiden Seiten). Du schneidest jetzt einfach links und rechts was ab. Bei „kleinem“ Intervall sieht das dann bei dir zB so aus:
9E4A1A4F-54E6-4512-9653-00C43F0347FE.jpeg


Das kann sicherlich sinnvoll sein, ist aber eben nur eine beschnittene Standardnormalverteilung.
 
Ok, das ist Intervallabhängig. Zum Beispiel wählt man -5 bis +5:
Java:
double[] d = new double[11];
for (int i = 0; i < 1000; i++) {
	int e = (int) Math.round(ng(-5, +5)) + 5;
	d[e] += 1.0 / 1000.0;
}

for (int i = 0; i < d.length; i++) {
	double e = d[i];
	System.out.println(((i - 5) + " " + (e)));
}

Code:
-5 0.0
-4 0.001
-3 0.009 (Nullen entfernt)
-2 0.067
-1 0.237
0 0.379
1 0.231
2 0.073
3 0.003
4 0.0
5 0.0

Das entspricht fast schon dieser Normalverteilung...

Möchte man das nicht, könnte man die Werte doch "strecken" (also auseinander-ziehen) mithilfe diese "Streckungsfaktors"?
 
Das entspricht fast schon dieser Normalverteilung...
Das hab ich nicht in Zweifel gezogen 😉

Aber nimm zb mal 0-1 (und skalier das für die Ausgabe auf 0-10), dann sieht man, was ich meine. Oder alternativ 0-100 (und skaliert auf 0-10).


Das soll keineswegs heißen, dass es falsch ist - man muss es nur wissen und beachten.
Je nach Anwendungsfall macht das eben Probleme.
 

Neue Themen


Zurück
Oben