myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Gemeinschaft » Smalltalk » Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Seiten (19): « erste ... « vorherige 13 14 15 16 [17] 18 19 nächste » Antwort erstellen
Zum Ende der Seite springen  

Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
D4rkScr43m
myCSharp.de-Mitglied

Dabei seit: 10.12.2010
Beiträge: 31


D4rkScr43m ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Funktioniert wie erwartet und ist allerdings nicht ganz so schnell wie meins. Bei entsprechend großen Dateien komme ich mit meiner Lösung auf ca. 9 Sekunden, wobei deine Lösung noch ca. 14 Sekunden braucht.

Der einzige Unterschied liegt wohl darin, dass ich Key und Value nicht auseinander ziehe sondern als einen langen String vergleiche.

Aber ich lass das mal durchgehen, mein erster Versuch lag bei den beiden Dateien bei über etwa 30 Sekunden.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von D4rkScr43m am 29.10.2012 10:47.

29.10.2012 10:15 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 2 Monate.
Scavanger Scavanger ist männlich
myCSharp.de-Mitglied

avatar-3209.jpg


Dabei seit: 13.03.2008
Beiträge: 309
Entwicklungsumgebung: MS VS C# 2010/2013 Prof.


Scavanger ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Um mal den Thread aus der Versenkung zu holen:

Ich möchte Code um die Größe von einer sequentiellen Struktur in Bytes abzurufen:

Beispiel:

C#-Code:
[StructLayout(LayoutKind.Sequential)]
struct Foo
{
    public int baz;
    public ushort bar;
    public long foobar;
}

Ohne dabei Marshal.SizeOf() oder sizeof() zu benutzen.
Die Angabe muss aber mit Marshal.SizeOf() identisch sein (Stichwort: Byte Alignment).

Ist locker mit weniger als 10 Zeilen zu schaffen. Augenzwinkern
08.01.2013 20:24 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegt mehr als ein Monat.
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Community,

die letzte Aufgabe scheint nicht so angekommen zu sein. Nachdem (deutlich) mehr als eine Woche ohne Lösung verhangen ist, greife ich den Thread wieder auf. Vielleicht habe ich mit dieser Aufgabe mehr Glück.

Die Aufgabe ist, die Methode

C#-Code:
public static String FormatSI (Decimal d, int significantDigits, String unit)

zu implementieren, um Zahlen zu formatieren, und zwar so, dass folgende Bedingungen erfüllt sind:
  • Für Vielfache von Tausend sollen die passenden  Vorsätze für Maßeinheiten verwendet werden (k, M, G, ..., m, µ, n, ...), so dass die eigentliche Zahl (Mantisse) immer ein bis drei Stellen vor dem Komma hat (ohne dass die Ziffer Null alleine vor dem Komma steht, es sei denn, die Zahl ist Null). Die Zahl 123456 würde also z.B. als 123k dargestellt werden. Die Zahl 0,1 würde als 100m (m für Milli, nicht Meter) dargestellt werden.

  • Ist die Zahl 10^27 oder größer oder ist die Zahl 10^-27 oder kleiner, soll statt der SI-Präfixe die Darstellung des Exponenten mit einem vorangestellten E erfolgen, also z.B. 123E27. Der verwendete Exponent muss dabei immer durch drei teilbar sein (27, 30, 33, ...).

  • Der Parameter significantDigits bestimmt, wie viele Ziffern dargestellt werden sollen bzw. auf wieviele Ziffern gerundet werden soll. Die Zahl 123456 würde also in Abhängigkeit von significantDigits wie folgt dargestellt werden:
    1 = 100k
    2 = 120k
    3 = 123k
    4 = 123,5k (Achtung: hier greift die Rundung)
    5 = 123,46k (Achtung: hier greift die Rundung)
    6 = 123,456k
    7 = 123,4560k

  • Der Parameter unit (z.B. "V") wird einfach an das bisherige Ergebnis angehängt, z.B. 123kV.
Es gibt noch drei weitere Rahmenbedingungen:
  • Es müssen nur positive Zahlen berücksichtigt werden. Die Bedingungen im obigen Text sind entsprechend formuliert. Wer negative Zahlen berücksichtigen will, sollte zu Anfang das Vorzeichen ermitteln und dann mit dem Absolutwert der Zahl weiterrechnen.

  • Die Methode soll keine if-Ketten enthalten. Sowas wie wenn d >= 1000, dann k, sonst wenn d >= 100000, dann M wäre also zu vermeiden. Insgesamt sollte eine elegante, kompakte Lösung angestrebt werden.

  • Testet die Methode gründlich, bevor ihr sie als Lösung postet. Beachtet insbesondere Grenz- und Sonderfälle. Die Methode soll für jede(*) mögliche Decimal-Zahl wie beschrieben funktionieren.
Ich wünsche euch viel Spaß!

herbivore

(*) Ausnahme sind - wie gesagt - negative Zahlen. Wenn ihr negative Zahlen wie beschreiben per Absolutwert behandeln wollt, sind negative Zahlen, die sich mit Math.Abs nicht in eine positive Zahl umwandeln lassen, ausgenommen.
15.02.2013 08:10 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Ace86 Ace86 ist männlich
myCSharp.de-Mitglied

Dabei seit: 21.03.2012
Beiträge: 15
Entwicklungsumgebung: VS 2010


Ace86 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hier ist mein Lösungsvorschlag.
Beim Runden auf die signifikanten Stellen, wollte ich keine eigene Methode schreiben, sondern die Mittel von .Net nutzen. Prinzipiell kann man das beim Umwandeln einer Zahl in eine Zeichenkette mit Hilfe des Formatzeichens G bzw. g lösen. Dabei wird bei der Ausgabe aber immer die kompakteste Schreibweise ausgegeben, so dass unter Umständen nicht die Komma-Schreibweise, sondern wissenschaftliche Notation ausgegeben wird, bsp. aus 123 wird bei 2 Signifkanten Stellen 1E+02. Daher parse ich in diesen Fällen die Zahl erneut als decimal und wandle sie wieder in einen String um. Ist etwas getrickst, aber funktioniert. Wenn jemand eine Idee hat wie man das schöner lösen kann, bzw. allgemeine Verbesserungsvorschläge hat, dann immer her damit. ;-)

C#-Code:
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
    string sign = "";
    string TextOfNumber = "";
    string siPrefix = "";

    if(significantDigits < 1)
        throw new ArgumentException("der Parameter significantDigits muss größer als 0 sein.");

    if(d < 0)
    {
        sign = "-";
        d = Math.Abs(d);
    }

    d = scaleNumberAndGetSIPrefix(d, out siPrefix);
    TextOfNumber = getSIString(d, significantDigits);

    return sign + TextOfNumber + siPrefix + unit;
}


private static decimal scaleNumberAndGetSIPrefix(decimal d, out string prefix)
{
    decimal multiplicationFactor = (d < 1) ? 1000m : 0.001m;
    int indexStep = (d < 1) ? -1 : +1;
    int thousandsIndex = 0;

    if(d != decimal.Zero)
    {
        while(d < 1 || d >= 1000)
        {
            d *= multiplicationFactor;
            thousandsIndex += indexStep;
        }
    }

    prefix = getSIPrefix(thousandsIndex);
    return d;
}


private static string getSIPrefix(int thousandsIndex)
{
    string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };

    if(thousandsIndex <= -9 || thousandsIndex >= 9)
        return "E" + thousandsIndex * 3;

    int offset = prefixes.Length / 2;
    return prefixes[thousandsIndex + offset];
}


private static string getSIString(decimal d, int significantDigits)
{
    // round to significant digits
    string textOfNumber = d.ToString("G" + significantDigits);
    if(textOfNumber.ToLower().Contains('e'))
    {
        // exponential notation to decimal point notation
        textOfNumber = decimal.Parse(textOfNumber, System.Globalization.NumberStyles.Float).ToString();
    }

    bool hasDecimalPoint = textOfNumber.Contains(',') || textOfNumber.Contains('.');
    int requiredLength = significantDigits;
    if(hasDecimalPoint)
        requiredLength++;

    // pad with zeros if necessary
    if(textOfNumber.Length < requiredLength)
    {
        if(!hasDecimalPoint)
        {
            textOfNumber += ",";
            requiredLength++;
        }
        textOfNumber = textOfNumber.PadRight(requiredLength, '0');
    }

    return textOfNumber;
}
20.02.2013 23:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Ace86,

vielen Dank für deinen Lösungsvorschlag. Die Aufteilung in Methoden gefällt mir gut. Das würde ich vermutlich nicht oder nicht viel anders machen.

Nur die Methode getSIString ist, wie du selbst geschrieben hast, nicht so schön. Es wäre wohl schöner, noch auf Ebene der Zahlen auszurechnen, wie korrekt formatiert werden müsste und dann passend zu formatieren, statt erst zu formatieren und dann auf String-Ebene rumzumanipulieren. Auch gibt es Probleme, wenn auf einem Rechner ein anderes Trennzeichen als Punkt oder Komma eingestellt ist.

Schönheit ist der eine Aspekt. Wichtiger ist jedoch die Korrektheit. Leider funktioniert deine Methode FormatSI noch nicht für alle Zahlen korrekt. Hier ein Gegenbeispiel:

FormatSI (950, 1, "") müsste "1k" liefern, liefert aber "1000". Das liegt unter anderem an der getSIString-Methode. Wenn du diese überarbeitest, könntest du versuchen, sie wie vorgeschlagen zu implementieren.

herbivore
21.02.2013 09:59 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Ace86 Ace86 ist männlich
myCSharp.de-Mitglied

Dabei seit: 21.03.2012
Beiträge: 15
Entwicklungsumgebung: VS 2010


Ace86 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Die letzten Tage hatte ich wenig Zeit, aber heute bin ich wieder dazu gekommen die Methode zu überarbeiten:

C#-Code:
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
    string sign = "";
    int thousandsExponent = 0;

    if(significantDigits < 1)
        throw new ArgumentException("der Parameter significantDigits muss größer als 0 sein.");

    if(d < 0)
    {
        sign = "-";
        d = Math.Abs(d);
    }

    d = scaleNumberAndGetSIPrefix(d, out thousandsExponent);
    d = round(d, significantDigits);

    // range overflow
    if(!(d < 1000m))
    {
        d = scaleNumber(d);
        d = round(d, significantDigits);
        thousandsExponent++;
    }

    return sign + getSIText(d, significantDigits) + getSIPrefix(thousandsExponent) + unit;
}


private static decimal scaleNumberAndGetSIPrefix(decimal d, out int thousandsExponent)
{
    decimal multiplicationFactor = (d < 1) ? 1000m : 0.001m;
    int indexStep = (d < 1) ? -1 : +1;
    int expontent = 0;

    if(d != decimal.Zero)
    {
        while(d < 1 || d >= 1000)
        {
            d *= multiplicationFactor;
            expontent += indexStep;
        }
    }

    thousandsExponent = expontent;
    return d;
}


private static decimal scaleNumber(decimal d)
{
    int num;
    return scaleNumberAndGetSIPrefix(d, out num);
}


private static decimal round(decimal d, int significantDigits)
{
    if(d.Equals(decimal.Zero))
        return d;

    double scaleExponent = Math.Floor(Math.Log10((double)d) - significantDigits + 1);
    decimal scaleFactor = (decimal)Math.Pow(10, scaleExponent);

    // scale to significant digits
    decimal a = d / scaleFactor;

    decimal b = Math.Round(a);

    // scale back
    return b * scaleFactor;
}


private static string getSIText(decimal d, int significantDigits)
{
    string textOfNumber = d.ToString();
    string decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
    bool hasDecimalSeparator = textOfNumber.Contains(decimalSeparator);

    int requiredLength = significantDigits;
    if(hasDecimalSeparator)
        requiredLength++;

    if(textOfNumber.Length < requiredLength)
    {
        if(!hasDecimalSeparator)
        {
            textOfNumber += ",";
            requiredLength++;
        }
        textOfNumber = textOfNumber.PadRight(requiredLength, '0');
    }

    return textOfNumber;
}


private static string getSIPrefix(int thousandsIndex)
{
    string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };

    if(thousandsIndex <= -9 || thousandsIndex >= 9)
        return "E" + thousandsIndex * 3;

    int offset = prefixes.Length / 2;
    return prefixes[thousandsIndex + offset];
}
26.02.2013 21:08 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Ace86,

das von mir genannte Rundungsproblem hast du behoben, aber dafür leider ein neues eingebaut. Das liegt daran, dass du einer Stelle fälschlich bzw. unbedachterweise mit double arbeitest.

Double hat nur eine Genauigkeit (Mantisse) von 15-16 Stellen, wogegen Decimal eine Genauigkeit (Mantisse) von 28-29 Stellen hat. Wenn man nun einen Decimal mit einer Mantisse von mehr als 16 Stellen verwendet, kann es zu falschen Ergebnissen führen.

Der folgende Code

C#-Code:
decimal m = 9.9999999999999999m;
Console.WriteLine ("{0:G28}", m);
Console.WriteLine ("{0:G28}", (double)m);
Console.WriteLine (FormatSI (m, 18, ""));
Console.WriteLine (FormatSI (m, 17, ""));

produziert folgende Ausgabe

9,9999999999999999
10
9,99999999999999990
10,000000000000000

Die ersten beiden Zeilen zeigen, dass bei der Umwandlung einer ausreichend genauen decimal-Zahl nahe aber kleiner 10 in einen double, diese zwangsläufig auf 10 aufgerundet wird. In der Folge wird in deiner round-Methode ein falscher scaleExponent berechnet. Deshalb wird anschließend die decimal-Zahl eine Stelle zu weit links gerundet. Dadurch wird in der vierten Zeile 10,000000000000000 ausgegeben, obwohl 9,9999999999999999 ausgegeben werden müsste, denn die Ziffer hinter der letzten 9 - ist wie die dritte Zeile zeigt - eine 0, also dürfte in der vierten Zeile noch nicht aufgerundet werden.

Nenn mich pingelig :-) aber ich hatte direkt in der Aufgabe ausdrücklich gefordert, dass "die Methode für jede mögliche Decimal-Zahl wie beschrieben funktionieren soll".

herbivore

PS: Sobald ich einen Fehler in einem Lösungsvorschlag gefunden habe, höre ich auf, intensiv nach weiteren Fehlern zu suchen. Es können also noch andere Fehler enthalten sein. Insbesondere wird Math.Round möglicherweise nicht so runden, wie es ToString tut, siehe z.B.  Math.Round rundet anders als erwartet. Allerdings habe ich in der Aufgabe keine explizite Aussage dazu getroffen, wie genau gerundet werden soll. Also ist es eine Frage der Interpretation, ob man den Aufgabentext so interpretiert, dass zwangsläufig die in Deutschland übliche kaufmännische Rundung gemeint ist und man eine anderes Rundungsverfahren demzufolge als Fehler ansieht. Die Beispiele jedenfalls sind kaufmännisch gerundet.
27.02.2013 12:52 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Ace86 Ace86 ist männlich
myCSharp.de-Mitglied

Dabei seit: 21.03.2012
Beiträge: 15
Entwicklungsumgebung: VS 2010


Ace86 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Nenn mich pingelig :-) aber ich hatte direkt in der Aufgabe ausdrücklich gefordert, dass "die Methode für jede mögliche Decimal-Zahl wie beschrieben funktionieren soll".

Ist schon richtig, wenn die Methode nicht den Anforderungen entspricht, dann muss sie korrigiert werden. Ich frage mich aber warum nicht alle Methoden der Math Bibliothek auch für decimal implementiert sind.

Zitat:
Insbesondere wird Math.Round möglicherweise nicht so runden, wie es ToString tut, siehe z.B. Math.Round rundet anders als erwartet.

Gut zu wissen, ich bin bisher davon ausgegangen, dass Math.Round standardmäßig kaufmännisch rundet

Und jetzt zum eigentlichen Anliegen. Aller guten Dinge sind (hoffentlich) drei, hier ist meine überarbeitete Version:

C#-Code:
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
    string sign = "";
    int powerOfThousand;
    string textOfNumber;
    string siPrefix;

    if(significantDigits < 1)
        throw new ArgumentException("der Parameter significantDigits muss größer als 0 sein.");

    if(d < 0)
    {
        sign = "-";
        d = Math.Abs(d);
    }

    powerOfThousand = getPowerOfThousand(d);
    d = scaleNumber(d);
    d = roundSignificant(d, significantDigits);

    // range overflow
    if(!(d < 1000m))
    {
        d = scaleNumber(d);
        d = roundSignificant(d, significantDigits);
        powerOfThousand++;
    }

    textOfNumber = getSIText(d, significantDigits);
    siPrefix = getSIPrefix(powerOfThousand);

    return sign + textOfNumber + siPrefix + unit;
}



private static int getPowerOfThousand(decimal d)
{
    int power;
    scaleNumberAndGetPower(d, 1000, out power);
    return power;
}


private static int getPowerOfTen(decimal d)
{
    int power;
    scaleNumberAndGetPower(d, 10, out power);
    return power;
}


private static decimal scaleNumber(decimal d)
{
    int power;
    return scaleNumberAndGetPower(d, 1000, out power);
}


private static decimal scaleNumberAndGetPower(decimal d, int upperBound, out int power)
{
    decimal factor = (d < 1) ? upperBound : 1m / upperBound;
    int indexStep = (d < 1) ? -1 : +1;
    int expontent = 0;

    if(d != decimal.Zero)
    {
        while(d < 1 || d >= upperBound)
        {
            d *= factor;
            expontent += indexStep;
        }
    }

    power = expontent;
    return d;
}


private static decimal roundSignificant(decimal d, int significantDigits)
{
    if(d.Equals(decimal.Zero))
        return d;

    decimal scaleFactor = (decimal)Math.Pow(10, getPowerOfTen(d) - significantDigits + 1);

    // scale to significant digits
    decimal a = d / scaleFactor;

    decimal b = Math.Round(a, MidpointRounding.AwayFromZero);

    // scale back
    return b * scaleFactor;
}



private static string getSIText(decimal d, int significantDigits)
{
    string textOfNumber = d.ToString();

    if(d == decimal.Zero && significantDigits > 1)
    {
        string decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
        int requiredLength = significantDigits + 1;

        textOfNumber += ",";
        textOfNumber = textOfNumber.PadRight(requiredLength, '0');
    }

    return textOfNumber;
}


private static string getSIPrefix(int powerOfThousand)
{
    string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };

    if(powerOfThousand <= -9 || powerOfThousand >= 9)
        return "E" + powerOfThousand * 3;

    int offset = prefixes.Length / 2;
    return prefixes[powerOfThousand + offset];
}
28.02.2013 23:03 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Ace86,

ich gebe es zu, ich suche natürlich extra nach den fiesen Grenzfällen. Ein Decimal hat 28-29 signifikante Stellen. Wenn der Aufrufer von FormatSI die maximal mögliche Genauigkeit haben will, egal welche Zahl er übergibt, wird er 29 als Parameter für significantDigits übergeben. Dann kommt es aber auf die Größe der übergebenen decimal-Zahl an, ob deine Methode funktioniert.

C#-Code:
{
   decimal m = 7.1234567890123456789012345678m;
   Console.WriteLine ("{0:G29}", m);
   Console.WriteLine (FormatSI (m, 28, ""));
   Console.WriteLine (FormatSI (m, 29, ""));
}

{
   decimal m = 8.1234567890123456789012345678m; // *
   Console.WriteLine ("{0:G29}", m);
   Console.WriteLine (FormatSI (m, 28, ""));
   Console.WriteLine (FormatSI (m, 29, "")); //**
}

Bei (**) kommt folgende Ausnahme:

Fehlermeldung:
Unbehandelte Ausnahme: System.OverflowException: Der Wert für eine Decimal war zu groß oder zu klein.
bei System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
bei System.Decimal.op_Division(Decimal d1, Decimal d2)
bei Versuch3.roundSignificant(Decimal d, Int32 significantDigits)
bei Versuch3.FormatSI(Decimal d, Int32 significantDigits, String unit)

(*) Zugegeben, die letzte 8 wird sowieso abgeschnitten (bzw. genaugenommen fürs Aufrunden der Ziffer davor verwendet). Eine decimal-Zahl, die mit 8 beginnt, kann maximal 28 signifikante Stellen haben. Aber zum einen greift hier die Argumentation von oben, dass ein Aufrufer für maximale Genauigkeit pauschal 29 significantDigits angeben möchte. Und außerdem hattest und hast du den Fall, dass significantDigits größer ist als die tatsächliche Anzahl der signifikanten Stellen der Zahl, ja explizit durch das PadRight berücksichtigt. Also sollte FormatSI auch bei significantDigits == 29 (eigentlich sogar bei significantDigits >= 29) immer korrekt funktionieren (wobei ich für significantDigits > 29 damit einverstanden wäre, wenn du den Fall genauso als Argumentfehler ansiehst wie significantDigits < 1).

Ich hoffe du hast den Spaß noch nicht verloren, sondern siehst es im Gegenteil als spannend und interessant an, wie viele Fehler in einer auf den ersten Blick korrekten Implementierung eines an sich überschaubaren Algorithmus stecken können, wenn man sehr genau hinschaut.

Wobei es für mich langsam eng wird. So viele Grenzfälle sehe ich nicht mehr. Du hast also gute Chancen für einen baldigen erfolgreichen Abschluss.

Ich bin sogar eben schon auf einen Grenzfall hineingefallen, bei dem ich dachte, dass deine Methode nicht korrekt funktioniert, sie es aber doch tut. Denn bei

C#-Code:
decimal m = 1.2345678901234567890123456789e-27m;
Console.WriteLine (Versuch3.FormatSI (m, 3, ""));

wird "1,20E-27" ausgegeben, also wo ist die 3 hin? Aber die ist gar nicht in dem decimal gelandet, denn bei e-27 kann ein decimal nur noch max. 2 signifikante Ziffern darstellen, wie auch die Ausgabe "1,2E-27" der folgenden Zeile zeigt:

C#-Code:
Console.WriteLine ("{0:G3}", m);

herbivore
01.03.2013 09:53 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Ace86 Ace86 ist männlich
myCSharp.de-Mitglied

Dabei seit: 21.03.2012
Beiträge: 15
Entwicklungsumgebung: VS 2010


Ace86 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Ich hoffe du hast den Spaß noch nicht verloren, sondern siehst es im Gegenteil als spannend und interessant an, wie viele Fehler in einer auf den ersten Blick korrekten Implementierung eines an sich überschaubaren Algorithmus stecken können, wenn man sehr genau hinschaut.

An dieser Aufgabe sieht man sehr schön wie gemein die "Einschränkung" alle decimal Zahlen (bzw. die die sich mit Math.Abs als positive umwandeln lassen) ist, insbsondere wenn jemand so genau wie du prüft. Der Spaß ist aber noch nicht weg.

Ich betrachte significantDigits > 29 jetzt auch als Argumentfehler. Schon im letzten Lösungsvorschlag wurde PadRight nur noch verwendet, wenn d = 0 ist. Für alle anderen Fälle hatte die ToString-Methode schon den richtigen Wert ausgegeben. Wenn die Zahl wie in deinem Beispiel statt 29 nur 28 signifikante Stellen hat, hänge ich auch keine Null mehr an, da die letzte Stelle des decimals schon von anfang an gerundet sein kann. Darüber lässt sich sicherlich auch streiten. Es wäre aber auch kein Problem das anfügen der Nullen wieder wie in früheren Vorschlägen durchzuführen.

C#-Code:
public static string FormatSI(Decimal d, int significantDigits, String unit)
{
    string sign = "";
    int powerOfThousand;
    string textOfNumber;
    string siPrefix;

    if(significantDigits < 1)
        throw new ArgumentException("Der Parameter \"significantDigits\" muss größer als 0 sein.");

    if(significantDigits > 29)
        throw new ArgumentException("Der Parameter \"significantDigits\" darf nicht größer als 29 sein.");

    if(d < 0)
    {
        sign = "-";
        d = Math.Abs(d);
    }

    powerOfThousand = getPowerOfThousand(d);
    d = scaleNumber(d);
    d = roundSignificant(d, significantDigits);

    // range overflow
    if(!(d < 1000m))
    {
        d = scaleNumber(d);
        d = roundSignificant(d, significantDigits);
        powerOfThousand++;
    }

    textOfNumber = getSIText(d, significantDigits);
    siPrefix = getSIPrefix(powerOfThousand);

    return sign + textOfNumber + siPrefix + unit;
}



private static int getPowerOfThousand(decimal d)
{
    int power;
    scaleNumberAndGetPower(d, 1000, out power);
    return power;
}


private static int getPowerOfTen(decimal d)
{
    int power;
    scaleNumberAndGetPower(d, 10, out power);
    return power;
}


private static decimal scaleNumber(decimal d)
{
    int power;
    return scaleNumberAndGetPower(d, 1000, out power);
}


private static decimal scaleNumberAndGetPower(decimal d, int upperBound, out int power)
{
    decimal factor = (d < 1) ? upperBound : 1m / upperBound;
    int indexStep = (d < 1) ? -1 : +1;
    int expontent = 0;

    if(d != decimal.Zero)
    {
        while(d < 1 || d >= upperBound)
        {
            d *= factor;
            expontent += indexStep;
        }
    }

    power = expontent;
    return d;
}


private static decimal roundSignificant(decimal d, int significantDigits)
{
    if(d.Equals(decimal.Zero))
        return d;

    try
    {
        decimal scaleFactor = (decimal)Math.Pow(10, getPowerOfTen(d) - significantDigits + 1);

        decimal a = d / scaleFactor;
        decimal b = Math.Round(a, MidpointRounding.AwayFromZero);
        return b * scaleFactor;
    }
    catch(OverflowException)
    {
        return roundSignificant(d, --significantDigits);
    }
}



private static string getSIText(decimal d, int significantDigits)
{
    string textOfNumber = d.ToString();

    if(d == decimal.Zero && significantDigits > 1)
    {
        string decimalSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
        int requiredLength = significantDigits + 1;

        textOfNumber += ",";
        textOfNumber = textOfNumber.PadRight(requiredLength, '0');
    }

    return textOfNumber;
}


private static string getSIPrefix(int powerOfThousand)
{
    string[] prefixes = { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" };

    if(powerOfThousand <= -9 || powerOfThousand >= 9)
        return "E" + powerOfThousand * 3;

    int offset = prefixes.Length / 2;
    return prefixes[powerOfThousand + offset];
}
05.03.2013 22:34 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Ace86,

Zitat:
Schon im letzten Lösungsvorschlag wurde PadRight nur noch verwendet, wenn d = 0 ist.

das war mir zwar auch aufgefallen, aber erst jetzt, als ich nochmal genauer hingeschaut habe, ist mir aufgegangen, warum du in den meisten Fällen ohne PadRight auskommst. Dem ganzen liegt zu Grunde, dass es bei decimal für die meisten Zahlen mehrere unterschiedliche Repräsentationen mit demselben Zahlenwert gibt.

Klarer wird das, wenn man sich die  Decimal.GetBits-Methode anschaut. Demzufolge besteht ein Decimal aus einer vorzeichenlosen 96bit Integer-Zahl (Mantisse), einem Bit für das Vorzeichen und einer Zahl zwischen 0 und 28, die bestimmt, um wieviele Stellen das Komma nach links verschoben werden soll.

Dadurch lässt sich z.B. die Zahl 10 auf verschiedene Arten repräsentieren, nämlich als Integer-Zahl 10 ohne Kommaverschiebung, als Integer-Zahl 100 mit einer Kommaverschiebung um eins nach links, als Integer-Zahl 1000 mit einer Kommaverschiebung um zwei nach links, usw.

Obwohl alle diese Repräsentationen den selben Zahlenwert haben, nämlich 10 und deshalb ein Vergleich mit == oder Equals auch bei unterschiedlichen Repräsentationen true liefert, werden die Zahlen doch unterschiedlich ausgegeben. Folgende Zeilen

C#-Code:
Console.WriteLine (new Decimal (10, 0, 0, false, 0));
Console.WriteLine (new Decimal (100, 0, 0, false, 1));
Console.WriteLine (new Decimal (1000, 0, 0, false, 2));

produzieren die folgende Ausgabe:

10
10,0
10,00

Das machst du dir - ob nun bewusst oder unbewusst - in der Methode roundSignificant zu nutze. Wenn z.B. die Zahl 123 mit fünf signifikanten Stellen ausgegeben werden soll, ist dein scaleFactor 0.01m. Der folgende Code

C#-Code:
decimal m = 123m;
Console.WriteLine (m);
m /= 0.01m;
Console.WriteLine (m);
m *= 0.01m;
Console.WriteLine (m);

produziert folgende Ausgabe:

123
12300
123,00

Obwohl die Ausgangszahl am Ende wieder den gleichen Zahlenwert hat, hat sich dennoch ihre Repräsentation und damit ihre Ausgabe geändert. Statt zu Anfang als 123 ohne Kommaverschiebung, wird sie am Ende als 12300 mit einer Kommaverschiebung um zwei Stellen nach links repräsentiert.

Kleiner Einschub
Interessanterweise würde das nicht passieren, wenn man Division und Multiplikation vertauschen und zum Ausgleich mit dem Kehrwert des scaleFactors rechen würde, also 100m statt 0.01m. Der folgende Code

C#-Code:
decimal m = 123m;
Console.WriteLine (m);
m *= 100m;
Console.WriteLine (m);
m /= 100m;
Console.WriteLine (m);

produziert folgende Ausgabe:

123
12300
123

Hier hat die Zahl am Ende die gleiche Repräsentation wie am Anfang.

Ich habe allerdings nicht geschaut, ob dieses (unterschiedliche) Verhalten irgendwo verbindlich spezifiziert ist. Wenn das nicht der Fall ist, könnte sich das Verhalten bei einer neuen Framework und/oder Compiler-Version ändern, und wäre somit möglicherweise nicht zukunftssicher. Aber das nur am Rande.

Kommen wir nach dieser Vorrede zu deiner Korrektur im aktuellen Lösungsvorschlag gegenüber dem vorherigen. Diese basiert darauf, die Overflow-Exception, die bei manchen Zahlen auftreten kann, wenn 29 signifikate Stellen gewünscht sind, abzufangen und mit einer signifikanten Stelle weniger zu runden. Das ist insofern nicht schlimm, als die Zahlen, bei denen die Overflow-Exception auftreten kann, ohnehin nur maximal 28 signifikante Stellen haben können. Es wird also trotzdem korrekt gerundet.

Allerdings verschiebt der scaleFactor dadurch die Mantisse auch um eine Stelle weniger und man bekommt entsprechend eine Repräsentation mit einer Stelle in der Mantisse weniger und entsprechend einer Kommaverschiebung um eine Stelle weniger. Im Ergebnis gibt ToString dadurch auch eine Stelle weniger aus.

Du sagst, dass man darüber streiten kann, ob in dem Fall zum Ausgleich eine Null angehängt werden müsste. Ich finde es allerdings eindeutig, dass das passieren müsste. Dazu zunächst folgende Test-Methode

C#-Code:
private static void Test (decimal d)
{
   for (int i = 25; i <= 29; ++i) {
      Console.WriteLine ("{0,2} {1}", i, FormatSI (d, i, ""));
   }
   Console.WriteLine ();
}

und folgende Aufrufe:

C#-Code:
Test (7.1234567890123456789012345678m);
Test (8.1234567890123456789012345678m); // Wie bekannt: die letzte 8 landet eh nicht im decimal

Test (7);
Test (8);

Die beiden ersten Zeilen produzieren folgende Ausgabe:

25 7,123456789012345678901235
26 7,1234567890123456789012346
27 7,12345678901234567890123457
28 7,123456789012345678901234568
29 7,1234567890123456789012345678

25 8,123456789012345678901235
26 8,1234567890123456789012346
27 8,12345678901234567890123457
28 8,123456789012345678901234568
29 8,123456789012345678901234568

Bis hierhin könnte man noch sagen, ok, die letzte Ziffer im zweiten Beispiel ist ja auch nicht im Decimal gelandet, also wird sie korrekterweise nicht mit ausgegeben, aber wenn man sich die Ausgabe der letzten beiden Test-Aufrufe ansieht

25 7,000000000000000000000000
26 7,0000000000000000000000000
27 7,00000000000000000000000000
28 7,000000000000000000000000000
29 7,0000000000000000000000000000

25 8,000000000000000000000000
26 8,0000000000000000000000000
27 8,00000000000000000000000000
28 8,000000000000000000000000000
29 8,000000000000000000000000000

dann sieht man, dass gewünschte signifikante Stellen, die in dem Wert gar nicht vorhanden sind, trotzdem immer als folgende Nullen ausgegeben werden, nur eben nicht in der allerletzten Zeile, die für den Benutzer der Methode nicht nachvollziehbar eine Null am Ende weniger enthält.

Allerdings stimmte ich dir darin zu, dass es kein Problem wäre, das Anfügen dieser letzten Null auch noch einzubauen.

Deshalb und da ich auch sonst keine Fehler mehr gefunden habe, erkläre ich die Aufgabe für gelöst. Ich hoffe, es hat dir und allen Mitleseren Spaß gemacht und einige neue Erkenntnisse beschert.

Du bist dran, die nächste Aufgabe zu stellen.

herbivore
06.03.2013 10:48 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Ace86 Ace86 ist männlich
myCSharp.de-Mitglied

Dabei seit: 21.03.2012
Beiträge: 15
Entwicklungsumgebung: VS 2010


Ace86 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Das Ziel die Aufgabe besteht darin eine Morphing-Animation zweier geometrischer Figuren auf der Konsole zu erstellen. Dabei soll ein zeitgesteuerter gleichmäßiger Übergang von einem Quadrat zu einem Rhombus mit gleichlangen Diagonalen erzeugt werden. Die Länge der Diagonalen im Rhombus ist gleich der Seitenlänge des Quadrates. Um das zu verdeutlichen habe ich ein Bild angehangen.

Zu Beginn ist die blaue Form zu sehen, welche in die rote Form übergeht. Nachdem die Endform erreicht ist, soll die Animation rückwärts ablaufen, bis wieder die ursprüngliche Form erreicht ist. Die Animation läuft unendlich lange.

Zusätzlich ist zu beachten, dass die Animation für verschieden große Quadrate funktionieren soll und dass zu jeder Zeit der Animation eine vollständig umschlossene Form sichtbar ist, die zur x- und y- Achse symmetrisch ist. Die Farbe der Form ist beliebig.

Es empfiehlt sich in der Konsole Zeichen gleicher Höhe und Breite zu verwenden. Zum zeichnen einer Linie kann die untenstehende Methode verwendet werden.

C#-Code:
public void drawLine(int x1, int y1, int x2, int y2, ConsoleColor bgColor)
{
    ConsoleColor BackgroundColorTemp = Console.BackgroundColor;
    int cursorLeftTemp = Console.CursorLeft;
    int cursorTopTemp = Console.CursorTop;

    Console.BackgroundColor = bgColor;

    int dx = Math.Abs(x2 - x1), sx = x2 < x1 ? -1 : 1;
    int dy = -Math.Abs(y2 - y1), sy = y2 < y1 ? -1 : 1;
    int err = dx + dy, e2;

    while(true)
    {
        Console.CursorLeft = x1;
        Console.CursorTop = y1;
        Console.Write(" ");
        if(x1 == x2 && y1 == y2) break;
        e2 = 2 * err;
        if(e2 > dy) { err += dy; x1 += sx; }
        if(e2 < dx) { err += dx; y1 += sy; }
    }


    Console.BackgroundColor = BackgroundColorTemp;
    Console.CursorLeft = cursorLeftTemp;
    Console.CursorTop = cursorTopTemp;
}

Ace86 hat dieses Bild (verkleinerte Version) angehängt:
Form.gif
Volle Bildgröße

07.03.2013 20:26 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Alf Ator
myCSharp.de-Mitglied

avatar-586.gif


Dabei seit: 30.10.2007
Beiträge: 614
Entwicklungsumgebung: VS2005 / VS2008


Alf Ator ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ist aber ne ganze Menge geworden..

Ich hoffe, dass stört nicht, dass die 'Pixel' in der Console nicht quatratisch sind. Das kann man erreichen, in dem eine Verknüpfung an die kompilierte Exe erstellt und die Schriftgröße auf z.B. 8x8 einstellt. Andere Möglichkeit ist über die WinAPI. Ich finde aber, dass der Code schon umfangreich genug ist.

C#-Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace RombusTest
{
    class Program
    {
        static void Main(string[] args)
        {
            CubeToRombusMorpher morpher = new CubeToRombusMorpher();
            morpher.Morph(3, 3, 60, 100);

            Console.ReadLine();
        }
    }

    public class CubeToRombusMorpher
    {
        private Timer timer;
        private List<Oktaeder> oktaeders;
        private IEnumerator<Oktaeder> enumerator;

        private class Point
        {
            public int X { get; set; }
            public int Y { get; set; }

            public Point(int x, int y)
            {
                X = x;
                Y = y;
            }
            public override string ToString()
            {
                return String.Format("({0}/{1})", X, Y);
            }
        }

        private class Oktaeder
        {
            public Point P1 { get; set; }
            public Point P2 { get; set; }
            public Point P3 { get; set; }
            public Point P4 { get; set; }
            public Point P5 { get; set; }
            public Point P6 { get; set; }
            public Point P7 { get; set; }
            public Point P8 { get; set; }

            public Oktaeder(int mx1, int my1, int mx2, int my2, int mx3, int my3, int mx4, int my4, int mx5, int my5, int mx6, int my6, int mx7, int my7, int mx8, int my8)
            {
                P1 = new Point(mx1, my1);
                P2 = new Point(mx2, my2);
                P3 = new Point(mx3, my3);
                P4 = new Point(mx4, my4);
                P5 = new Point(mx5, my5);
                P6 = new Point(mx6, my6);
                P7 = new Point(mx7, my7);
                P8 = new Point(mx8, my8);
            }

            public override string ToString()
            {
                return String.Join(", ", P1, P2, P3, P4, P5, P6, P7, P8);
            }
        }

        public void Morph(int x1, int y1, int length, int interval)
        {
            InitConsole(x1, y1, length);
            InitTimer(interval);
            InitOktaeders(x1, y1, length);
            DrawCube(x1, y1, x1 + length, y1 + length, ConsoleColor.Green);
            Console.ReadLine();
            timer.Start();
        }

        private void InitConsole(int x1, int y1, int length)
        {
            Console.SetWindowSize((x1 * 2) + length, (y1 * 2) + length);
            Console.SetBufferSize((x1 * 2) + length, (y1 * 2) + length);
        }

        private void InitOktaeders(int x1, int y1, int length)
        {
            oktaeders = new List<Oktaeder>();
            for (int i = 0; i <= (length / 2); i++)
            {
                int mx1 = x1,               my1 = y1 + i;
                int mx2 = x1 + i,           my2 = y1;
                int mx3 = x1 + length - i,  my3 = y1;
                int mx4 = x1 + length,      my4 = y1 + i;
                int mx5 = x1 + length,      my5 = y1 + length - i;
                int mx6 = x1 + length - i,  my6 = y1 + length;
                int mx7 = x1 + i,           my7 = y1 + length;
                int mx8 = x1,               my8 = y1 + length - i;
                oktaeders.Add(new Oktaeder(mx1, my1, mx2, my2, mx3, my3, mx4, my4, mx5, my5, mx6, my6, mx7, my7, mx8, my8));
            }
            enumerator = oktaeders.GetEnumerator();
        }

        private void InitTimer(int interval)
        {
            timer = new Timer();
            timer.Interval = interval;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
        }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            timer.Stop();
            Console.Clear();

            if (enumerator.MoveNext())
            {
                DrawOktaeder(enumerator.Current, ConsoleColor.Magenta);
                Console.WriteLine();
                Console.WriteLine(enumerator.Current);
            }
            else
            {
                oktaeders.Reverse();
                enumerator = oktaeders.GetEnumerator();
                if (enumerator.MoveNext())
                {
                    DrawOktaeder(enumerator.Current, ConsoleColor.Magenta);
                    Console.WriteLine();
                    Console.WriteLine(enumerator.Current);
                }
            }

            timer.Start();
        }

        private void DrawOktaeder(Oktaeder oktaeder, ConsoleColor color)
        {
            DrawLine(oktaeder.P1.X, oktaeder.P1.Y, oktaeder.P2.X, oktaeder.P2.Y, color);
            DrawLine(oktaeder.P2.X, oktaeder.P2.Y, oktaeder.P3.X, oktaeder.P3.Y, color);
            DrawLine(oktaeder.P3.X, oktaeder.P3.Y, oktaeder.P4.X, oktaeder.P4.Y, color);
            DrawLine(oktaeder.P4.X, oktaeder.P4.Y, oktaeder.P5.X, oktaeder.P5.Y, color);
            DrawLine(oktaeder.P5.X, oktaeder.P5.Y, oktaeder.P6.X, oktaeder.P6.Y, color);
            DrawLine(oktaeder.P6.X, oktaeder.P6.Y, oktaeder.P7.X, oktaeder.P7.Y, color);
            DrawLine(oktaeder.P7.X, oktaeder.P7.Y, oktaeder.P8.X, oktaeder.P8.Y, color);
            DrawLine(oktaeder.P8.X, oktaeder.P8.Y, oktaeder.P1.X, oktaeder.P1.Y, color);
        }

        private void DrawRombus(int x1, int y1, int x2, int y2, ConsoleColor color)
        {
            DrawLine(x1, (y1 + y2) / 2, (x1 + x2) / 2, y1, color);
            DrawLine((x1 + x2) / 2, y1, x2, (y1 + y2) / 2, color);
            DrawLine(x1, (y1 + y2) / 2, (x1 + x2) / 2, y2, color);
            DrawLine((x1 + x2) / 2, y2, x2, (y1 + y2) / 2, color);
        }

        private void DrawCube(int x1, int y1, int x2, int y2, ConsoleColor color)
        {
            DrawLine(x1, y1, x2, y1, color);
            DrawLine(x1, y1, x1, y2, color);
            DrawLine(x1, y2, x2, y2, color);
            DrawLine(x2, y1, x2, y2, color);
        }

        private void DrawLine(Point p1, Point p2, ConsoleColor color)
        {
            DrawLine(p1.X, p1.Y, p2.X, p2.Y, color);
        }
        private void DrawLine(int x1, int y1, int x2, int y2, ConsoleColor color)
        {
            ConsoleColor BackgroundColorTemp = Console.BackgroundColor;
            int cursorLeftTemp = Console.CursorLeft;
            int cursorTopTemp = Console.CursorTop;

            Console.BackgroundColor = color;

            int dx = Math.Abs(x2 - x1), sx = x2 < x1 ? -1 : 1;
            int dy = -Math.Abs(y2 - y1), sy = y2 < y1 ? -1 : 1;
            int err = dx + dy, e2;

            while (true)
            {
                Console.CursorLeft = x1;
                Console.CursorTop = y1;
                Console.Write(" ");
                if (x1 == x2 && y1 == y2) break;
                e2 = 2 * err;
                if (e2 > dy) { err += dy; x1 += sx; }
                if (e2 < dx) { err += dx; y1 += sy; }
            }

            Console.BackgroundColor = BackgroundColorTemp;
            Console.CursorLeft = cursorLeftTemp;
            Console.CursorTop = cursorTopTemp;
        }
    }
}

Edit: Da macht man noch ne winzige Änderung und hat gleich mal nen Fehler rein.. korrigiert.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Alf Ator am 08.03.2013 13:13.

08.03.2013 13:03 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Ace86 Ace86 ist männlich
myCSharp.de-Mitglied

Dabei seit: 21.03.2012
Beiträge: 15
Entwicklungsumgebung: VS 2010


Ace86 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Vielen Dank für deine Lösung.
Um die 8x8 Pixel einzustellen reicht es beispielweise einmal den Debugmodus im VS zu starten und einen Rechtsklick auf die Titelleiste der Konsole zu machen und die Eigenschaften entsprechend anzupassen. Die werden gespeichert und bleiben beim nächsten Start bestehen. Aber das ist nur ein Detail, dass jeder selbst einstellen kann und auch nicht in der Aufgabenstellung gefordert war.

Ansonsten habe ich mir die Umsetzung genau so vorgestellt, auch der Quellcode ist schön übersichtlich geworden. Wenn du die DrawRombus-Methode noch entfernst ist er noch ein wenig kürzer Augenzwinkern . Die Aufgabe ist damit gelöst und du darfst die nächste stellen.
08.03.2013 19:45 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Alf Ator
myCSharp.de-Mitglied

avatar-586.gif


Dabei seit: 30.10.2007
Beiträge: 614
Entwicklungsumgebung: VS2005 / VS2008


Alf Ator ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ok, danke!

Ich hab mir das was überlegt:
Ihr sollt die Methode GetShortestWay für die Klasse Link implementieren.

C#-Code:
class Link
{
    public int ID { get; set; }
    public List<Link> Links { get; set; }

    public Link(int id)
    {
        this.ID = id;
        this.Links = new List<Link>();
    }

    public string GetShortestWay(int startID, int goalID)
    {
        // Viel Spaß!

        // [ ... ]

        return "0 -> 7 -> 3 -> 15 -> 4";
    }

    private void Add(Link link)
    {
        if(!this.Links.Contains(link))
            this.Links.Add(link);
    }

    private Link GetRandomLink(Random random)
    {
        int index = random.Next(0, this.Links.Count + 1);
        if (index >= this.Links.Count)
            return this;
        else
            return this.Links[index].GetRandomLink(random);
    }

    public static Link CreateRootLink(int maxLinkCount, int randomLinksCount)
    {
        Link rootLink = new Link(0);
        Random random = new Random(DateTime.Now.Millisecond);
        for (int i = 1; i < maxLinkCount; i++)
            rootLink.GetRandomLink(random).Add(new Link(i));
        for (int i = 1; i < randomLinksCount; i++)
            rootLink.GetRandomLink(random).Add(rootLink.GetRandomLink(random));
        return rootLink;
    }

    public override string ToString()
    {
        return String.Format("{0}: {1} ({2})", this.ID, this.Links.Count,String.Join(", ", this.Links.Select(link => link.ID)));
    }
}
15.03.2013 17:06 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Scavanger Scavanger ist männlich
myCSharp.de-Mitglied

avatar-3209.jpg


Dabei seit: 13.03.2008
Beiträge: 309
Entwicklungsumgebung: MS VS C# 2010/2013 Prof.


Scavanger ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Servus,


ich hab das ganze ganz klassisch mit einer  Breitensuche gelöst (Kürzester Weg in einem Graphen).
Die ursprüngliche Klasse habe ich noch um das Property "parent" erweitert um einfacher den Weg rekonstruieren zu können.
Es wird auch der Weg zwischen zwei beliebigen (nicht nur root-Knoten "0" als Start) Knoten ("Links") gefunden, sofern er existiert, allerdings nur "nach oben" in den Objektbaum hinein, nicht nach unten, da die Objekte keinen "Eltern" Knoten kennen, und mein Algorithmus abbricht wenn er das Kind gefunden hat, ansonsten müsste der komplette Objektbaum durchlaufen werden um die "Eltern"-Beziehung herzustellen.

Ich hoffe es reicht, trotzdem:

C#-Code:
class Link
{
    public int ID { get; set; }
    public List<Link> Links { get; set; }

    public Link Parent { get; set; }

    public Link(int id)
    {
        this.ID = id;
        this.Links = new List<Link>();
    }

    private static Link BfsSearch(Link start, int goalID)
    {
        Queue<Link> queue = new Queue<Link>();
        Link endLink = null;
        List<Link> visited = new List<Link>();

        queue.Enqueue(start);
        visited.Add(start);

        while (queue.Count != 0)
        {
            Link curLink = queue.Dequeue();

            if (curLink.ID == goalID)
            {
                endLink = curLink;
                break;
            }

            foreach (Link child in curLink.Links)
            {
                if (!visited.Contains(child))
                {
                    child.Parent = curLink;
                    visited.Add(child);
                    queue.Enqueue(child);
                }
            }
        }

        return endLink;
    }

    public string GetShortestWay(int startID, int goalID)
    {
        Link endLink = BfsSearch(startID != this.ID ? GetLinkById(startID) : this, goalID);

        if (endLink == null)
            return string.Empty;

        List<int> way = new List<int>();
        while (endLink.ID != startID)
        {
            way.Add(endLink.ID);
            endLink = endLink.Parent;
        }
        way.Add(startID);
        way.Reverse();
        return String.Join(" -> ", way);

    }

    public Link GetLinkById(int id)
    {
        return BfsSearch(this, id);
    }

    // ... (siehe oben)

}
18.03.2013 22:28 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Alf Ator
myCSharp.de-Mitglied

avatar-586.gif


Dabei seit: 30.10.2007
Beiträge: 614
Entwicklungsumgebung: VS2005 / VS2008


Alf Ator ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Scavanger,

danke für die schöne Lösung. (Ich konnte erst jetzt antworten, weil ich krank im Bett lag.)

Also, du bist jetzt dran.

Gruß, Alf
25.03.2013 09:35 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 2 Monate.
Mandy
myCSharp.de-Mitglied

Dabei seit: 25.04.2008
Beiträge: 47
Entwicklungsumgebung: VS2010 .. VS2012


Mandy ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Scavanger,

nachdem du dich nicht mehr meldest, stelle ich da ein Rätsel ein, das ich gerade erhalten habe:

Ich suche eine Zahl:
Stelle ich sie im Zweiersystem dar, so endet sie auf 0 und ist 11-stellig.
Stelle ich sie im Vierersystem dar, so ergibt sich für die Quersumme der Wert 11.
Stelle ich sie im Sechzehnersystem dar, so ergibt sich für die Quersumme ein Wert von 26 und für die alternierende Quersumme einer von 0.
Wie lautet die von mir gesuchte Zahl?
(Die jeweilige Quersumme ist im Zehnersystem angegeben!)

Erhalten habe ich das von einem Mathematiker, nachdem das nicht mein Fach ist, habe ich mich an eine empirische Ermittlung (aber mit reduziertem Zahlenraum) gemacht.

Bin gespannt, was euch dazu einfällt...

Mandy
30.05.2013 12:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
pdelvo pdelvo ist männlich
myCSharp.de-Mitglied

avatar-3354.png


Dabei seit: 02.11.2008
Beiträge: 1.346
Entwicklungsumgebung: Visual Studio 2012 Prof.


pdelvo ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hier ist meine Lösung:

C#-Code:
class Program
{
    private static void Main()
    {
        for (var i = (int) Math.Pow(2, 10); i < Math.Pow(2, 11); i++) // 11 Stellig im 2er System
            if (i%2 == 0 // Endet im 2er-System auf 0
                && DigitSum(i, 4) == 11 // Quersumme zur Basis 4 = 11
                && DigitSum(i, 16) == 26 // Quersumme zur Basis 16 = 26
                && AlterniteDigitSum(i, 16) == 0) // alternierende Quersumme zur Basis 16 = 0
                    Console.WriteLine(i);
        Console.ReadKey();
    }

    static int DigitSum(int number, int toBase)
    {
        return ToString(number, toBase).Select(a => FromString(a.ToString(), toBase)).Sum();
    }
    static int AlterniteDigitSum(int number, int toBase)
    {
        bool add = false;
        int sum = 0;
        foreach (char a in ToString(number, toBase).Reverse())
        {
            sum += add ? FromString(a.ToString(), toBase) : -FromString(a.ToString(), toBase);
            add = !add;
        }
        return sum;
    }

    static string ToString(int number, int toBase)
    {
        var resultingString = new StringBuilder();

        const string mapping = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        do
        {
            resultingString.Append(mapping[number % toBase]);
            number /= toBase;
        } while (number > 0);

        return new string(resultingString.ToString().Reverse().ToArray());
    }

    static int FromString(string number, int toBase)
    {
        var resultingNumber = 0;

        const string mapping = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        foreach (var ch in number)
        {
            resultingNumber *= toBase;
            resultingNumber += mapping.IndexOf(ch);
        }

        return resultingNumber;
    }
}

Die Lösung ist 2006 ;)
30.05.2013 13:53 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Sarc
myCSharp.de-Mitglied

Dabei seit: 29.09.2008
Beiträge: 417
Entwicklungsumgebung: VS 2012


Sarc ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Mandy:
Bin gespannt, was euch dazu einfällt...

Google :P
 Mathe-Board: Knobelecke: Gesucht wird eine Zahl...
30.05.2013 14:00 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Mandy
myCSharp.de-Mitglied

Dabei seit: 25.04.2008
Beiträge: 47
Entwicklungsumgebung: VS2010 .. VS2012


Mandy ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo pdelvo,

die DigitSum gefällt mir, darauf wär ich nicht gekommen.

Bin gespannt, was du für eine Aufgabe hast...

Mandy
04.06.2013 11:37 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 3 Monate.
Scavanger Scavanger ist männlich
myCSharp.de-Mitglied

avatar-3209.jpg


Dabei seit: 13.03.2008
Beiträge: 309
Entwicklungsumgebung: MS VS C# 2010/2013 Prof.


Scavanger ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Vielleicht hat ja jemand Lust darauf:

Ich suche eine Implementierung für folgende Erwieterungsmethode:

C#-Code:
public static bool Contains<T>(this IEnumerable<T> enumerable, IEnumerable<T> item, IEqualityComparer<T> comparer)

Die Überladung soll prüfen ob innerhalb einer Auflistung die selben Elemente einer anderen Auflistung vorkommen in der gleichen Reihenfolge und Anzahl an beliebiger Stelle.
Beispiel:

A ("enumerable"):
  • a
  • b
  • c
  • d
  • e
  • f
  • g
B ("item):
  • c
  • d
Soll TRUE ergeben:

ebenso:
  • a
  • b
oder
  • e
  • f
  • g
aber nicht:
  • b
  • c
  • e
oder
  • f
  • g
  • h
12.09.2013 10:48 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz


Programmierhans ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zugegeben eine plumpe Lösung:

C#-Code:
public static bool Contains<T>(IEnumerable<T> enumerable, IEnumerable<T> item, IEqualityComparer<T> comparer)
{

    StringBuilder sbA = new StringBuilder();
        StringBuilder sbB = new StringBuilder();

        foreach (var a in enumerable)
        {
            sbA.Append(a.ToString());
        }

        foreach (var b in item)
        {
            sbB.Append(b);
        }

        return sbA.ToString().IndexOf(sbB.ToString())>-1;
}
12.09.2013 13:40 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
pdelvo pdelvo ist männlich
myCSharp.de-Mitglied

avatar-3354.png


Dabei seit: 02.11.2008
Beiträge: 1.346
Entwicklungsumgebung: Visual Studio 2012 Prof.


pdelvo ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo!

@Programmierhans

Leider enthällt deine Lösung einige Fehler:
  • Du kannst nicht davon ausgehen, dass die Klasse ToString korrekt implementiert, und vorallem kannst du nicht davon ausgehen, dass Die ToString zuordnung eindeutig ist
  • Sei enumerable { 10, 11, 12}, dann ist bei deiner Lösung {1, 1} oder {1, 2} enthalten
  • Du kannst nicht dsavon ausgehen das enumerable endlich ist. Es kann ja z.B. eine Auflistung aller Primzahlen sein, und somit nie enden, und trotzdem ist {2, 3, 5, 7} enthalten
Hier ist meine Lösung:

Sie mag zwar nicht wirklich die schnellste sein, aber ich denke sie funktioniert:

C#-Code:
        public static bool All<T>(this IEnumerable<T> items, Func<T, bool> predicate, out int? count)
        {
            int i = 0;

            foreach (var item in items)
            {
                if (!predicate(item))
                {
                    count = null;
                    return false;
                }
                i++;
            }
            count = i;
            return true;
        }


        public static bool Contains<T>(this IEnumerable<T> enumerable, IEnumerable<T> item,
                                       IEqualityComparer<T> comparer)
        {
            var itemCount = item.Count();

            if (itemCount == 0) return true;

            var index = 0;
            foreach (var temp in enumerable.Select(currentStart => enumerable.Skip(index).Zip(item, comparer.Equals)))
            {
                int? count;
                temp.All(a => a, out count);
                if (count == itemCount) return true;
                index++;
            }
            return false;
        }

LG pdelvo
12.09.2013 15:07 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
HiGHteK HiGHteK ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.03.2007
Beiträge: 114


HiGHteK ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

ich hab mich auch mal eben daran versucht, weil es als kurze Ablenkung genau richtig kam ;)

C#-Code:
public static bool Contains<T>(this IEnumerable<T> source,
    IEnumerable<T> needle,
    IEqualityComparer<T> comparer)
{
    if (!needle.Any())
        return false;

    var firstNeedle = needle.First();
    var needleEnumerator = needle.Skip(1).GetEnumerator();

    bool itemsAreEqual = false;

    foreach (var item in source)
    {
        if (!itemsAreEqual)
            itemsAreEqual = comparer.Equals(item, firstNeedle);
        else
        {
            if (!needleEnumerator.MoveNext())
                return true;
            else
            {
                itemsAreEqual = comparer.Equals(needleEnumerator.Current, item);
                if (!itemsAreEqual)
                    needleEnumerator = needle.Skip(1).GetEnumerator();
            }
        }
    }

    return itemsAreEqual;
}

In einem schnellen Test hat es geklappt, keine Ahnung wie robust oder performant das insgesamt ist ;)

Grüße, HiGHteK
12.09.2013 15:44 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo pdelvo,

Zitat von pdelvo:
Du kannst nicht dsavon ausgehen das enumerable endlich ist.

dann solltest du IEnumerable<T>.Count() nicht benutzen :-) [EDIT]Ok, dein Edit im folgenden Beitrag zeigt, dass du verstanden hast, warum hinter dem Satz ein Smiley stand/steht. Für die Methode wären unendliche Sequenzen unpraktikabel. In der Mathematik gibt sowas allerdings häufiger, siehe  Semi-entscheidbar.[/EDIT]


Hallo HiGHteK,

das kann so nicht hinkommen, der folgende Code liefert mit deiner Implementierung fälschlich false:

C#-Code:
var a = new List<int> { 1, 1, 2, 3 };
var b = new List<int> {    1, 2, 3 };
Console.WriteLine (Contains (a, b, EqualityComparer<int>.Default));

herbivore
12.09.2013 16:09 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
pdelvo pdelvo ist männlich
myCSharp.de-Mitglied

avatar-3354.png


Dabei seit: 02.11.2008
Beiträge: 1.346
Entwicklungsumgebung: Visual Studio 2012 Prof.


pdelvo ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich benutze es bei der zu prüfenden Sequenz. Da kann ich mMn schon von endlichkeit Ausgehen. Ansonsten kann man auf programmatischen Weg nicht überprüfen ob die Sequenz in einer anderen enthalten ist. Bei der Äußeren Sequenz benutze ich Count nicht. Die kann (theoretisch) unendlich sein

Edit: Jetzt wo ich nochmal drüber nachdenke macht die Frage unendlicher Sequenzen eigentlich keinen Sinn. Wenn ich eine Auflistung aller Primzahlen habe, und dort nach {1,7,8} suche würde ich nie zu einem Ergebnis kommen. Das ganze funktioniert nur wirklich wenn die Sequenz auch enthalten ist

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von pdelvo am 12.09.2013 16:41.

12.09.2013 16:35 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Robin0
myCSharp.de-Mitglied

Dabei seit: 07.06.2012
Beiträge: 212


Robin0 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich würde Einfach die Distanz(aus [2,4,8] wird [2,4] der einzelnen Elemente zwischenspeichern, wichtig ist die Listen vorerst in zahlen zu convertieren die klar und eindeutig voneinander unterscheidbar sind, es eignen sich sehr 2er potenzen hierfür.
Ja, diese verkürzung deiner elemente kannst du sooft machen wie du möchtest wenn dus äquivalent in beiden tabellen tust. Und dann einfach vergleichen.


mycsharp.de  Moderationshinweis von herbivore (13.09.2013 06:58):

Bitte keine Vorschläge, wie man die Aufgabe lösen könnte, sondern nur fertige Lösungen, die man selbst für korrekt hält. Wir sind hier im Programmierspiel und nicht in  Entwicklung.
 
12.09.2013 16:54 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz


Programmierhans ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von pdelvo:
Leider enthällt deine Lösung einige Fehler:
  • Du kannst nicht davon ausgehen, dass die Klasse ToString korrekt implementiert, und vorallem kannst du nicht davon ausgehen, dass Die ToString zuordnung eindeutig ist

:-) Es ist eine ganz schmerzfreie Implementierung nur genau für dieses Szenario (mit den strikt aufeinander folgenden Strings)... das bin ich mir durchaus bewusst... deshalb auch der Hinweis dass es eine sehr plumpe Lösung ist.

Aber schön dass es jemand gemerkt hat :-)... meine Lösung war ja auch mehr als Scherz gedacht :-)

PS: In genau diesem Szenario würden die Ergebnisse passen.

Gruss
Programmierhans
12.09.2013 17:08 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
HiGHteK HiGHteK ist männlich
myCSharp.de-Mitglied

Dabei seit: 28.03.2007
Beiträge: 114


HiGHteK ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo herbivore,

ich hatte befürchtet, dass ich irgendwas übersehen hab. Aber ich hab die obige Implementierung noch um eine Zeile ergänzt, um den Fehler aus deinem Beispielfall zu eliminieren...
Allerdings kann ich mangels Zeit nicht weiter darüber sinnieren, aber ich hoffe die Implementierung ist nun im Großen und Ganzen soweit OK ;)

Hier nochmals die korrigierte Implementierung...

C#-Code:
public static bool Contains<T>(this IEnumerable<T> source,
    IEnumerable<T> needle,
    IEqualityComparer<T> comparer)
{
    if (!needle.Any())
        return false;

    var firstNeedle = needle.First();
    var needleEnumerator = needle.Skip(1).GetEnumerator();

    bool itemsAreEqual = false;

    foreach (var item in source)
    {
        if (!itemsAreEqual)
            itemsAreEqual = comparer.Equals(item, firstNeedle);
        else
        {
            if (!needleEnumerator.MoveNext())
                return true;
            else
            {
                itemsAreEqual = comparer.Equals(needleEnumerator.Current, item);
                if (!itemsAreEqual)
                {
                    itemsAreEqual = comparer.Equals(item, firstNeedle);
                    needleEnumerator = needle.Skip(1).GetEnumerator();
                }
            }
        }
    }

    return itemsAreEqual;
}

Grüße, HiGHteK
13.09.2013 09:17 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz


Programmierhans ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@HiGHteK

Die Lösung ist falsch.

source: a; b; c; d; e; f; g; needle: b; a; Erwartet: False Result: False
source: a; b; c; d; e; f; g; needle: a; b; Erwartet: True Result: True
source: a; b; c; d; e; f; g; needle: e; f; g; Erwartet: True Result: True
source: a; b; c; d; e; f; g; needle: f; g; h; Erwartet: False Result: True
source: a; b; c; d; e; f; g; needle: f; g; g; Erwartet: False Result: True
source: a; b; c; d; e; f; g; needle: a; c; e; Erwartet: False Result: False


Gruss
Programmierhans
13.09.2013 13:08 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo HiGHteK,

du hast da ein prinzipielles Problem, das man m.E. nicht mit einer Zeile korrigieren kann. Mein Beispiel war ja nur ein Spezialfall einer ganzen Klasse von ähnlichen Beispielen, die dein Algorithmus alle nicht berücksichtigt. Hier noch eins, das nicht geht (einfach die Kette der Einsen verlängert):

C#-Code:
var a = new List<int> {  1, 1, 1, 2, 3 };
var b = new List<int> {     1, 1, 2, 3 };

Dadurch, dass das Muster eine Weile lang passt, verpasst du den Punkt, an dem du für das Finden einer Übereinstimmung mit dem Vergleich beginnen müsstest.

herbivore
13.09.2013 14:44 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz


Programmierhans ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hier noch meine Implementierung:

C#-Code:
        public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> needle, IEqualityComparer<T> comparer)
        {
            if (!needle.Any())
            {
                //wenn b leer ist, dann sind alle aus needle (also keine) in source enthalten
                return true;
            }

            bool ret = false;

            IEnumerator<T> enumerA = source.GetEnumerator();
            var bFirst = needle.First();

            int iAOffset = 0;

            while (enumerA.MoveNext())
            {
               //zuerst nur gegen das erste element aus needle vergleichen
                if (comparer.Equals(enumerA.Current, bFirst))
                {
                    //das erste Item von needle passt schon mal
                    ret = true;

                    //einen inneren Enumerator ziehen und auf den äusseren synchronisieren
                    IEnumerator<T> innerA = source.Skip(iAOffset).GetEnumerator();
                    IEnumerator<T> innerB = needle.GetEnumerator();
                    while (ret)
                    {
                        if (innerB.MoveNext())
                        {
                            //verschiebe auch A
                            if (innerA.MoveNext())
                            {
                                ret = comparer.Equals(innerA.Current, innerB.Current);
                            }
                            else
                            {
                                //InnerA ist am Ende also kann B nicht mehr matchen
                                ret = false;
                                break;
                            }
                        }
                        else
                        {
                            if (ret)
                            {
                                //InnerB ist am Ende und ret ist true also kann die Suche abgebrochen werden
                                return ret;
                            }
                        }
                    }
                }
                iAOffset += 1;
            }

            return ret;


        }

source: a; b; c; d; e; f; g; needle: Erwartet: True Result: True
source: a; b; c; d; e; f; g; needle: b; a; Erwartet: False Result: False
source: a; b; c; d; e; f; g; needle: a; b; Erwartet: True Result: True
source: a; b; c; d; e; f; g; needle: e; f; g; Erwartet: True Result: True
source: a; b; c; d; e; f; g; needle: f; g; h; Erwartet: False Result: False
source: a; b; c; d; e; f; g; needle: f; g; g; Erwartet: False Result: False
source: a; b; c; d; e; f; g; needle: a; c; e; Erwartet: False Result: False
source: a; b; c; d; e; f; g; needle: a; b; b; Erwartet: False Result: False
source: a; b; c; d; e; f; g; needle: a; h; Erwartet: False Result: False


Passt oder habe ich was übersehen ?

Gruss
Programmierhans
13.09.2013 14:45 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
xxxprod xxxprod ist männlich
myCSharp.de-Poweruser/ Experte

avatar-2329.gif


Dabei seit: 13.04.2006
Beiträge: 1.376
Herkunft: Österreich\Wien


xxxprod ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Dann möcht ich meine unleserliche Version hier auch posten... :)

C#-Code:
        public static bool Contains<T>(this IEnumerable<T> enumerable, IEnumerable<T> items, IEqualityComparer<T> comparer)
        {
            T[] arr1 = enumerable.ToArray();
            T[] arr2 = items.ToArray();

            int i = 0, i2, j;
            do
            {
                for (j = 0; i < arr1.Length && arr2.Length > 0 && !comparer.Equals(arr1[i], arr2[0]); i++) ;
                for (i2 = i; i2 < arr1.Length && j < arr2.Length && comparer.Equals(arr1[i2], arr2[j]); i2++, j++) ;

            } while (i++ < arr1.Length && j != arr2.Length);

            return j == arr2.Length;
        }

//Edit: Und weils so lustig war noch eine ultrakurz version :D

C#-Code:
        public static bool Contains2<T>(this IEnumerable<T> x, IEnumerable<T> y, IEqualityComparer<T> c)
        {
            T[] a = x.ToArray(), b = y.ToArray();
            for (int i = 0, l, k, n = a.Length, m = b.Length; i < n; i++)
            {
                for (k = 0; i < n && m > 0 && !c.Equals(a[i], b[0]); i++) ;
                for (l = i; l < n && k < m && c.Equals(a[l], b[k]); l++, k++) ;
                if (k == m) return true;
            }
            return false;
        }

Lg, XXX

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von xxxprod am 13.09.2013 16:43.

13.09.2013 16:19 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
zero_x zero_x ist männlich
myCSharp.de-Mitglied

avatar-2567.gif


Dabei seit: 16.02.2008
Beiträge: 1.044
Herkunft: Koblenz


zero_x ist offline Füge zero_x Deiner Kontaktliste hinzu

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

auch wenn schon mehrere ihre Lösung gepostet haben, hier meine noch kürzere (und ekelhafte) als xxxprods:

C#-Code:
var idx = 0;
return enumerable.Select( (source, iteration) =>
    item.All( condition =>
    idx < enumerable.Count() && comparer.Equals(condition, enumerable.ToList()[idx++]))).Any(b => b);

zero_x
15.09.2013 12:48 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Scavanger Scavanger ist männlich
myCSharp.de-Mitglied

avatar-3209.jpg


Dabei seit: 13.03.2008
Beiträge: 309
Entwicklungsumgebung: MS VS C# 2010/2013 Prof.


Scavanger ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Sory das ich mich erst jetzt melde:

pdelvo lösung ist korrekt und war der erste. Du bist dran.

Meine Lösung sieht fast genau so aus. großes Grinsen
17.09.2013 15:13 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
pdelvo pdelvo ist männlich
myCSharp.de-Mitglied

avatar-3354.png


Dabei seit: 02.11.2008
Beiträge: 1.346
Entwicklungsumgebung: Visual Studio 2012 Prof.


pdelvo ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Wieviele verschiedene Möglichkeiten gibt es 2,50€ zusammenzusetzen.

Dabei sind natürlich nur Geldstücke zu benutzen, die auch existieren, alle dürfen aber beliebig oft verwendet werden.
Die Reihenfolge, wie man die Geldstücke zusammenlegt nicht zu beachten. So zählt {2€, 50c} und {50c, 2€} nur einmal.

Als kleine Überprüfungsmöglichkeit kann ich folgende Hilfe geben: Die Quersumme des Ergebnisses ist 18 :)

LG pdelvo
17.09.2013 16:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Alf Ator
myCSharp.de-Mitglied

avatar-586.gif


Dabei seit: 30.10.2007
Beiträge: 614
Entwicklungsumgebung: VS2005 / VS2008


Alf Ator ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

geschockt Ich habe gestern noch ein kleines Progrämmchen geschrieben, dass mir das ausrechnet und das läuft immer noch. Ist wohl ein bisschen langsam. Schöne Aufgabe.
18.09.2013 08:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz


Programmierhans ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Eigentlich nur eine Spezielle Art von:  Rucksackproblem
18.09.2013 11:51 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
herbivore
myCSharp.de-Poweruser/ Experte

avatar-2627.gif


Dabei seit: 11.01.2005
Beiträge: 49.480
Entwicklungsumgebung: csc/nmake (nothing is faster)
Herkunft: Berlin


herbivore ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

mich wundert, dass noch kein anderer eine Lösung gepostet hat. Anscheinend war die Aufgabe zu einfach. :-)

Die Lösung ist 63234 [EDIT]200187[/EDIT]. Berechnet mit:

C#-Code:
static class App
{
// EDIT: Leider mache auch ich manchmal Flüchtigkeitsfehler:
// private static int [] coins = new int [] { 1, 2, 5, 10, 50, 100, 200 }; // aufsteigend
   private static int [] coins = new int [] { 1, 2, 5, 10, 20, 50, 100, 200 }; // aufsteigend

   public static void Main (string [] astrArg)
   {
      int count = 0;
      CountCombinations (250, coins [coins.Length - 1], ref count);
      System.Console.WriteLine (count);
   }

   private static void CountCombinations (int remaining, int max, ref int count)
   {
      foreach (var coin in coins) {
         if (coin > remaining || coin > max) {
            return;
         }
         if (coin == remaining) {
            ++count;
            return;
         }
         CountCombinations (remaining - coin, coin, ref count);
      }
   }
}

Das Programm ist nach ca. einer [EDIT]anderthalb[/EDIT] Zehntelsekunden fertig. :-)

[EDIT]Ich hatte zuerst vergessen die 20Cent Stücke in die Liste zu packen.[/EDIT]

herbivore
18.09.2013 16:38 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Seiten (19): « erste ... « vorherige 13 14 15 16 [17] 18 19 nächste » Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 10 Jahre.
Der letzte Beitrag ist älter als 3 Jahre.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 30.11.2020 11:52