Nächste Zufallsfließkommazahl in [0, 1] ermitteln?

Hallo, wisst ihr vielleicht, warum das Programm nicht anhalten mag, oder wo sich der Denkfehler versteckt?

public static float getnf() {
	return (float) (new Random().nextInt() >>> 1) / (float) Integer.MAX_VALUE;
}

public static void main(String[] args) {
	float min = 1;
	float max = 0;
	while (true) {
		float f = getnf();
		if (f < min)
			min = f;
		if (f > max)
			max = f;
		if (f <= 0 || f >= 1) {
			break;
		}
		System.out.println(f + " " + min + " " + max);
	}
}

(StackOverflow liefert zu dem Thema nix… In C wäre das wohl möglich) :slightly_frowning_face:

Das hält schon an, musst nur lang genug warten.

Hält zB an, wenn nextInt 1 zurückgibt.

Aber okay, ich geh dann mal meine Baustelle suchen… :man_shrugging:

Oh Shit, ich muss das revidieren, @mrBrown hatte recht, denn das hält zum Beispiel bei 1.0 an:

import java.util.Random;

public class R1 {
	static Random r = new Random(1000); // hält bei 1.0 an

	public static float getnf1() {
		return (float) (r.nextInt() >>> 1) / (float) Integer.MAX_VALUE;
	}

	public static float getnf2() {
		return Float.intBitsToFloat(Float.floatToIntBits(1f) + 1) / (float) Integer.MAX_VALUE
				* (float) (r.nextInt() >>> 1);
	}

	public static void main(String[] args) {
		float min = 1;
		float max = 0;
		while (true) {
			float f = getnf1();
			if (f < min)
				min = f;
			if (f > max)
				max = f;
			System.out.println(f + " " + min + " " + max);
			if (f <= 0 || f >= 1) {
				break;
			}
		}
	}
}

Ist der Code korrekt oder gibt es irgendwelche „Threats“?

Hi,
da du direkt danach fragst, möchte ich auch deinen Code einer normalen Review unterziehen:

  • Die Importe sind mit angegeben aber dass package nicht. Ich gehe davon aus, dass es keine Paketstruktur gibt. Dies ist in den aller meisten Fällen schlechter Stiel und wird bereits in mittleren und großen Projekten schnell unübersichtlich.

  • public class R1 {
    Die Benennung der Klasse ist extrem schlecht. Man kann nicht erkennen, was die Klasse tut oder in welchem Kontext sie eingeordnet ist. Mangels Paket, kann man das nichtmal versuchen abzuleiten. Das gilt gleichermaßen für fast alle deine Variablen und Methodenbezeichner.

  • static Random r = new Random(1000); // hält bei 1.0 an
    Das ist zum formellen auch fachlich falsch. Zum einen fehlt der acceess modifier. Zum anderen hält das hier nicht bei 1.0 an. Warum auch?

  • return (float) (r.nextInt() >>> 1) / (float) Integer.MAX_VALUE;
    Das hier liefert deswegen 10, weil durch den BIT-Shift aus -99 annährend MAX_VALUE erreicht wird. Durch einen Rundungsfehler kommt 1.0 raus. Aus dem BIT-Shift ergeben sich auch negative Zahlen, weil das höchste BIT bei „>>>“ nicht beachtet wird, bzw. mitverschoben wird.

  • public static float getnf2() {
    Es ist komplett unklar, was diese Methode im Quellcode soll. Sie wird nicht benutzt. Das Ergebnis ist ähnlich skurill, wie das der ähnlich benannten. Es wird auch nicht beschrieben, was der Zweck sein soll.

Insgesammt versuche ich den tieferen Grund deiner Quellcodes zu entschlüsseln, aber leider ist das extrem schwer. Java und andere Programmiersprachen haben so tolle Konzepte und Frameworks mit denen man sich auseinander setzen kann.
Allen voran OOP. Du postest hier schon sol lange tollen Quellcode, aber ich vermisse da irgentwie den Progress.
Ich mein wofür ist das ganze? Welche Probleme zeigt es auf? Was ist der Sinn dahiner?

Ich gehe nicht davon aus, dass du das deswegen lernst, weil du es für den Beruf brauchst. Maximal um ein mathemtische Fragen zu klären, aber dafür sind die Postings doch recht oberflächlich.

My two cents.
Gruß,
Martin

1 „Gefällt mir“

das ist richtig. Hast du die Überschrift gelesen? Es geht um ein mathematisches Problem, also eigentlich nur um eine Zeile, und nicht um ein komplettes Review.

Ein Beispiel, @CyborgBeta : Das folgende Programm soll ein magisches Quadrat ausgeben:

class S
{
    public static void main(String args[])
    {
        int n = 5;
        int a[][] = new int[n][n];
        f(n,1,n/2,n-1,a);
        print(a);
    }

    static int f(int ì,int i,int í,int î,int ï[][])
    {
        return i>ì*ì?i-1:(ï[í][î]=f(ì,i+2,(í+(i%ì==0?0:1))%ì,(î+(i%ì==0?-1:1))%ì,ï))-1;
    }

    public static void print(int a[][])
    {
        for (int i=0; i<a.length; i++)
        {
            for (int j=0; j<a[i].length; j++)
            {
                System.out.print((a[j][i]<10?" ":"")+a[j][i]+" ");
            }
            System.out.println();
        }
    }
}

Das tut es aber nicht. Im geposteten code ist nur ein byte (!) falsch.

Los, such! :dog:

1 „Gefällt mir“

Ich kenn mich mit dem Zirkumflex nicht so aus… :cold_face: Hab doch schon versucht, alles auf das wirklich Wesentliche zu beschränken, also ein SSCCE zu schreiben.

@Marco13 : Siehst du ein Problem in der Umsetzung der Zeile?

Edit: Da ist möglicherweise eine schließende Klammer zu viel…(?)

In diesem Fal lautet die Antwort:

Die Lösung ist fasch, denn es gibt einen wesentlich einfachereren sauberen Weg Floats zu berechnen.
Schöne Grüße
Martin

Ja, woran denkst du da?

funktional vermutlich richtig, aber vermutlich zu kompliziert - man kann eigentlich nicht sagen, eine Lösung sei falsch, ohne gleichzeitig eine bessere Alternative zu nennen - und diese seh ich bislang nicht. :slightly_frowning_face:

Nun, die ursprüngliche Frage war recht unspezfisch, aber jetzt „hält es ja an“ - also ist das Problem gelöst.

Oder?

Oder?

(das ist eine Suggestivfrage…)

Sie hält vermutlich immer mit der richtigen Lösung an, ja.

Was ist eine Suggestivfrage?

Ich kann auch anders fragen, wie feingranular ist die Division und haben alle möglichen Werte die (annähernd) gleiche Wahrscheinlichkeit?

Durch einen ganz einfachen Versuch hab ich jetzt herausgefunden, dass diese Methode (mathematisch gesehen) nicht „genau genug“ ist.

Beispiel:

import java.util.Random;

public class R1 {
	static Random r = new Random(1000); // hält bei 1.0 an

	public static float getnf1() {
		return (float) (r.nextInt() >>> 1) / (float) Integer.MAX_VALUE;
	}

	public static float getnf2() {
		return r.nextFloat();
	}

	public static float getpi1(int n) {
		int treffer = 0;
		for (int i = 0; i < n; i++) {
			float x = getnf1();
			float y = getnf1();
			if (x * x + y * y <= 1) {
				treffer++;
			}
		}
		return (float) ((double) treffer / (double) n);
	}

	public static float getpi2(int n) {
		int treffer = 0;
		for (int i = 0; i < n; i++) {
			float x = getnf2();
			float y = getnf2();
			if (x * x + y * y <= 1) {
				treffer++;
			}
		}
		return (float) ((double) treffer / (double) n);
	}

	public static void main(String[] args) {
		System.out.println(Math.PI / 4.0);
		float p1 = getpi1(10000);
		float p2 = getpi2(10000);
		System.out.println(p1 + " " + Math.abs(Math.PI / 4.0 - p1) + " " + (1 + (Math.PI / 4.0 - p1) / (Math.PI / 4.0)));
		System.out.println(p2 + " " + Math.abs(Math.PI / 4.0 - p2) + " " + (1 + (Math.PI / 4.0 - p2) / (Math.PI / 4.0)));
	}
}
0.7853981633974483
0.7812 0.004198171694414832 1.0053452782169168
0.7878 0.002401850621564172 0.9969418688080778

Die eigene Methode weicht um 0.0042 vom gewünschten Ergebnis ab, und #nextFloat() weicht um 0.0024 vom gewünschten Ergebnis ab (das ist weniger).

Hast du dir mal angeschaut wie das alles implementiert ist? Wenn ich dich richtig verstehe, dann geht es dir ja darum, dass bei Random#nextFloat() normalerweise 1 exklusive ist, du möchtest aber 1 inklusive haben. Richtig?

Random#nextFloat() ist so implementiert: Mantisse * 2^-24, wobei für die Mantisse eine Pseudozufallszahl zwischen 0 und 2^24 (exklusive) genommen. Damit nun 1 dabei ist, musst du die Obergrenze für die Mantisse auf 2^24 + 1 setzten. Folgender Code sollte gehen:

public class Test {

  private static final Random RANDOM = new Random();
  private static final int UPPERBOUND = 0b00000001_00000000_00000000_00000000;  

  public static void main(String[] args) {
    float f = 0;
    
    while((f = randomFloat()) != 1) {
      System.out.println(f);
    }
    
    System.out.println(f);
  }

  public static float randomFloat() {
    int random = RANDOM.nextInt(UPPERBOUND + 1);
    return random / ((float) (1 << 24));
  }
}

Dieser Code spuckt auf jeden Fall IRGENDWANN mal 1.0 aus weil 0b00000001_00000000_00000000_00000000 * 2 ^ -24 = 1.0 ist.

Ja, Richtig. Danke für deinen Code jedenfalls.