Leider hab ich einen Fehler im vorgegebenen Code gefunden, zumindest stimmt es nicht mit der Beschreibung überein. Ich korrigiere den Fehler und mache die Stelle kenntlich. Hoffe niemand hat sich daran den Kopf zerbrochen :(
Hier sind drei Test-Automaten:
// Gleich viele "a" wie "b"
private static PushDownAutomaton GleichVieleAundB()
{
PushDownAutomaton automat = new PushDownAutomaton();
State startUndEndZustand = new State();
// (eingabe, stack, ausgabe)
// Wichtig zu beachten: Das was auf dem Stack liegt wird vom Stack genommen!
Transition a_1 = new Transition("a", null, startUndEndZustand, "a"); // (a, leer, a) => a auf den Stack legen
Transition a_2 = new Transition("a", "a", startUndEndZustand, "a", "a"); // (a, a, aa) => a verdoppeln (1 a wird zu 2 a)
Transition a_3 = new Transition("a", "b", startUndEndZustand, null); // (a, b, nichts) => b vom Stack nehmen
Transition b_1 = new Transition("b", null, startUndEndZustand, "b"); // (b, leer, b) => b auf den Stack legen
Transition b_2 = new Transition("b", "b", startUndEndZustand, "b", "b"); // (b, b, bb) => b verdoppeln
Transition b_3 = new Transition("b", "a", startUndEndZustand, null); // (b, a, nichts) => a vom Stack nehmen
startUndEndZustand.Transitions.AddRange(new Transition[]
{
a_1, a_2, a_3, b_1, b_2, b_3
});
automat.FinalStates.Add(startUndEndZustand);
automat.StartState = startUndEndZustand;
return automat;
}
// Es gibt mehrere mögliche Übergänge.
// Anzahl von a = (2 + 4k) => 2, 6, 10, 14, ...
// Ein etwas komplizierter Automat
private static PushDownAutomaton MehrereÜbergänge()
{
PushDownAutomaton automat = new PushDownAutomaton();
State startZustand = new State();
State endZustand = new State();
// (eingabe, stack, ausgabe)
Transition start_1 = new Transition("a", null, startZustand, "a"); // (a, leer, a)
Transition start_2 = new Transition("a", "a", startZustand, "a", "a"); // (a, a, aa)
Transition start_3 = new Transition("a", "a", endZustand, null); // (a, a, nichts)
Transition end_1 = new Transition("a", null, endZustand, "a"); // (a, leer, a)
Transition end_2 = new Transition("a", "a", endZustand, "a", "a"); // (a, a, aa)
Transition end_3 = new Transition("a", "a", startZustand, null); // (a, a, nichts)
startZustand.Transitions.AddRange(new Transition[]
{
start_1, start_2, start_3
});
endZustand.Transitions.AddRange(new Transition[]
{
end_1, end_2, end_3
});
automat.FinalStates.Add(endZustand);
automat.StartState = startZustand;
return automat;
}
// Gerade Anzahl an a
private static PushDownAutomaton EpsilonÜbergänge()
{
PushDownAutomaton automat = new PushDownAutomaton();
State startZustand = new State();
State endZustand = new State();
// (eingabe, stack, ausgabe)
Transition start_1 = new Transition("a", null, startZustand, "a"); // (a, leer, a)
Transition start_2 = new Transition("a", "a", startZustand, "a", "a"); // (a, a, aa)
Transition start_3 = new Transition(null, null, endZustand, null); // Epsilonübergang
Transition end = new Transition("a", "a", endZustand, null); // (a, a, nichts)
startZustand.Transitions.AddRange(new Transition[]
{
start_1, start_2, start_3
});
endZustand.Transitions.AddRange(new Transition[]
{
end
});
automat.FinalStates.Add(endZustand);
automat.StartState = startZustand;
return automat;
}
public static void Main(String[] args)
{
PushDownAutomaton automat = GleichVieleAundB();
while (true)
{
String input = Console.ReadLine();
List<String> word = new List<String>();
foreach (Char c in input)
word.Add(c.ToString());
Console.WriteLine(automat.CheckWord(word));
}
}
Da wohl keiner Lust hatte die Aufgabe zu lösen, poste ich meine Lösung. Somit ist das Programmierspiel wieder freigegeben (so wie es auch im ersten Post beschrieben ist, da keine Antwort innerhalb einer Woche kam).
// input => Wort
// index => Der aktuelle Buchstabe im Wort
// stack => Der Stack
// finalStates => Die Endzustände
// Gibt <true> zurück, wenn das Wort abgearbeitet ist, der Stack leer ist und der letzte Zustand ein Endzustand war
public Boolean ChangeState(List<String> input, Int32 index, Stack<String> stack, List<State> finalStates)
{
// Wenn das Wort abgearbeitet ist, nichts mehr auf dem Stack liegt und der momentane Zustand ein Endzustand ist...
if (index ≥ input.Count && stack.Count == 0 && finalStates.Contains(this))
return true; // Dann wurde das Wort erkannt
// Alle Übergänge durchgehen
foreach (var transition in Transitions)
{
// Wenn das Wort abgearbeitet ist, sind nur noch Epsilonübergänge erlaubt, andernfalls muss die Bedingung überprüft werden
Boolean condition = index ≥ input.Count ? transition.IsEpsilon : transition.CheckConditions(input[index], stack);
if (condition)
{
String element = null;
// Den Stack ändern, außer bei Epsilonübergängen und leeren Stacks
if (stack.Count > 0 && !transition.IsEpsilon)
element = stack.Pop();
// Den Output vom Übergang auf den Stack legen
foreach (String str in transition.Output)
stack.Push(str);
// ------------
// Zum nächsten Zustand wechseln. Wenn es ein Epsilonübergang war, darf nicht zum nächsten Element im Wort gesprungen werden!
if (transition.NextState.ChangeState(input, index + (transition.IsEpsilon ? 0 : 1), stack, finalStates))
return true;
// Alle Änderungen auf dem Stack rückgängig machen
for (Int32 i = 0; i < transition.Output.Count; i++)
stack.Pop();
if (element != null)
stack.Push(element);
// ------------
}
}
// Alle Übergange wurden geprüft und keiner hat zum Ziel geführt
return false;
}
da die Letzte Aufgabe nicht gelöst wurde, nach 10 Tagen die Lösung vom Aufgabensteller selbst gepostet wurde, dieser darauf verzichtet hat erneut eine Aufgabe zu stellen ...
Zitat von DerKleineTomy
Somit ist das Programmierspiel wieder freigegeben
... und seit 5 Tagen keine neue Aufgabe mehr gestellt wurde, versuche ich es jetzt einmal mit einer neuen Aufgabe. Insofern niemand was dagegen hat.
Also:
Aufgabe:
Vervollständigen Sie, die im Test-Code definierte Methode so, dass diese eine [URL]Mandelbrot-Menge[/URL] (Apfelmännchen) zeichnet.
Rahmenbedingungen:
- Die Mandelbrot-Menge soll Einfarbig (frontColor) und mit einer Hintergrundfarbe (backColor) gezeichnet werden.
- Die Parameter "section -Left -Top -Right -Bottom" geben den Ausschnitt in der Mandelbrot-Menge an, der gezeichnet werden soll.
- Der Parameter "deep" gibt die maximale Anzahl Iterationen pro Pixel an.
Test-Code:
using System;
using System.Windows.Forms;
using System.Drawing;
namespace Fractals
{
/// <summary>
/// Stellt ein Fenster dar, dass ein Mandelbrot Apfelmännchen Fraktal anzeigt.
/// </summary>
public class MandelbrotForm : Form
{
//Mandelbrot PictureBox
PictureBox mandelbrotPicture;
/// <summary>
/// Erstellt ein neues Mandelbrot Fenster.
/// </summary>
public MandelbrotForm()
{
//Fenster Parameter festlegen
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.StartPosition = FormStartPosition.CenterScreen;
this.Size = new Size(900, 600);
//Image Control erstellen
mandelbrotPicture = new PictureBox();
mandelbrotPicture.Dock = DockStyle.Fill;
this.Controls.Add(mandelbrotPicture);
//Fenster anzeigen
this.Show();
//Image mit dem Mandelbrot Fraktal erstellen
Bitmap mandelbrot = new Bitmap(this.Size.Width, this.Size.Height);
using (Graphics gfx = Graphics.FromImage(mandelbrot))
{
Mandelbrot.DrawMandelbrot(
gfx,
Color.OrangeRed,
Color.LightBlue,
-2.0, -1.0, 1.0, 1.0, //Ganzes Apfelmännchen
//0.3184375, -0.0557421875, 0.34533203125, -0.0355712890625, //Ausschnitt
//0.32155833984375, -0.6327984375, 0.422811767578125, -0.55685836669921875, //Ausschnitt
100);
}
mandelbrotPicture.Image = mandelbrot;
}
}
/// <summary>
/// Stellt eine Klasse zum Zeichnen von Mandelbrot Apfelmännchen Fraktalen dar.
/// </summary>
public static class Mandelbrot
{
/// <summary>
/// Zeichnet ein Mandelbrot Apfelmännchen.
/// </summary>
/// <param name="gfx">Grafikausgabe.</param>
/// <param name="frontColor">Farbton, der für das Zeichnen verwendet werden soll.</param>
/// <param name="backColor">Hintergrundfarbe.</param>
/// <param name="sectionLeft">Gibt den linken Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="sectionTop">Gibt den oberen Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="sectionRight">Gibt den rechten Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="sectionBottom">Gibt den unteren Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="deep">Gibt die maximale Anzahl Iterationen an.</param>
public static void DrawMandelbrot(Graphics gfx, Color frontColor, Color backColor, double sectionLeft, double sectionTop, double sectionRight, double sectionBottom, int deep)
{
//Erstellen Sie Ihren Code hier.
}
}
}
Viel Spaß und Gruß Stalky13
Ps. Fraktale sind Awsome :D
Edit:
Da es nicht voran zu gehen scheint, hier ein Tipp: In dem Wikipediabeitrag findet ihr die nötigen Informationen zur Lösung ganz unten. Ihr müsst also nicht den ganzen Beitrag lesen, sondern nur einen kleinen Teil am Ende.
Meine Version besteht aus 20 Zeilen Code (ohne Klammern und Kommentare).
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von stalky13 am .
Aus großer Macht folgt große Verantwortung!
Moderationshinweis von herbivore
(31.05.2012 - 07:05)
Offenbar kommt keiner drauf also möchte ich eine neue kleine Aufgabe stellen.
Um etwas das Verständnis in Sachen "Wie rechnet mein Rechner?" eigentlich zu festigen würde ich gern folgende Funktion sehen:
Ich übergebe 3 Bit (2 die ich addieren will und den Übertrag von der vorherigen Rechnung)
Um Große zahlen zu berechnen wird jede Operation auf Additionen zurückgeführt die die Recheneinheit dann berechnen kann. (Wie das geht steht bestimmt irgendwo auf Wikipedia und co.) Dabei werden immer 2 Bit miteinander addiert und ggf. ein Übertrag der vorherigen Stelle)
Ziel ist es ganz einfach eine Funktion zu schreiben der man 3 Bit (0 oder 1) übergibt.
Zahl1, Zahl2 und den Übertag.
Die Funktion gibt dann die Summe aus und zwar als string in Binärschreibweise.
Dazu will ich noch wissen wie sich das nennt was ihr da Programmiert habt.
Zur Info:
bei 3 Bit die Man addiert kann nur 00, 01, 10 oder 11 herauskommen von daher kann man das von mir aus auch mit einer switch Anweisung machen oder so um sich das umrechnen von dezimal in dual zu sparen.
Wichtig ist dass ich die Ergebnisse von
0 und 0 Übertrag 0
0 und 0 Übertrag 1
0 und 1 Übertrag 0
0 und 1 Übertrag 1
1 und 0 Übertrag 0
1 und 0 Übertrag 1
1 und 1 Übertrag 0
1 und 1 Übertrag 1
bekomme
Kommt ein Mann in die Wirtschaft und sagt zum Wirt: 16 Bit!
Sagt der Wirt: Das ist ein Wort!
hiho die antwort ist richtig ich wollte grade noch sagen weil ich darauf hingewiesen wurde bitte auch strings als parameter. aber das hast du ja schon gemacht. warum strings? tja warum strings? ich wollt halt nicht klassisch bool oder zahlen nutzen ;) aber ist auch nebensächlich das wichtige ist dass die idee verstanden wurde wie ein rechner rechnet du bist dran scavanger
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von thetruedon am .
Kommt ein Mann in die Wirtschaft und sagt zum Wirt: 16 Bit!
Sagt der Wirt: Das ist ein Wort!
Da seit über einem Monat keine neue Aufgabe gestellt wurde, mache ich das mal:
Gesucht ist eine Methode [tt]TElement PickRandom(IEnumerable<TElement> source)[/tt], die ein zufälliges Element aus einer Auflistung zurückgeben soll.
Folgende Bedingungen müssen allerdings erfüllt sein:
[list]
[*]Die Auflistung darf nur ein einziges Mal durchlaufen werden.
[*]Die Auflistung darf nicht zwischengespeichert werden.
[*]Die Wahrscheinlichkeit, ausgewählt zu werden, muss für alle Elemente der Auflistung gleich sein.
[/list]
Falls die Auflistung keine Elemente enthält, sollte eine Ausnahme ausgelöst werden.
private static Random m_objRand;
public TElement PickRandom (IEnumerable<TElement> source) {
if (m_objRand == null) m_objRand = new Random();
int iCount = source.Count<TElement>();
if (iCount == 0) throw new Exception("COUNT darf nicht 0 sein.");
int iIndex = 0;
int iRandomObjectIndex = m_objRand.Next(0, source.Count<TElement>());
foreach (TElement obj in source) {
if (iIndex == iRandomObjectIndex) {
return obj;
} // if end
iIndex++;
} // foreach end
return null;
}
Ich denke, die Antwort ist zu "einfach" oder? :D
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von DeZio am .
Okay... source.Count<TElement>() als 2. Parameter in new Random sollte eh iCount werden - aber das tut ja nichts zur Sache. 2 sind's trotzdem :D
// Edit
Schwierige Sache. Ich habe eine 2. Idee gehabt: Erst OrderBy Random (1 Durchlauf) - und dann das erste Element [0] durch ToList() returnen. Bei Fehler eben throw Excption - aber ich denke: ToList() läuft die IEnumerable ein zweites Mal durch. Oder?
Würde so aussehen:
public class TElement { public int Index = 0; }
internal class Program {
private static Random m_objRand;
private static void Main () {
List<TElement> source = new List<TElement>();
source.Add(new TElement() { Index = 0 });
source.Add(new TElement() { Index = 1 });
source.Add(new TElement() { Index = 2 });
source.Add(new TElement() { Index = 3 });
source.Add(new TElement() { Index = 4 });
for (int i = 0; i < 2; i++) {
TElement rand = PickRandom(source);
Console.WriteLine(rand.Index);
} // for end
}
public static TElement PickRandom (IEnumerable<TElement> source) {
if (m_objRand == null) m_objRand = new Random();
try {
return source.OrderBy(obj => m_objRand.Next()).First();
} // try end
catch {
throw new Exception();
}
}
}
// Edit 2: Hab die Lösung! Man benötigt nicht "ToList()" - man kann auf die Methode First() zurückgreifen :)
Grüße
Dennis Z.
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von DeZio am .
public static T PickRandom<T>(IEnumerable<T> source)
{
Random r = new Random();
int count = 0;
T selected = default(T);
foreach (T t in source)
{
count++;
if (r.Next(count) == 0)
selected = t;
}
if (count == 0)
throw new Exception("source was empty");
return selected;
}
Ich laufe jedes Element der Auflistung durch und zähle dabei count hoch. Mit einer Wahrscheinlichkeit von 1/count überschreibe ich das Element in selected, dass heißt das erste Element überschreibt mit einer Wahrscheinlichkeit von 1/1 -> es wird auf jedenfall gewählt. Das 2. Element wird mit einer Wahrscheinlichkeit von 1/2 gewählt.
Das 3. Element überschreibt mit einer Wahrscheinlichkeit von 1/3 das Element, das davor gewählt wurde. Mit einer Wahrscheinlichkeit von 1/3 ist also das dritte Element das gewählte. Die restlichen 2/3 teilen sich die ersten beiden Elemente, die dadurch beide auch jeweils 1/3 Wahrscheinlichkeit haben.
Das n. Element wird mit einer Wahrscheinlichkeit von 1/n gewählt. Dadurch hat insgesamt jedes Element die Wahrscheinlichkeit 1/n, da das Element davor mit einer Wahrscheinlichkeit von 1/n überschrieben wird und die Elemente davor alle eine Wahrscheinlichkeit von 1/(n-1) hatten.
Nach dem Schleifendurchlauf prüfe ich dann noch ob count 0 war, damit ich dann wie in der Aufgabe gefordert eine Exception werfen kann.
Und falls ich nichts übersehen habe und meine Antwort richtig ist, darf wer anders die nächste Frage stellen.
Darth Maim
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Darth Maim am .
OK wenn der nächste darf geb ich mal was neues vor.
Ich möchte ein Quine also ein Programm, das sich selber ausgibt (also den Quelltext)
Ich gebe mal folgende Klasse vor:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("...");
}
}
Wie ihr seht will ich keine gigantische Anwendung belasst es bei einer kleinen Ausgabe da ihr euch sonst nur noch mehr Arbeit aufhalst.
Bearbeitet die Main so dass alles von class bis } ausgegeben wird.
Und das alles ohne Reflection und in max. 5 Zeilen (soviele braucht man gar nicht denke ich.
Have fun
Kommt ein Mann in die Wirtschaft und sagt zum Wirt: 16 Bit!
Sagt der Wirt: Das ist ein Wort!
class Program
{
static void Main(string[] args)
{
string o = "class Program{{static void Main(string[] args){{string o={1}{0}{1};System.Console.WriteLine(o, o, (char)34);}}}}";
System.Console.WriteLine(o, o, (char)34);
}
}
btw so ists angenehmer
class Program
{
static void Main(string[] args)
{
string o = "class Program{{static void Main(string[] args){{string o={1}{0}{1};System.Console.WriteLine(o, o, (char)34);System.Console.ReadLine();}}}}";
System.Console.WriteLine(o, o, (char)34);
System.Console.ReadLine();
}
}
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Quadsoft am .
Erstaunlich nahe an meinem Code
Jop naja namespace hin oder her die Vorlage sollte nur hinweisen dass ich nicht gleich ein 100 Zeilenprogramm sehen will weil das beim schreiben und lesen sonst zu depressionen geführt hätte
Richtig gemacht du bist dran
Edit:
Achso hab ich zwar nicht gesagt aber schön wäre ein Environment.NewLine für Zilenumbrüche gewesen schließlich will man den Code ja am ende auch wieder lesen können
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von thetruedon am .
Kommt ein Mann in die Wirtschaft und sagt zum Wirt: 16 Bit!
Sagt der Wirt: Das ist ein Wort!
/// <summary>
/// Zeichnet ein Mandelbrot Apfelmännchen.
/// </summary>
/// <param name="gfx">Grafikausgabe.</param>
/// <param name="frontColor">Farbton, der für das Zeichnen verwendet werden soll.</param>
/// <param name="backColor">Hintergrundfarbe.</param>
/// <param name="sectionLeft">Gibt den linken Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="sectionTop">Gibt den oberen Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="sectionRight">Gibt den rechten Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="sectionBottom">Gibt den unteren Rand des Ausschnitts an, der von dem Apfelmännchen gezeichnet werden soll.</param>
/// <param name="deep">Gibt die maximale Anzahl Iterationen an.</param>
public static void DrawMandelbrot(Graphics gfx, Color frontColor, Color backColor, double sectionLeft, double sectionTop, double sectionRight, double sectionBottom, int deep)
{
SolidBrush front = new SolidBrush(frontColor);
SolidBrush back = new SolidBrush(backColor);
System.Numerics.Complex z, c;
for (int x = 0; x < (int)gfx.VisibleClipBounds.Width; x++)
{
for (int y = 0; y < (int)gfx.VisibleClipBounds.Height; y++)
{
z = new System.Numerics.Complex(0,0);
c = new System.Numerics.Complex(sectionLeft + (sectionRight - sectionLeft) * (x / gfx.VisibleClipBounds.Width),
sectionTop + (sectionBottom - sectionTop) * (y / gfx.VisibleClipBounds.Height));
int i = 0;
while (true)
{
i++;
z = z * z + c;
if (z.Magnitude > 2.0)
{
gfx.FillRectangle(back, x, y, 1, 1);
break;
}
else if (i == deep)
{
gfx.FillRectangle(front, x, y, 1, 1);
break;
}
}
}
}
}
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Quadsoft am .
die Lösung von Quadsoft entspricht genau der Aufgabe und ist somit völlig korrekt.
Mit Einfarbig hatte ich eigendlich einen Farbverlauf von der Fordergrund zur Hintergrund Farbe gemeint, beim erneuten durch lesen der Aufgabe, ist mir allerdings erst jetzt aufgefallen, das ich diesen Punkte hätte besser definieren sollen.
Wie auch immer, ich freue mich, dass nun doch eine Lösung gekommen ist
@Quadsoft
Ich werd mir dein Programm und deine Facharbeit auf jeden Fall ansehen, aber erst morgen ich muss nämlich gleich los.
Ach ja und da deine Antwort richtig ist, darfst du vor 2 Monaten eine neue Aufgabe stellen
Wenn eine Woche seit dem jeweils letzten Beitrag vergangen ist, ist der Thread wieder frei für eine neue Aufgabe (egal wer die neue Aufgabe stellen möchte und egal ob dieser letzte Beitrag nun eine Aufgabe, eine Nachfrage oder eine Lösung ist und egal ob die Lösung richtig oder falsch war, also einfach: eine Woche Inaktivität = neue Aufgabe erlaubt).
Damit möchte ich auch gleich den Thread mal wieder aus der Versenkung holen.
Es ist eine kleine Aufgabe, ihr könnt sie als Code oder auch mit Code und Papier lösen.
Wie ihr das macht ist ja im Endeffekt euch überlassen.
Hinter folgender Zahl ist ein Wort versteckt, welches ich gerne von euch wissen möchte: 110098088072122105107
2 kleine Tipps:
- Das Wort besteht aus 7 Buchstaben.
- Sobald ihr aus den Zahlen Buchstaben gemacht habt müsst ihr verkehrt herum denken.
Also 7 Buchstaben-Wort und 21 Zahlen -> 3 Ziffern pro Buchstabe. Das ganze in der Asciitabelle nachschlagen und dann die herausgefundenen Buchstaben, von hinten abzählen. Also X wird zu C.
Die Tipps habens sehr einfach gemacht.
da sich noch keiner erbarmt hat und Alf Ator es freigestellt hat sich dazwischen zu drängeln, hier eine Aufgabe aus dem Alltag.
Letzte Woche stand ich vor folgendem Problem:
Eine Erinnerungsfunktion sollte um eine Sprachausgabe erweitert werden und mir ein paar Informationen nennen.
Darunter die aktuelle Uhrzeit und die Dauer zum nächsten Termin.
Wie spreche ich nun also nach allen Regeln der deutschen Sprache korrekt ganze Zahlen aus?
Um es noch etwas schwieriger zu machen, brauchen wir auch noch Hunderter, Tausender, bis zur einer Milliarde (Zahlennamen).
(Es hat einen speziellen Grund, weshalb ich diese nicht als string[] oben mit eingefügt habe)
Nun kann man schon fleißig los programmieren und schnell wird klar, dass noch ein und her muss. Die Ausgaben scheinen auch schon zu funktionieren:
Fünfhundertsechsundzwanzig
Zweihundertsieben ("und" bei einer Null an der Zehnerstelle nicht unbedingt notwendig)
Hundertein => Fehler!
Also noch eine Unterscheidung mehr und wir sind am Ende angekommen.
Oder?
[EDIT] Auf Anraten von herbivore möchte ich die Aufgabe auf folgenden Zahlenbereich deckeln: 0 bis 1 000 000 000 (eine Millarde).
Den Bereich noch weiter zu erhöhen ist schließlich nur Fleißarbeit.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von trib am .
Da sich bisher noch niemand gemeldet hat, gebe ich noch schnell zwei entscheidende Tipps:
(Wer sich dessen schon angenommen hat, sollte hier ggf. nicht weiterlesen )
Die Hunderter, Tausender, usw. habe ich absichtlich weggelassen, da diese optimaler Weise auf zwei Arrays aufgeteilt werden müssen. Und zwar in singular & plural.
Die Eingabe wird in Dreiergruppen aufgespaltet und verarbeitet!
Ansonsten: Kritik oder Anregungen?
Zu einfach, zu langweilig, gibt es schon 100fach im Internet?
Hier mal meine Lösung, basierend auf deinen Tipps:
public class NumberToTextConverter
{
private string[] _lessThan20 = new string[]{ "null", "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn", "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn" };
private string[] _magnitude1 = new string[] { "", "ein", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun" };
private string[] _magnitude10 = new string[] { "", "zehn", "zwanzig", "dreissig", "vierzig", "fünfzig", "sechzig", "siebzig", "achtzig", "neunzig" };
private string[] _magnitudesSingular = new string[] { "hundert", "tausend", " million ", " milliarde " };
private string[] _magnitudesPlural = new string[] { "hundert", "tausend", " millionen ", " milliarden " };
public string NumberToText(int number)
{
StringBuilder resultBuilder = new StringBuilder();
string result = string.Empty;
string numberText = number.ToString();
int orderOfMagnitude = 0;
if (number < 20)
result = _lessThan20[number];
else
{
while (numberText.Length > 2)
{
result = this.NumberPartToText(numberText.Substring(numberText.Length - 3, 3), orderOfMagnitude++) + result;
numberText = numberText.Remove(numberText.Length - 3);
}
result = this.NumberPartToText(numberText.PadLeft(3, '0'), orderOfMagnitude++) + result;
}
foreach (string word in result.Split(' '))
if (word.Length > 0)
resultBuilder.Append(word.Substring(0, 1).ToUpper() + word.Substring(1) + " ");
return resultBuilder.ToString();
}
private string NumberPartToText(string numberPart, int orderOfMagnitude)
{
string result = string.Empty;
if (numberPart == "000")
return string.Empty;
if (numberPart == "001" && orderOfMagnitude > 0)
return (orderOfMagnitude == 1 ? "ein" : "eine") + _magnitudesSingular[orderOfMagnitude];
if (numberPart[0] != '0')
result = _magnitude1[int.Parse(numberPart[0].ToString())] + _magnitudesSingular[0];
result += this.TwoDigitsToText(numberPart.Substring(1, 2));
if (orderOfMagnitude > 0)
result += int.Parse(numberPart) > 1 ? _magnitudesPlural[orderOfMagnitude] : _magnitudesSingular[orderOfMagnitude];
return result;
}
private string TwoDigitsToText(string twoDigits)
{
string result = string.Empty;
int value = int.Parse(twoDigits);
if (twoDigits == "00")
return string.Empty;
if (value < 20)
return _lessThan20[value];
if (twoDigits[1] != '0')
result += _magnitude1[int.Parse(twoDigits[1].ToString())] + "und";
result += _magnitude10[int.Parse(twoDigits[0].ToString())];
return result;
}
}
Viel kürzer krieg ich's aufgrund der ganzen Spezialfälle nicht hin ... schon erstaunlich, wie "unregelmässig" das ist, wenn man genau hinschaut Zum Testen hab ich folgenden Code genommen:
static void Main(string[] args)
{
NumberToTextConverter num = new NumberToTextConverter();
int[] test = new int[] {
0, 1, 19, 30, 99,
100, 201, 311, 420, 543,
1000, 2001, 3012, 4300, 5430, 5432,
10000, 20001, 30012, 55555, 99200,
100000, 200001, 333333, 550055, 444000, 333301,
1000000, 2000001, 1234567, 5000000, 7050011,
10000000, 30000001, 12345678, 87654321, 1000000000, int.MaxValue };
for (int i = 0; i < test.Length; i++)
Console.WriteLine("{0}: {1}", test[i].ToString().PadRight(10), num.NumberToText(test[i]));
Console.ReadLine();
}
Und die Ausgabe sieht in dem Fall so aus (ob die Spaces da jetzt überall Sinn machen, weiss ich nicht, aber das wäre ja schnell geändert - und vorgegeben war es auch nicht):
0 : Null
1 : Eins
19 : Neunzehn
30 : Dreissig
99 : Neunundneunzig
100 : Einhundert
201 : Zweihunderteins
311 : Dreihundertelf
420 : Vierhundertzwanzig
543 : Fünfhundertdreiundvierzig
1000 : Eintausend
2001 : Zweitausendeins
3012 : Dreitausendzwölf
4300 : Viertausenddreihundert
5430 : Fünftausendvierhundertdreissig
5432 : Fünftausendvierhundertzweiunddreissig
10000 : Zehntausend
20001 : Zwanzigtausendeins
30012 : Dreissigtausendzwölf
55555 : Fünfundfünfzigtausendfünfhundertfünfundfünfzig
99200 : Neunundneunzigtausendzweihundert
100000 : Einhunderttausend
200001 : Zweihunderttausendeins
333333 : Dreihundertdreiunddreissigtausenddreihundertdreiunddreissig
550055 : Fünfhundertfünfzigtausendfünfundfünfzig
444000 : Vierhundertvierundvierzigtausend
333301 : Dreihundertdreiunddreissigtausenddreihunderteins
1000000 : Eine Million
2000001 : Zwei Millionen Eins
1234567 : Eine Million Zweihundertvierunddreissigtausendfünfhundertsiebenundsechzig
5000000 : Fünf Millionen
7050011 : Sieben Millionen Fünfzigtausendelf
10000000 : Zehn Millionen
30000001 : Dreissig Millionen Eins
12345678 : Zwölf Millionen Dreihundertfünfundvierzigtausendsechshundertachtundsiebzig
87654321 : Siebenundachtzig Millionen Sechshundertvierundfünfzigtausenddreihunderteinundzwanzig
1000000000: Eine Milliarde
2147483647: Zwei Milliarden Einhundertsiebenundvierzig Millionen Vierhundertdreiundachtzigtausendsechshundertsiebenundvierzig
vorab: Getestet und für gut befunden :)
Du bist dran!
Zum Code:
Der sieht für mich vollkommen in Ordnung aus. Mir ist nur aufgefallen, dass ich mit einem Array weniger ausgekommen bin. "_magnitude1" ist ja quasi bis auf ein/eins in "_lessThan20" enthalten.
private static string Speak(long number)
{
string result = string.Empty;
double maxNo = Math.Pow(10, (3 * singular.Length)); //10*3*10
if(number ≥ maxNo)
return "Nummer ist zu groß!!!";
if (number == 0)
return "null";
//number in Dreiergruppen aufspalten:
int[] group = new int[(number.ToString().Length / 3)+1];
int i = 0;
while( number > 0)
{
group[i++] = Convert.ToInt32(number % 1000);
number = number / 1000;
}
//Dreiergruppen von links nach rechts aussprechen:
i = group.Length -1;
while(i ≥ 0)
{
result += SpeakTrifurcator(group[i], i) + "\n";
i--;
}
return result;
}
Dreier-Zahlen aussprechen:
private static string SpeakTrifurcator(int number, int position)
{
string result = string.Empty;
if(number == 0)
return string.Empty;
if (number == 1)
return upTo20[number] + singular[position];
int hundred = number / 100;
int rest = number % 100;
int tenth = rest / 10;
int first = number % 10;
//Make it speak
if (hundred > 0)
result += upTo20[hundred] + "hundert";
//Do the rest
if (rest > 0)
{
// Sonderfall: number endet auf 01,
// ist aber ungleich 1
if (rest == 1)
result += "eins";
else
if (rest < 20)
result += upTo20[rest];
else
{
if (first > 0)
result += upTo20[first];
// sage "und", wenn es Einer-
// und Zehneranteile vorhanden
if (tenth > 0 & first > 0)
result += "und";
// Zehneranteil aussprechen:
if (tenth > 0)
result += ten[tenth];
}
}
return result + plural[position];
}
Der sieht für mich vollkommen in Ordnung aus. Mir ist nur aufgefallen, dass ich mit einem Array weniger ausgekommen bin. "_magnitude1" ist ja quasi bis auf ein/eins in "_lessThan20" enthalten.
Ja stimmt, das hätte man noch etwas kürzen können ... wären aber wieder mehr Spezialfälle geworden
Zitat von trib
Du bist dran!
OK, der Thread deckt ja jetzt schon eine Menge ab und mir fällt grade keine wirklich neue Aufgabe ein, also machen wir doch das Naheliegende
Und nochmal rückwärts
Erstelle einen Parser, der aus einer komplett ausgeschriebenen Zahl den passenden Zahlenwert ermittelt. Das Ergebnis sollte eine Methode mit folgender Signatur sein:
Der Rahmen für die gültigen "Zahlentexte" soll auch hier wieder 0 bis 1 Milliarde umfassen, also genau wie in der letzten Aufgabe, nur eben umgekehrt. Zum Erzeugen von Testeingaben kann dann praktischerweise meine Klasse oder tribs Code verwendet werden ... Viel Spass 8)
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von jannemann13 am .
Moderationshinweis von gfoidl
(17.08.2012 - 14:59)