Negative Zahlen als Fakultät ablehnen

Ich möchte wenn man bei der Fakultät eine negative Zahl eingibt den Benutzer wieder daraufhinweisen dass er falsch liegt, dass gar nicht geht und wieder am Anfang hin bringen um nicht wie bei mir das ganze Programm zu returnen

     
     
    public class Fakultaet 
    {
    	public static void main (String args[])
    	{
    		int number1 = 0;
    		int fakultaet = 1;
    		Scanner scan = new Scanner(System.in);
    		System.out.print("!");
    		number1 = scan.nextInt();
    		while (number1 < 0)
    		{
    			System.out.println("Im Minusbereich gibt es keine Fakultät");
    			return;
    		}
     
    		while (true)
    		{
    			if (number1 <= 1)
    			{
    				break;
    			}
     
    			fakultaet*=number1;
    			number1--;
    		}
    		System.out.println(fakultaet);
    	}
     
    }```

Dann solltest du eine do-while-Schleife nutzen. So in etwa:

do {
  // Eingabe lesen
} while (<eingabe ungültig>);

Genau! Was meinst du denn mit “Minusbereich”?

Fakultät ist für negative Zahlen (d.h. < 0) und Fließkommazahlen nicht definiert, ich denke das ist gemeint.

Also wie @EikeB schon richtig geschrieben hat, musst du dir überlegen, wann was abgefragt werden soll.
Bei einer do-while-Schleife hat man mind. einen Durchlauf, bevor die Bedingung geprüft wird, bei der while-Schleife hat
man diese direkt am Anfang.

Nebenbei bemerkt: Wieso benutzt du ein break, statt die Abbruch-Bedingung in while anzugeben?

while (number1 > 1)  {
   fakultaet *= number1;
   number1--;
}

Oha, da fehlen aber wichtige Grundlagen !
Ich würde es spontan ungefähr so schreiben :

import java.math.*;
public class Fak
{
	public static void main(String[] args)
	{
		BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
		BigInteger base=BigInteger.ZERO;
		do
		{
			System.out.println("please enter base (non-negative integer)");
			try { base=new BigInteger(in.readLine()); }
			catch(NumberFormatException nfe) { System.out.println("not an integer"); nfe.printStackTrace(); }
			catch(IOException ioe) { System.out.println("error while read from terminal"); ioe.printStackTrace(); }
		} while(base.compareTo(BigInteger.ZERO)!=1);
		System.out.println("!"+base+" = "+fak(base));
	}
	private static BigInteger fak(BigInteger base)
	{
		if(base.compareTo(BigInteger.ZERO)==0)
			return BigInteger.ONE;
		return base.multiply(fak(base.subtract(BigInteger.ONE)));
	}
}```
Zwar nicht schön, aber macht was es soll.

Zumindest der „Eingabeteil“ auf der Console wird dem Fragensteller da schon klarer (aber warum wird 0! nicht berechnet?)

Hat auch noch einen Schönheitsfehler in der Ausführung:

BigInteger ← damit man auch große Zahlen verarbeiten kann

Rekursion ← verhindert genau das

Wenn schon BigInteger, dann mit Schleife und klassischem Aufmultiplizieren

Hmm, prüfen auf „gleich oder größer 0“ würde dann so aussehen : while(base.compareTo(BigInteger.ZERO.subtract(BigInteger.ONE))!=1)

[QUOTE=Bleiglanz;118755]Hat auch noch einen Schönheitsfehler in der Ausführung:

BigInteger ← damit man auch große Zahlen verarbeiten kann

Rekursion ← verhindert genau das

Wenn schon BigInteger, dann mit Schleife und klassischem Aufmultiplizieren[/QUOTE]

Ja, mir ist klar dass BigInteger deutlich mehr Overhead hat als int oder long und damit in rekursivem Aufbau recht schnell zu nem StackOverflow führen kann, aber bis mindestens !10000 ist es sicher (hab jetzt nicht weiter ausprobiert bis wohin es geht bis der StackOverflow fliegt).
Sonst hier auch gerne noch die iterative Variante :

import java.math.*;
public class Fak
{
	public static void main(String[] args)
	{
		BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
		BigInteger base=BigInteger.ZERO;
		do
		{
			System.out.println("please enter base (non-negative integer)");
			try { base=new BigInteger(in.readLine()); }
			catch(NumberFormatException nfe) { System.out.println("not an integer"); nfe.printStackTrace(); }
			catch(IOException ioe) { System.out.println("error while read from terminal"); ioe.printStackTrace(); }
		} while(base.compareTo(BigInteger.ZERO)!=1);
		System.out.println("!"+base+" = "+fak(base));
	}
	private static BigInteger fak(BigInteger base)
	{
		BigInteger result=BigInteger.ONE;
		while(base.compareTo(BigInteger.ZERO)==1)
		{
			result=result.multiply(base);
			base=base.subtract(BigInteger.ONE);
		}
		return result;
	}
}```

PS : Warum bietet BigInteger eigentlich kein .clone() an ? Wenn ich den Parameter final gemacht hätte würde dass so nämlich nicht funzen. Google liefert dazu witziger weise : BigInteger sind immutable - es gibt also keinen Grund für .clone() ~ wow, haben die alle noch nichts davon gehört wenn man mit einem Parameter arbeiten will, dieser aber final ist ?

BigInteger bietet doch einen Kopierkonstruktor an. clone() wäre da doppeltgemoppelt. Und da BigInteger immutable ist, kann immer dieselbe Instanz benutzt werden, imho.

[quote=Sen-Mithrarin]Hmm, prüfen auf „gleich oder größer 0“ würde dann so aussehen :
while(base.compareTo(BigInteger.ZERO.subtract(BigInteger.ONE))!=1)[/quote]
echt? das muss doch als etwas komisch auffallen,
wenn überhaupt eine extra Konstante für -1, nicht immer ausrechnen,
aber Vergleich nur mit ZERO und dann Ergebnis >= 0 reicht doch auch,

genau auf 1 zu prüfen ist generell etwas unnötig riskant, vielleicht wird irgendwo mal der Abstand zurückgegeben, also auch Werte 2, 3, …, lieber > 0 usw. prüfen

speziell bei der Grenze 0 bietet sich noch die signum()-Methode an, base.signum() >= 0


?
die Erklärung ist doch schlüssig, immutable = kein clone nötig, du willst doch hoffentlich keinen unnötigen Code-Standard aufpressen (clone bei JEDEN Parameter, egal ob…)

wenn du den Parameter final gemacht hättest, dann brauchst du vor allem eine lokale Variable X, in der der Schleifenwert geschrieben wird,
im mutable-Fall beim Übertragen des Parameters in X eine Kopie erstellen, im immutable-Fall einfach nur übertragen/ hineinlegen, fertig?

Nein, BigInteger sind immer noch winzig (vergleichsweise), der StackOverflow kommt viel, viel früher - weshalb die iterative Variante viel größere Inputs verarbeiten kann.

OT1: Der Name base ist schlecht gewählt, nennt man nicht so

OT2: Es heißt 1000! und nicht !1000 (das Prefix-! ist normalerweise die logische Negation)

*** Edit ***

clone bei einem immutable ist absoluter Quatsch - unnötig, reine Speicherplatzverschwendung - da hat Google also recht

Nein, haben die alle nicht. Mit einem Parameter arbeiten, der final ist - da sind immutables super geeignet, weil man da kein clone braucht :slight_smile:

@SlaterB
x.compareTo(y) ist fest definiert :
-1 wenn x kleiner als y ist
0 wenn x gleich y ist
1 wenn x größer y ist
Andere Werte gibt es nicht (zumindest nicht bei einem korrekt implementierten compareTo()).
@Bleiglanz
Leider falsch. Die mathematisch korrekte Bezeichnung für eine Fakultät sieht vor dass das Ausrufezeichen VOR der Basis (darum auch der Variablenname) steht. Ein doppeltes Ausrufezeichen (!!x) bedeutet super-Fakultät, und ist fast identisch, nur das in den einzelnen Steps nicht mit Basiswert sondern deren Fakutltät gerechnet wird : !!3 = !3x!2x!1

Dass das Ausrufezeichen gleichzeitig auch als Negierung zu verstehen ist ist mir durchaus bewusst, aber ich hab mir das alles nicht einfallen lassen.

btw : zeig mir mal bitte den Fall an dem ohne was an den Standard-Parametern zu machen ein StackOverflow fliegt … int und long sind da schon lange aus ihrem Wertebereich raus wonach für die korrekte Berechnung nur noch “große” Klassen wie BigInteger in Frage kommen die theoretisch bis 2^INT_MAX verarbeiten können (siehe Java-Doc).

Zum clone() eines finalen immutable Parameter : da BigInteger (gutes Beispiel warum es clone() geben sollte) keinen direkten copy-Konstruktor anbietet fallen unnötige Umkonvertierungen an. Als einfachstes Beispiel wäre dass dann BigInteger(BigInteger.toString()) … kommt aber sicher auf den Compiler an. Ein clone() wäre hier hilfreich da lediglich das interne Daten-Array 1-zu-1 kopiert werden müsste was deutlich weniger Aufwand macht.

@Sen-Mithrarin

Das ist falsch in Java definiert Compareable nur das es kleiner oder größer null sein muss. Comparable (Java Platform SE 7 )
Nicht aber dass genau -1 und 1 verwendet werden müssen.

Es wird auch extra inder Doku die signum funktion verwendet um die werte des compareTo auf 1 und -1 zu bekommen. Wäre das ganze so wie du es schreibst könntest du die Signum funktion weglassen.

[QUOTE=Sen-Mithrarin]@SlaterB
x.compareTo(y) ist fest definiert :
-1 wenn x kleiner als y ist
0 wenn x gleich y ist
1 wenn x größer y ist
Andere Werte gibt es nicht (zumindest nicht bei einem korrekt implementierten compareTo()).
[/QUOTE]
(falls es überhaupt so ist) aber verlassen kann man sich nicht darauf, da dummerweise kein Zahl-/Datentyp nur mit diesen drei Werten benutzt wird/ existiert,

z.B. hat die nicht ganz unbedeutende Klasse namens String eine Implementation a la

        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

ist auch bisschen aufwendig in Eigenimplementierung, bei jeder int- oder sonstigen Differenz außerhalb [-1,0,1] extra noch auf -1 bzw. 1 herunterzurechnen,
besser wäre es freilich (oder auch nicht wenn doch nicht vorgeschrieben),
aber im eigenene Code kann man gefahr- und aufwandlos sichergehen, wozu ein Risiko eingehen und im Zweifel dann solche Fehler bei anderen bemängeln?


Leider falsch. Die mathematisch korrekte Bezeichnung für eine Fakultät sieht vor dass das Ausrufezeichen VOR der Basis (darum auch der Variablenname) steht. Ein doppeltes Ausrufezeichen (!!x) bedeutet super-Fakultät, und ist fast identisch, nur das in den einzelnen Steps nicht mit Basiswert sondern deren Fakutltät gerechnet wird : !!3 = !3x!2x!1

wenn in Gegenrede angesprochen, und nicht von irgendwem, und selbst Wikipedia

Sie wird durch ein dem Argument nachgestelltes Ausrufezeichen („!“) abgekürzt.

Fakultät
schreibt sowie sicherlich die Lebenserfahrung/ Matheunterricht der gesamten Welt (auch bei dir?),
wäre es dann nicht angebracht, evtl. exotische ‚eigentlich‘-Definitionen mit Quellen zu hinterlegen?


Zum clone() eines finalen immutable Parameter : da BigInteger (gutes Beispiel warum es clone() geben sollte) keinen direkten copy-Konstruktor anbietet fallen unnötige Umkonvertierungen an. Als einfachstes Beispiel wäre dass dann BigInteger(BigInteger.toString()) … kommt aber sicher auf den Compiler an. Ein clone() wäre hier hilfreich da lediglich das interne Daten-Array 1-zu-1 kopiert werden müsste was deutlich weniger Aufwand macht.

wozu das clone()? eben weil es immutable ist ist das doch relativ unnötig,

String hat freilich einen Copy-Konstruktor, es ist korrekt dass das hier fehlt, aber gerade besonders nötig?

Quellenangabe bitte

Berechne einfach mal die Fakultät vom 10000000 modulo 347: das geht ohne weiteres, aber eben nicht rekursiv - die Tiefe des Stacks bei rekursiven Aufrufen hat nicht unbedingt was damit zu tun, wie viel Speicher die Variablen selbst benötigen…

Alles völlig sinnlos: man braucht keine Copy einer immutable Variable, NIE; dein einfachstes Beispiel verstehe ich nicht…

[QUOTE=Sen-Mithrarin]Oha, da fehlen aber wichtige Grundlagen !
Ich würde es spontan ungefähr so schreiben :

Zwar nicht schön, aber macht was es soll.[/QUOTE]

Das ist richtig, alles schön. Rekursion kann auch teilweise zur Laufzeit ‘aufgelöst’ werden durch VM, je nach Form.

if (! bi1.equals(new bi.Zero)) {

sollte doch auch funktionieren. Basis darf nicht 0 sein. 1 ist multiplikationsneutral, und 0 ‘setzt ein Ergebnis zurück’. Besser kann ich das auch nicht formulieren.