Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Floste,

leider ist schon die Abwesenheit der MaxSize-Property ein KO-Kriterium. :-(

Ansonsten ist es ein sehr interessanter Ansatz, eine lockfreie Queue und zwei Semaphoren zu einer blockierenden Queue zu verbinden. :-)

herbivore
private Nachricht | Beiträge des Benutzers
Floste
myCSharp.de - Member

Avatar #avatar-2376.jpg


Dabei seit:
Beiträge: 1158
Herkunft: Norddeutschland

beantworten | zitieren | melden

    class SyncQueue<T>
    {
        public SyncQueue(int capacity)
        {
            this.capacity=capacity;
            first = new Node();
            last = first;
            deQueueableS = new Semaphore(0, int.MaxValue);
            enQueueableS = new Semaphore(capacity, int.MaxValue);
        }

        int capacity;
        public int MaxSize
        {
            get
            {
                return capacity;
            }
            set
            {
                ModifyCapacity(value-this.capacity);
                this.capacity=value;
            }
        }

        Semaphore deQueueableS;
        Semaphore enQueueableS;
        Node first;
        Node last;

        public void Enqueue(T item)
        {
            enQueueableS.WaitOne();
lock(this)
{
            Node newNode = new Node();
            newNode.item = item;
            Node old = first;
            first= newNode;
            old.next = newNode;
            deQueueableS.Release();
}
        }

        public T Dequeue()
        {
            deQueueableS.WaitOne();
            Node current;
            do
            {
                current = last;
            }
            while (current != Interlocked.CompareExchange(ref last, current.next, current));
            enQueueableS.Release();
            return current.next.item;
        }

        class Node
        {
            public T item;
            public Node next;
        }

void ModifyCapacity(int change)
{
    if(change>0) enQueueableS.Release(change);
    else
    {
        for(int i=-change;i != 0; i--) enQueueableS.WaitOne();
    }
}
    }
Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Floste,

hehe, jetzt aber bitte nicht Schlag auf Schlag posten, bis es stimmt. Momentan ist zumindest noch nicht der Fall berücksichtigt, dass mehrere Threads die MaxSize-Property gleichzeitig ändern. Nach möglichen weiteren Problemen hab ich noch nicht geschaut.

herbivore
private Nachricht | Beiträge des Benutzers
Floste
myCSharp.de - Member

Avatar #avatar-2376.jpg


Dabei seit:
Beiträge: 1158
Herkunft: Norddeutschland

beantworten | zitieren | melden

So: MaxSize Threadsicher gemacht und das Warten, bis die entfernten Plätze wieder frei sind ins Enqueue verlegt.


    public class SyncQueue<T>
    {
        public SyncQueue(int capacity)
        {
            this.capacity = capacity;
            first = new Node();
            last = first;
            deQueueableS = new Semaphore(0, int.MaxValue);
            enQueueableS = new Semaphore(capacity, int.MaxValue);
        }

        int capacity;
        public int MaxSize
        {
            get
            {
                return capacity;
            }
            set
            {
                int change = value - Interlocked.Exchange(ref this.capacity,value);
                if (change > 0) enQueueableS.Release(change);
                else Interlocked.Add(ref oversize, -change);
            }
        }

        int oversize = 0;

        Semaphore deQueueableS;
        Semaphore enQueueableS;
        Node first;
        Node last;

        public void Enqueue(T item)
        {
            enQueueableS.WaitOne();
            while (true)
            {
                int cachedOversize = oversize;
                if (cachedOversize ≤ 0) break;
                if (Interlocked.CompareExchange(ref oversize, cachedOversize - 1, cachedOversize) == cachedOversize)
                    enQueueableS.WaitOne();//Platz wurde dauerhaft entfernt. Da aber immernoch das Item eingefügt werden soll, muss eine neuer reserviert werden.
            }
            lock (this)//hinzufügen
            {
                Node newNode = new Node();
                newNode.item = item;
                Node old = first;
                first = newNode;
                old.next = newNode;
                deQueueableS.Release();
            }
        }

        public T Dequeue()
        {
            deQueueableS.WaitOne();
            Node current;
            do current = last;
            while (current != Interlocked.CompareExchange(ref last, current.next, current));
            enQueueableS.Release();
            return current.next.item;
        }

        class Node
        {
            public T item;
            public Node next;
        }
    }
Zitat
hehe, jetzt aber bitte nicht Schlag auf Schlag posten, bis es stimmt.
Das mit der änderbaren maxsize hatte ich übersehen. Dann hatte ich erstmal was tzusammengehackt, um es änderbar zu machen. Jetzt habe ich es threadsicher versucht zu machen. Aber warum soll ich den code nicht verbessern, bis er stimmt?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Floste am .
Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Floste,
Zitat
Aber warum soll ich den code nicht verbessern, bis er stimmt?

das sollst du ja. Es ging nur darum, dass du bitte nicht jeden Zwischenstand postetet. Aus meiner Sicht hätten die Schritte ...
Zitat
Dann hatte ich erstmal was tzusammengehackt, um es änderbar zu machen. Jetzt habe ich es threadsicher versucht zu machen.

... zusammengehört, weil ja von vornherein klar war, dass es nicht darum ging, was "zusammenzuhacken", sondern was wirklich threadsicheres, korrektes und robustes abzuliefern. Aber sei es drum. :-)


Das threadsichere hast du nachgereicht (zumindest habe ich auf die Schnelle nichts offensichtliches schiefes gefunden). Die Korrektheit und Robustheit fehlt in meinen Augen noch an zwei Stellen.

Korrektheit: So weit ich das sehe, ist folgender Fall noch nicht berücksichtigt:
Zitat
MaxSize == 0 soll bedeuten, dass es keine Beschränkung gibt.

Robustheit: Darunter fällt für mich, dass Eingabewerte darauf geprüft werden, ob sie in einem sinnvollen Bereich liegen, bzw. dass mit Werten außerhalb des sinnvollen Bereichs sinnvoll umgegangen wird.


Ich finde es interessant, dass der zunächst so elegante Ansatz durch die zusätzlichen Anforderungen ziemlich "ausgebeult" wird. Ich hatte, als ich die Aufgabe gestellt habe, eine Erweiterung im Sinn, die näher am Original liegt. Und bei der lassen sich alle genannten Anforderung viel leichter "unterbringen".


Hallo zusammen,

vielleicht versucht sich ja noch jemand an einer Lösung, die in die Richtung geht, die Corpsegrinder eingeschlagen hat.

herbivore
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo zusammen,
Zitat von herbivore
vielleicht versucht sich ja noch jemand an einer Lösung, die in die Richtung geht, die Corpsegrinder eingeschlagen hat.
Zitat
Ich möchte noch nicht verraten, wo die Probleme liegen, weil der Reiz dieser Aufgabe in meinen Augen in gerade im Knobeln und im Kniffligen liegt.

nachdem ihr nun eine Menge Zeit zum Knobeln "im eigenen Saft" hattet, will ich jetzt die zweite Stufe zünden und die Fallstricke, die ich (bei dem Lösungsansatz, den Corpsegrinder gewählt hat und der auch mir vorschwebt) sehe, doch verraten. Die Reihenfolge der Fallstricke ist willkürlich. Sie dient nur dazu, die einzelnen Punkte eindeutig identifizieren zu können:


Fallstrick 1:

Wertebereich von MaxSize: Zu einem robusten Programm gehört- wie schon im vorigen Beitrag gesagt, "dass Eingabewerte darauf geprüft werden, ob sie in einem sinnvollen Bereich liegen, bzw. dass mit Werten außerhalb des sinnvollen Bereichs sinnvoll umgegangen wird."


Fallstrick 2:

Zusätzlich zur allgemeinen Berücksichtigung des Wertebereichs, muss man den Speziallfall, dass MaxSize == 0 keine Beschränkung bedeutet, überall berücksichtigen.


Fallstrick 3: (den ich ja schon weiter oben genannt habe)

Wenn MaxSize verringert wird, kann es sein, dass die Queue "übervoll" ist. Das muss man bei der Formulierung der Abfrage, ob die Queue voll ist, berücksichtigen.


Fallstrick 4:

Man muss den Fall berücksichtige, dass möglicherweise wartende Enqueues weiterarbeiten können bzw. müssen, wenn MaxSize "vergrößert" wird (hier auch an Fallstrick 2 denken). Unabhängig davon, ob die Queue nur voll oder übervoll ist.


Fallstrick 5:

Man muss verhindern, dass durch Caching MaxSize in verschiedene Threads verschiedenen Werte hat.


Fallstrick 6:

Nicht nur die Zugriffe Enqueue und Dequeue müssen threadsafe sein, sondern auch die auf MaxSize (und dabei reicht es nicht unbedingt, dass MaxSize sich als int atomar ändern lässt).


Fallstricke 7 und 8: (besonders tricky)

Bei einem Enqueue muss dafür gesorgt werden, dass ein evtl. schon wartendes Dequeue weiterlauft. Und umgekehrt. Man könnte denke, dass das Pulse im Enqueue und im Dequeue genau das tut. Aber schaut euch mal diesen Fall an:
  1. Stand: Zwei Consumer, zwei Producer, Queue leer, keiner wartet auf die Sperre, keiner im Wait-Status, MaxSize == 1
  2. Consumer1 Dequeue: betritt die Sperre (bei lock) und läuft - da die Queue leer ist - aufs Wait (wodurch er in den Wait-Status geht und die Sperre wieder frei ist)
  3. Consumer2 Dequeue: betritt die Sperre (bei lock) und läuft - da die Queue leer ist - aufs Wait (wodurch er in den Wait-Status geht und die Sperre wieder frei ist)
  4. ProducerA Enqueue: betritt die Sperre (bei lock), aber dann erfolgt zufällig ein Threadwechsel, bevor das Pulse erreicht wurde
  5. ProducerB Enqueue: versucht die Sperre zu betreten, aber da ist ja ProducerA drin, also wartet ProducerB auf die Sperre (bei lock)
  6. ProducerA läuft weiter und führt das Pulse aus
  7. Dadurch wartet Consumer1 wieder auf die Sperre (bei Wait), stellt sich also hinter dem schon wartenden ProducerB an.
  8. ProducerA verlässt die Sperre
  9. Stand: Ein Item in der Queue, ProducerB und Consumer1 warten in dieser Reihenfolge auf die Sperre, Consumer2 wartet im Wait-Status
  10. ProducerB betritt die Sperre (bei lock), und läuft - da die Queue voll ist - aufs Wait (wodurch er in den Wait-Status geht und die Sperre wieder frei ist)
  11. Stand: Ein Item in der Queue, Consumer1 wartet auf die Sperre, Consumer2 und ProducerB warten in dieser Reihenfolge im Wait-Status
  12. Consumer1 betritt die Sperre (bei Wait), holt das von ProducerA eingestellt Element ab und und führt das Pulse aus
  13. Dadurch wartet Consumer2 wieder auf die Sperre (bei Wait). Vor ihm steht keiner.
  14. Consumer1 verlässt die Sperre
  15. Stand: Queue leer, Consumer2 wartet auf die Sperre, ProducerB wartet im Wait-Status
  16. Consumer2 betritt die Sperre (bei Wait) und läuft - da die Queue leer ist - aufs Wait (wodurch er in den Wait-Status geht und die Sperre wieder frei ist)
  17. Stand: Queue leer, keiner wartet auf die Sperre, ProducerB und Consumer2 warten in dieser Reihenfolge (für immer) im Wait-Staus, obwohl ProducerB und danach auch Consumer2 arbeiten könnte

  18. Das Problem ist, dass in Schritt 12 durch das Pulse von Consumer1 fälschlich Consumer2 aufgeweckt wird und nicht wie beabsichtigt ProducerB. Und da der aufgeweckte Consumer2 auf eine leere Queue trifft, schläft er gleich wieder ein, wodurch das Pulse effektiv verloren ist.

    Solche Fälle sind es, die die Aufgabe in meinen Augen so spannend machen. Solche Fälle muss man im Blick haben, wenn man sich überlegt, ob die eigene Lösung korrekt ist.


    Fallstrick 9 (auch schon bekannt):

    Statt einer if-Abfrage um die Waits, muss eine while-Schleife verwendet werden. Siehe den Fall in SyncQueue <T> - Eine praktische Job-Queue.


    Damit habe ich die Fallstricke genannt, die man beachten muss. Ich habe absichtlich nicht gesagt, was man tun kann oder tun muss, um die Fallstricke zu umgehen.


    Wem gelingt es mit dem Gesagten, eine fehlerfreie und robuste Implementierung hinzubekommen?

    herbivore
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 416

beantworten | zitieren | melden

So, hier nun die von herbivore abgesegnete Lösung ;-)



using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;

namespace SyncQueueTest
{

	//*****************************************************************************
	public class SyncQueue <T>
	{
		   //--------------------------------------------------------------------------
		private Queue <T> _q   = new Queue <T> ();
		private volatile int _maxSize;
		public int MaxSize { 
			get { 
				return _maxSize; 
			} 
			set {
				lock(this) {
					if(value < 0) value = 0;
					int oldSize = _maxSize;
					_maxSize = value;
					if(_maxSize > oldSize || _maxSize == 0)
						Monitor.PulseAll(this);
				}
			} 
		}
		
		public SyncQueue (int maxSize)
		{
			MaxSize = maxSize;
		}
	
		//==========================================================================
		// <summary>
		// Trägt einen Eintrag und wartet dabei nötigenfalls
	  // solange bis wieder Platz vorhanden ist.
		// </summary>
		public void Enqueue (T tItem)
		{
			lock (this) {
				while (_q.Count ≥ MaxSize && MaxSize != 0) {
					Debug.WriteLine (Thread.CurrentThread.Name + " Wait");
					Monitor.Wait (this);
				}
				_q.Enqueue (tItem);
				Monitor.PulseAll(this);
				Debug.WriteLine (Thread.CurrentThread.Name + " Enqueue ==> " + _q.Count);
			}
		}
	
	   //==========================================================================
	   // <summary>
	   //    Holt einen Eintrag aus der Queue heraus und wartet dabei nötigenfalls
	   //    solange bis wieder ein Eintrag vorhanden ist.
	   // </summary>
	   public T Dequeue ()
	   {
	      lock (this) {
	         while (_q.Count == 0) {
	            Debug.WriteLine (Thread.CurrentThread.Name + " Wait");
	            Monitor.Wait (this);
	         }
			 Monitor.PulseAll(this);
	         Debug.WriteLine (Thread.CurrentThread.Name + " Dequeue ==> " + (_q.Count - 1));
	         return _q.Dequeue ();
	      }
	   }
	}
}

private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Corpsegrinder,
Zitat
So, hier nun die von herbivore abgesegnete Lösung ;-)
richtig, ich hatte dir schon per PM bestätigt, dass ich die Lösung als korrekt ansehe.

Ich hoffe du bist mir nicht böse, wenn ich noch zwei winzige Schönheitfehler nenne, die mir aufgefallen sind. :-) Zum einen die unnötige Zwischenvariable oldSize und zum anderen das unnötige PulseAll, wenn MaxSize von 0 auf 0 gesetzt wird. Aber wie ich selbst vorgegeben habe:
Zitat von herbivore
Eine Aufgabe gilt als gelöst, wenn der Code das vorgegebene Problem löst, egal wie wie gut oder schlecht der Programmierstil ist.

Und der Programmierstil ist insgesamt gar nicht übel. Egal wie gut man etwas macht, es geht fast immer noch ein bisschen besser. :-)

Du bist mit der nächsten Aufgabe dran!

herbivore
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 416

beantworten | zitieren | melden

Zitat von herbivore
Und der Programmierstil ist insgesamt gar nicht übel. Egal wie gut man etwas macht, es geht fast immer noch ein bisschen besser. :-)

Danke ;-), muss auch sagen, dass ich aus C# voll raus bin, da ich momentan fast nur noch mit Scala oder Objective-C arbeite. Und Concurrency unter C# habe ich auch noch nicht gemacht. Naja.. nun mal zu meiner nächsten Aufgabe:


Es soll eine möglichst performante Matrix-Klasse erstellt werden. Es sollen die normalen Matrix-Operationen (+, -, * und ^) implementiert werden. Wichtig ist, dass die Matrix absolut Threadsafe und alle Methoden pure sind (d.h. egal wie oft man sie aufruft, es kommt immer das gleiche Ergebnis heraus). Achso... eine schicke ToString() ist natürlich auch gerne gesehen ;-)
private Nachricht | Beiträge des Benutzers
dr4g0n76
myCSharp.de - Experte

Avatar #avatar-1768.jpg


Dabei seit:
Beiträge: 3047
Herkunft: Deutschland

beantworten | zitieren | melden

Dazu habe ich noch eine paar Fragen @Corpsegrinder

Ich stelle die Fragen mal hier öffentlich, weil ich denke dass mehrere Leute hier die gleichen Fragen haben könnten.
Zitat
Es soll eine möglichst performante Matrix-Klasse erstellt werden.

Heißt möglichst performant elegant mathematisch mit möglichst wenigen Rechenoperationen oder heißt das zusätzlich noch parallele Berechnungen per Threads wenn mehrere benutzt werden?
Zitat
Es sollen die normalen Matrix-Operationen (+, -, * und ^) implementiert werden.

Heißt hier ^ potenzieren oder meinst Du damit etwas anderes?
Zitat
Wichtig ist, dass die Matrix absolut Threadsafe und alle Methoden pure sind (d.h. egal wie oft man sie aufruft, es kommt immer das gleiche Ergebnis heraus).

Heißt das wirklich "pure"? Ich kenne das als idempotent (kann mich auch irren). ;-)
Zitat
Achso... eine schicke ToString() ist natürlich auch gerne gesehen ;-)

Wie soll ausgegeben werden? Ich meine in welchem Format.

Stellst Du Dir da so was vor:

3 5 2
4 3 1
3 4 2

oder etwas anderes?

Oder meinst Du gar eine Matrix aus der Prädikatenlogik? Das wäre dann etwas ganz anderes IMHO als die mathematische Variante.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 416

beantworten | zitieren | melden

zu 1: Wie du du die Performance erlangst ist deine Sache. Wenn du eine vernünftige Lösung mit Threads erstellt ist das natürlich super.

zu 2: Ja, das soll die Potenz sein

zu 3: pure bedeutet, dass die Methode keine Seiteneffekte hat, somit das eigentliche Objekt nicht verändert. Außerdem darf die Methode nicht auf irgendwelchen Werten basieren, die sich zur Laufzeit de Programms verändern können. Nur so kann sichergestellt werden, dass z.B. bei


int[] args = {1,2,3,4};
Matrix m = new Matrix(2, args);

m*5
m*5
m*5
m*5

immer das gleiche Ergebnis herauskommt. Siehe auch http://en.wikipedia.org/wiki/Pure_function

zu 4: ja, so zum Beispiel

Achso... der Einfachheit halber: Die Matrix ist quadratisch und fehlende Werte sind mit 0 aufzufüllen. z.B. sähe dann eine Matrix der größe 2, die nur den Wert 1 bekommt so aus:

1 0
0 0

Wie in meinem Beispiel schon zu sehen ist der erste Parameter die Größe der Matrix, der zweite ein Array mit den Werten.
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Corpsegrinder am .
private Nachricht | Beiträge des Benutzers
Floste
myCSharp.de - Member

Avatar #avatar-2376.jpg


Dabei seit:
Beiträge: 1158
Herkunft: Norddeutschland

beantworten | zitieren | melden

Zitat
Wie in meinem Beispiel schon zu sehen ist der erste Parameter die Größe der Matrix, der zweite ein Array mit den Werten.
warum keine mehrdimensionalen arrays?

Der code ist ziemlich lang, unter anderem, weil man für negateive exponenten eine invertierte matrix braucht.
Threadsafe ist es, da immuteable. Die Potenzierung ist etwas optimiert, aber weiter opßtimierungen würden den code noch deutlich verlängern.


    public class MaTrix
    {
        public MaTrix(float[,] array)
        {
            this.array=(float[,])array.Clone();
            width=array.GetUpperBound(0)+1;
            height=array.GetUpperBound(1)+1;
        }

        public MaTrix Clone()
        {
            return new MaTrix((float[,])array.Clone());
        }

        int width;
        public int Width
        {
            get { return width; }
        }

        int height;
        public int Height
        {
            get { return height; }
        }

        private float[,] array;
        public float this[int x, int y]
        {
            get
            {
                return array[x, y];
            }
        }

        public static MaTrix CreateIdentity(int num)
        {
            MaTrix C=new MaTrix(new float[num,num]);
            for(int i=0;i<num;i++)
                C.array[i,i]=1;
            return C;
        }

        public static MaTrix operator +(MaTrix A, MaTrix B)
        {
            if (A.width != B.width || A.height != B.height)
                throw new Exception("Dimensions not Equal");
            MaTrix C = new MaTrix(new float[A.width, B.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    C.array[x, y] = A.array[x, y] + B.array[x, y];
                }
            return C;
        }

        public static MaTrix operator -(MaTrix A, MaTrix B)
        {
            if (A.width != B.width || A.height != B.height)
                throw new Exception("Dimensions not Equal");
            MaTrix C = new MaTrix(new float[A.width, B.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    C.array[x, y] = A.array[x, y] - B.array[x, y];
                }
            return C;
        }


        public static MaTrix operator *(MaTrix A, MaTrix B)
        {
            if (A.width != B.height)
                throw new Exception("Dimensions");
            MaTrix C = new MaTrix(new float[B.width, A.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    float temp=0;
                    for (int i = 0; i < C.height; i++)
                    {
                        temp += A.array[i, y] * B.array[x, i];
                    }
                    C.array[x, y]=temp;
                }
            return C;
        }


        public static MaTrix operator *(float scalar, MaTrix B)
        {
            MaTrix C = new MaTrix(new float[B.width, B.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    C.array[x, y] = scalar * B.array[x, y];
                }
            return C;
        }

        public static MaTrix operator ^(MaTrix B, int exp)
        {
            if(B.width!=B.height)
                throw new Exception("Matrix has to be quadratic");
            if(exp==0)return CreateIdentity(B.width);
            if(exp<0)
            {
                B=MaTrix.Invert(B);
                exp=-exp;
            }
            int pCount=0;
            for(int p=exp;p!=0;p/=2)
                pCount++;
            MaTrix[] pows=new MaTrix[pCount];
            pows[0]=B;//pCount ist nur 0, wenn exp das auch ist, was schon abgefragt wurde
            for(int i=1;i<pCount;i++)
            {
                pows[i]=pows[i-1]*pows[i-1];
            }
            MaTrix C=null;
            bool first = true;
            for(int p=exp,i=0;p!=0;p/=2,i++)
            {
                if ((p & 1) != 0)
                {
                    if (first)
                    {
                        C = pows[i];
                        first = false;
                    }
                    else
                        C *= pows[i];
                }
            }
            return C;
        }

        public static MaTrix Invert(MaTrix A)
        {
            if(A.width!=A.height)
                throw new Exception("Matrix has to be quadratic");
            return new MaTrix(MatrixInversion(A.array, A.width));
        }

        public float[,] ToArray()
        {
            return (float[,])array.Clone();
        }

        static float[,] MatrixInversion(float[,] A, int size)
        {
            double det = 1.0 / CalcDeterminant(A, size);
            float[,] minor = new float[size - 1, size - 1];
            float[,] C=new float[size,size];
            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    GetMinor(A, minor, j, i, size);
                    C[i, j] = (float)(det * CalcDeterminant(minor, size - 1));
                    if ((i + j) % 2 == 1)
                        C[i, j] = -C[i, j];
                }
            }
            return C;
        }

        static int GetMinor(float[,] src, float[,] dest, int row, int col, int size)
        {
            int colCount = 0, rowCount = 0;
            for (int i = 0; i < size; i++)
            {
                if (i != row)
                {
                    colCount = 0;
                    for (int j = 0; j < size; j++)
                    {
                        if (j != col)
                        {
                            dest[rowCount,colCount] = src[i,j];
                            colCount++;
                        }
                    }
                    rowCount++;
                }
            }
            return 1;
        }

        static double CalcDeterminant(float[,] mat, int size)
        {
            if (size == 1)
                return mat[0, 0];
            double det = 0;
            float[,] minor = new float[size - 1, size - 1];
            for (int i = 0; i < size; i++)
            {
                GetMinor(mat, minor, 0, i, size);
                det += /*pow( -1.0, i )*/((i & 1) == 0 ? 1 : -1) * mat[0, i] * CalcDeterminant(minor, size - 1);
            }
            return det;
        }


        public override string ToString()
        {
            List<string> list = new List<string>(2*width+height);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    list.Add(array[x, y].ToString("#0.0000").PadRight(10));
                    list.Add("  ");
                }
                list.Add(Environment.NewLine);
            }
            return string.Concat(list.ToArray());
        }
    }
Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 416

beantworten | zitieren | melden

Hi floste,

es sind zwar noch ein paar kleine Schönheitsfehler drin (2.0F * matrix möglich matrix * 2.0F nicht) und wenn eine Matrix keine Inverse besitzt liefert sie eine Matrix mit NaN gefüllt zurück, aber die Inverse war eh nicht gefordert und im groben läuft es ja. Also sehe ich die Aufgabe als erfüllt an.

Dann hau ma die nächste raus.
private Nachricht | Beiträge des Benutzers
Floste
myCSharp.de - Member

Avatar #avatar-2376.jpg


Dabei seit:
Beiträge: 1158
Herkunft: Norddeutschland

beantworten | zitieren | melden

Mal was kurtzes:

Die tcp/ip checksumme über ein array berechnen. Kümmert euch dabei weder um pseudoherder, headerstruktur oder sonstwas tcp-spezifisches, noch um little oder big-endian ,denn es kommt unabhängig vom der endianness sowieso die gleiche bytefolge raus.

Die funktion soll also folgende Signatur haben:
Zitat
public ushort Checksum(byte[] data)//data kann auch eine ungrade länge haben!
{
...

Beschreibung des Algorithmus:
http://wwwse.inf.tu-dresden.de/data/courses/SE1/exercises/se_ws0405_exercise8_tcp_checksum.pdf
Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!
private Nachricht | Beiträge des Benutzers
markus111
myCSharp.de - Member

Avatar #avatar-3108.png


Dabei seit:
Beiträge: 520
Herkunft: Henstedt-Ulzburg

beantworten | zitieren | melden


public ushort Checksum(byte[] data)//data kann auch eine ungrade länge haben!
{
    int checksum=0;
    for (int i=0; i < data.Length; i++)
    {
        if((i & 1) != 0)
            checksum += data[i] * 256;
        else 
            checksum += data[i];
    }

    while ((checksum & (~(int)0xFFFF)) != 0)
        checksum =(checksum & 0xFFFF) + (checksum >> 16);

    return (ushort)((~checksum)&0xFFFF);
}

müsste klappen...

mfg.
markus111
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von markus111 am .
private Nachricht | Beiträge des Benutzers
Floste
myCSharp.de - Member

Avatar #avatar-2376.jpg


Dabei seit:
Beiträge: 1158
Herkunft: Norddeutschland

beantworten | zitieren | melden

Ein kleiner Schreibfehler ist drin:checksum und nicht sum.

Aber also irgendwie habe ich da so eine Ahnung, dass der Code nicht wirklich von dir stammt *hust*.

Wie dem auch sei: Der Code ist korrekt (vom Schreibfehler abgesehen) und erfüllt die Aufgabe und es wäre witzlos, wenn eine korrekt6e und vollständige Lösung hier steht und ein anderer eine weitere schreiben müsste: stell die nächste aufgabe....
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von Floste am .
Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!
private Nachricht | Beiträge des Benutzers
markus111
myCSharp.de - Member

Avatar #avatar-3108.png


Dabei seit:
Beiträge: 520
Herkunft: Henstedt-Ulzburg

beantworten | zitieren | melden

Ich weiß dass du mir den Code dafür irgendwann mal gegeben hast, aber ich hatte keine Lust ihn rauszusuchen. Der Schreibfehler kommt daher, dass ich den Code nicth getestet hab

Gut, nächste Aufgabe (bestimmt zu einfach):
Auf der Konsole soll eine Sinus-Welle ausgegeben werden:
                                                      ############
                                                    ###          ###
                                                   ##              ##
                                                 ##                  ##
                                                ##                    ##
                                               ##                      ##
                                             ##                          ##
                                            ##                            ##
                                           ##                              ##
                                          ##                                ##
                                         ##                                  ##
                                        ##                                    ##
#                                      ##                                      #
##                                    ##
 ##                                  ##
  ##                                ##
   ##                              ##
    ##                            ##
     ##                          ##
       ##                      ##
        ##                    ##
         ##                  ##
           ##              ##
            ###          ###
              ###########

So in etwa sollte das Ergebniss aussehen.

mfg.
markus111
private Nachricht | Beiträge des Benutzers
m0rius
myCSharp.de - Member

Avatar #avatar-3125.png


Dabei seit:
Beiträge: 1043

beantworten | zitieren | melden

Hallo markus111,

hier meine Lösung. Dauert zwar ein paar Sekunden, aber Effizienz war ja auch nicht Teil der Anforderung.


using System;

namespace SinusConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            int xWidth = 63;
            int yHeight = 10;

            for (double i = 0; i < xWidth; i += 0.0005)
            {
                double y = (Math.Sin(i / xWidth * 2 * Math.PI) + 1) * yHeight;
                
                Console.SetCursorPosition(
                    (int)Math.Round(i),
                    (int)Math.Round(y)
                );
                Console.Write('#');
            }

            Console.Read();            
        }
    }
}
m0rius
Attachments
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
private Nachricht | Beiträge des Benutzers
markus111
myCSharp.de - Member

Avatar #avatar-3108.png


Dabei seit:
Beiträge: 520
Herkunft: Henstedt-Ulzburg

beantworten | zitieren | melden

Jup, richtig. Ich hattes es so, dass es über die komplette Breite geht, aber das ist schon okay. Du bist dran

EDIT: Zu deiner Effizenz: den i Wert nur um das wirklich benötigte erhöhen, also 1 / xWidth.

mfg.
markus111
private Nachricht | Beiträge des Benutzers
m0rius
myCSharp.de - Member

Avatar #avatar-3125.png


Dabei seit:
Beiträge: 1043

beantworten | zitieren | melden

Hallo markus111,

kannst das ja gerne mal ausprobieren ... Es kommt nicht die gleich Ausgabe bei raus ;).

m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
private Nachricht | Beiträge des Benutzers
m0rius
myCSharp.de - Member

Avatar #avatar-3125.png


Dabei seit:
Beiträge: 1043

beantworten | zitieren | melden

Hallo zusammen,

nächste Aufgabe:
Es soll eine Methode geschrieben werden, die alle glücklichen Zahlen bis zu einer festlegbaren Obergrenze [tt]upperLimit[/tt] zurückgibt. Es soll dabei die folgende Signatur verwendet werden:
[CSHARP]public int[] GetLuckyNumbers(int upperLimit)[/CSHARP]Alles Weitere findet ihr im entsprechenden [URL]Wikipedia-Artikel[/URL].

Ich wünsche euch viel Spaß!

m0rius
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von m0rius am .
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
private Nachricht | Beiträge des Benutzers
Kaji
myCSharp.de - Member



Dabei seit:
Beiträge: 602
Herkunft: Clausthal-Zellerfeld

beantworten | zitieren | melden

Hallo m0rius,

ich hab da mal was gebastelt was wirklich ziemlich hässlich ist aber es funktioniert ^^; Ich glaube ich habe da ziemlich quer gedacht.. das ganze kann sicherlich deutlich einfacher gemacht werden aber ich habe heute nen Wurm im Kopf. Naja meine Lösung:


        static void Main(string[] args)
        {
            int upperLimit = Convert.ToInt32(Console.ReadLine());
            int[] LuckyNumbers = GetLuckyNumbers(upperLimit);
            for (int i = 1; i ≤ LuckyNumbers.Length; i++)
            {
                Console.WriteLine(LuckyNumbers[i-1].ToString());
            }
            Console.ReadLine();
        }

        public static int[] GetLuckyNumbers(int upperLimit)
        {
            int[] LuckyNumbers = new int[upperLimit];
            int erase = 2;
            int eraseCounter = 1;
            for (int i = 1; i ≤ upperLimit; i++)
            {
                LuckyNumbers[i - 1] = i;
            }
            while (erase < LuckyNumbers.Length)
            {
                int[] newLuckyNumbers = new int[LuckyNumbers.Length];
                int digit = 1;
                for (int i = 0; i < LuckyNumbers.Length; i++)
                {
                    if (digit == erase)
                    {
                        digit = 0;
                    }
                    else
                    {
                        newLuckyNumbers[i] = LuckyNumbers[i];
                    }
                    digit++;
                }
                LuckyNumbers = new int[LuckyNumbers.Length - (LuckyNumbers.Length / erase)];
                int x = 0;
                foreach (int number in newLuckyNumbers)
                {
                    if (number > 0)
                    {
                        LuckyNumbers[x] = number;
                        x++;
                    }
                }
                erase = LuckyNumbers[eraseCounter];
                eraseCounter++;
            }
            return LuckyNumbers;
        }
private Nachricht | Beiträge des Benutzers
TheBrainiac
myCSharp.de - Member

Avatar #avatar-3152.png


Dabei seit:
Beiträge: 832
Herkunft: /dev/null

beantworten | zitieren | melden

AHHHH!

Vier Minuten zu spät.....

Ich poste mal trotzdem meine Lösung, aber Kaji war leider schneller... :evil:

public static Int32[] GetLuckyNumbers(Int32 upperLimit)
{
    var luckyNumbers = new List<Int32>(new Int32[upperLimit]);

    for (var i = 1; i ≤ upperLimit; i++)
    {
        luckyNumbers[i - 1] = i;
    }

    var step = 2;
    var stepIndex = 1;

    while (luckyNumbers.Count ≥ step)
    {
        var numsToRemove = new List<Int32>();

        for (var i = step - 1; i < luckyNumbers.Count; i += step)
        {
            numsToRemove.Add(i);
        }

        while (numsToRemove.Count > 0)
        {
            luckyNumbers.RemoveAt(numsToRemove[numsToRemove.Count - 1]);
            numsToRemove.RemoveAt(numsToRemove.Count - 1);
        }

        step = luckyNumbers[stepIndex++];
    }

    return luckyNumbers.ToArray();
}

Der Code zum Testen war:

public static void Main()
{
    Test(10, new Int32[] { 1, 3, 7, 9 });
    Test(20, new Int32[] { 1, 3, 7, 9, 13, 15 });
    Test(30, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25 });
    Test(50, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49 });
    Test(63, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63 });
    Test(77, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75 });
    Test(100, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, 87, 93, 99 });
    Test(120, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, 87, 93, 99, 105, 111, 115 });

    Console.ReadKey();
}

private static void Test(Int32 num, Int32[] expected)
{
    var numbers = GetLuckyNumbers(num);

    var success = numbers.Length == expected.Length;

    if (success)
    {
        for (var i = 0; i < numbers.Length; i++)
        {
            if (numbers[i] != expected[i])
            {
                success = false;
                break;
            }
        }
    }

    Console.WriteLine(success ? "Success!" : "Failed!");

    Console.Write("Expected: ");
    for (var i = 0; i < expected.Length; i++)
    {
        Console.Write(expected[i]);
        Console.Write(" ");
    }

    Console.WriteLine();

    Console.Write("Computed: ");
    for (var i = 0; i < numbers.Length; i++)
    {
        Console.Write(numbers[i]);
        Console.Write(" ");
    }

    Console.WriteLine();
    Console.WriteLine();
}

Gruß, Christian.
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von TheBrainiac am .
There are 10 types of people in the world:
Those, who think they understand the binary system
Those who don't even have heard about it
And those who understand "Every base is base 10"
private Nachricht | Beiträge des Benutzers
m0rius
myCSharp.de - Member

Avatar #avatar-3125.png


Dabei seit:
Beiträge: 1043

beantworten | zitieren | melden

Hallo Kaji,

da du zuerst abgegeben hast und deine Lösung funktioniert, bist du mit der nächsten Aufgabe dran!


Hallo Schamese,

vielleicht tröstet es dich, wenn ich dir sage, dass deine Lösung die schönere gewesen wäre :).

m0rius
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von m0rius am .
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
private Nachricht | Beiträge des Benutzers
Kaji
myCSharp.de - Member



Dabei seit:
Beiträge: 602
Herkunft: Clausthal-Zellerfeld

beantworten | zitieren | melden

Hallo Leute,

da ich nicht wirklich fit bin und mir nichts so recht kurzes kleines einfällt wäre es schön wenn jemand anderes eine Aufgabe stellen könnte :)


Gruß Kaji
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo Kaji,

ich hätte da was. :-)


Hallo zusammen,

ist eine eher kleine Spielerei ohne praktischen Nutzen, aber dafür tricky zu programmieren.

Die Aufgabe:

Schreibt ein Windows Forms Programm, das unter Verwendung von Array.Sort (T [], IComparer<T>) oder Array.Sort (T [], Comparison<T>) eine vorgegebene Liste von Strings sortiert und am Ende in einer ListBox anzeigt, ohne dass dabei je das GUI blockiert und ohne dass es dabei "ungültige threadübergreifende Zugriffe" gibt.

Wo ist der Witz, werdet ihr euch fragen. Man kann doch einfach das Array.Sort in einem Thread starten und schon blockiert das GUI nicht. Und zum Anzeigen der sortierten Liste verwendet man Control.Invoke/BeginInvoke und schon hat man auch die "ungültigen threadübergreifenden Zugriffe" vermieden.

[FAQ] Warum blockiert mein GUI?
[FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)

Ok, stellt euch vor, die Liste von Strings enthält die sieben Eissorten Vanille, Schokolade, Stracciatella, Erdbeer, Zitrone, Kirsch und Schlumpf. Und die Liste soll nach dem Geschmack des Benutzers aufsteigend sortiert werden, die sortierte Liste fängt mit dem am wenigsten gemochten Eis an und hört mit dem leckersten auf.

Der Comparer muss also immer irgendwie den Benutzer fragen, welche von zwei Eissorten er leckerer findet. Dazu sollen die beiden gerade zu vergleichenden Eissorten in zwei Textboxen angezeigt werden und der Benutzer klickt auf die TextBox, in der das Eis steht, das er leckerer findet als das andere. Er muss sich entscheiden, "gleich lecker" ist nicht möglich.

Die Kommunikation zwischen dem Comparer und den GUI hinzubekommen ohne, dass das GUI blockiert, ist also die eigentliche Herausforderung der Aufgabe. Nicht ganz einfach, aber machbar. Viel Spaß dabei.

herbivore

PS: Damit die Länge des Codes nicht unnötig aufgebläht wird, muss sich der Benutzer kooperativ verhalten. Er muss also konsistent antworten. Was andersherum gesagt heißt, er darf keine widersprüchlichen Eingaben machen: Eine widersprüchliche Eingabe wäre, wenn er sagt: Vanille ist leckerer als Schokolade, Schokolade ist lecker Stracciatella und Stracciatella ist lecker als Vanille. In einem robusten Programm müsste man solche widersprüchlichen Eingaben verhindern oder abfangen, aber das würde wie gesagt den Code sehr aufblähen und soll daher nicht Teil der Aufgabe sein.

PPS: Bitte gebt minimalen, aber vollständigen Code an. Wenn man den Designer benutzt, erfordert das vermutlich etwas Nacharbeit, die ich aber als Teil der Aufgabe ansehe. Nur wenn man den geposteten Code 1:1 in eine Datei kopieren und (mit csc) übersetzen kann, gilt die Lösung. :-)
private Nachricht | Beiträge des Benutzers
edsplash
myCSharp.de - Member

Avatar #avatar-3111.jpg


Dabei seit:
Beiträge: 411

beantworten | zitieren | melden

Hallo herbivore

Hier meine Lösung!


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace Mcs
{
  class Program
  {
    [STAThread]
    static void Main(string[] args)
    {
      Application.EnableVisualStyles();

      Application.Run(new SortForm());
    }
  }

  class SortForm : Form
  {
    private ListBox sortedListListBox = new ListBox();
    private TextBox firstCompareTextBox = new TextBox();
    private TextBox secondCompareTextBox = new TextBox();
    private Button startSortingButton = new Button();
    private GroupBox compareGroupBox = new GroupBox();

    public SortForm()
    {
      InitializeComponent();
      sortedListListBox.Items.AddRange("Vanille, Schokolade, Stracciatella, Erdbeer, Zitrone, Kirsch und Schlumpf".Split(new string[] { ", ", " und " }, StringSplitOptions.RemoveEmptyEntries));
    }

    private void InitializeComponent()
    {
      //SortForm
      Height = 280;
      Width = 229;
      FormBorderStyle = FormBorderStyle.Fixed3D;
      Text = "UISort";
      MaximizeBox = false;

      //sortedListComboBox
      sortedListListBox.Top = 10;
      sortedListListBox.Left = 10;
      sortedListListBox.Width = 200;
      sortedListListBox.Height = 200;

      Controls.Add(sortedListListBox);

      //startSortingButton
      startSortingButton.Top = 215;
      startSortingButton.Width = 75;
      startSortingButton.Height = 23;
      startSortingButton.Left = 10;
      startSortingButton.Text = "Sort !1elf";
      startSortingButton.Click += StartSortingButton_Click;

      Controls.Add(startSortingButton);
    }

    private void PrepareComparisonControls()
    {
      //SortForm
      Width += 160;

      //compareGroupBox
      compareGroupBox.Top = 10;
      compareGroupBox.Left = 220;
      compareGroupBox.Width = 150;
      compareGroupBox.Height = 200;
      compareGroupBox.Text = "Compare";

      Controls.Add(compareGroupBox);

      //firstCompareTextBox
      firstCompareTextBox.Top = 15;
      firstCompareTextBox.Left = 10;
      firstCompareTextBox.Width = 100;
      firstCompareTextBox.Height = 23;
      firstCompareTextBox.ReadOnly = true;
      compareGroupBox.Controls.Add(firstCompareTextBox);

      //secondCompareTextBox
      secondCompareTextBox.Top = 43;
      secondCompareTextBox.Left = 10;
      secondCompareTextBox.Width = 100;
      secondCompareTextBox.Height = 23;
      secondCompareTextBox.ReadOnly = true;

      compareGroupBox.Controls.Add(secondCompareTextBox);
    }

    private void RemoveComparisonControls()
    {
      if (InvokeRequired)
      {
        Invoke(new Action(RemoveComparisonControls));
      }
      else
      {
        //Reset
        Width -= 160;
        Controls.Remove(compareGroupBox);
        compareGroupBox.Controls.Remove(firstCompareTextBox);
        compareGroupBox.Controls.Remove(secondCompareTextBox);
      }
    }

    private void StartSorting()
    {
      string[] elements = sortedListListBox.Items.Cast<String>().ToArray();
      UIComparer comparer = new UIComparer();

      comparer.ComparisonRequired += ComparisonRequired;

      ThreadPool.QueueUserWorkItem(
        new WaitCallback(o =>
      {
        Array.Sort<String>(elements, comparer);
        DisplaySortedResult(elements);
        RemoveComparisonControls();
      }), null);
    }

    private void DisplayComparison(CompareEventArgs e)
    {
      if (InvokeRequired)
      {
        Invoke(new Action<CompareEventArgs>(DisplayComparison), e);
      }
      else
      {
        firstCompareTextBox.Text = e.X;
        secondCompareTextBox.Text = e.Y;

        firstCompareTextBox.Click += (o, ea) => e.Chosen = e.X;
        secondCompareTextBox.Click += (o, ea) => e.Chosen = e.Y;
      }
    }

    private void DisplaySortedResult(String[] elements)
    {
      if (InvokeRequired)
      {
        Invoke(new Action<String[]>(DisplaySortedResult), ((Object)elements));
      }
      else
      {
        sortedListListBox.Items.Clear();
        sortedListListBox.Items.AddRange(elements);
      }
    }

    #region Events

    private void StartSortingButton_Click(Object sender, EventArgs e)
    {
      PrepareComparisonControls();
      StartSorting();
    }

    private void ComparisonRequired(Object sender, CompareEventArgs e)
    {
      DisplayComparison(e);
    }

    #endregion
  }

  class UIComparer : IComparer<String>
  {

    #region IComparer<string> Member

    public int Compare(String x, String y)
    {
      if (x == y)
        return 0;

      CompareEventArgs e = new CompareEventArgs(x, y);

      if (ComparisonRequired != null)
        ComparisonRequired.Invoke(this, e);

      e.WaitForUserInput();

      if (e.Chosen == x)
      {
        return 1337;
      }
      else
      {
        return -1337;
      }
    }

    #endregion

    public event EventHandler<CompareEventArgs> ComparisonRequired;
  }

  class CompareEventArgs : EventArgs
  {
    private String chosen = string.Empty;
    private AutoResetEvent waiter;

    public CompareEventArgs(String x, String y)
    {
      X = x;
      Y = y;
      waiter = new AutoResetEvent(false);
    }

    public void WaitForUserInput()
    {
      waiter.WaitOne();
    }

    public String X { get; set; }
    public String Y { get; set; }

    public String Chosen
    {
      get
      {
        return chosen;
      }
      set
      {
        chosen = value;
        waiter.Set();
      }
    }
  }
}

using Skill
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 52329
Herkunft: Berlin

beantworten | zitieren | melden

Hallo edsplash,

zwei Kleinigkeiten habe(hatte) ich gefunden:

Die eine, nämlich dass dem Benutzer Fragen der Art "Kirsch" vs "Kirsch" gestellt, hast du mittlerweile selber bemerkt und behoben.

Die andere ist, dass für die TextBoxen immer neue EventHandler registriert werden, ohne dass die alten deregistriert werden. Das führt mal abgesehen von dem bei steigender Listenlänge quadratischen Aufwand inbesondere dazu, dass die EventArgs die ganze Zeit nicht freigegeben werden.

Ansonsten funktioniert die Lösung genau so, wie ich mir das vorgestellt hatte, also inbesondere mit dem AutoResetEvent, durch den der Sort-Thread wartet, bis der Benutzer sich entschieden hat.

Du bist mit der nächsten Aufgabe dran.

herbivore
private Nachricht | Beiträge des Benutzers
edsplash
myCSharp.de - Member

Avatar #avatar-3111.jpg


Dabei seit:
Beiträge: 411

beantworten | zitieren | melden

Hallo herbivore

[offtopic]
Das mit dem Kirsch vs Kirsch halte ich nach wie vor für einen Schönheitsfehler, da der Quicksort Algorithmus ja sowieso nicht weiss, was er machen soll wenn zwei Werte gleich gross sind. Es ändert sich da Funktional also nichts. Habs aber trotzdem nachgebessert. ;)

Die Click Events hätte man allerdings generischer behandeln können, aber da hier gerne Quick&Dirty Lösungen präsentiert werden, bzw. das im ersten Beitrag erwähnt wird, ist das wohl auch nicht so Schlimm. ;)
[/offtopic]

Meine Aufgabe kommt dann Morgen. :)

Gruss
using Skill
private Nachricht | Beiträge des Benutzers
edsplash
myCSharp.de - Member

Avatar #avatar-3111.jpg


Dabei seit:
Beiträge: 411

beantworten | zitieren | melden

Hallo Zusammen

Meine Aufgabe:

Ziel ist es ein Interface IMap<TLeft, TRight> zu implementieren: ein IMap Objekt stellt quasi eine 1:1 Verknüpfung, bzw. eine eindeutige Zuordnung von Objekten verschiedenen Typs dar. Sowohl auf der linken, wie auf der rechten Seite darf nicht zweimal dasselbe Objekt vorkommen. Elemente der rechten Seite können dabei mit dem entsprechenden Element der linken Seite aus dem IMap Objekt geladen/beschrieben werden und umgekehrt.

Man sollte dabei sofern möglich vermeiden auf .NET Klassen wie Dictionary<T>, HashSet<T> zurück zu greifen. (Wäre ja langweilig :))


  interface IMap<TLeft, TRight>
  {
    /// <summary>
    /// Fügt der Map eine Zuordnung hinzu
    /// </summary>
    void Add(TLeft leftItem, TRight rightItem);

    /// <summary>
    /// Liest ein Element auf der rechten Seite der Map aus oder setzt dieses
    /// </summary>
    /// <param name="item">Ein Element auf der linken Seite, welches als Schlüssel dient</param>
    TRight this[TLeft item] { get; set; }

    /// <summary>
    /// Liest ein Element auf der linken Seite der Map aus oder setzt dieses
    /// </summary>
    /// <param name="item">Ein Element auf der rechten Seite, welches als Schlüssel dient</param>
    TLeft this[TRight item] { get; set; }

    /// <summary>
    /// Gibt an, ob auf der linken Seite ein spezifisches Element vorhanden ist
    /// </summary>
    bool Contains(TLeft item);

    /// <summary>
    /// Gibt an, ob auf der rechten Seite ein spezifisches Element vorhanden ist
    /// </summary>
    bool Contains(TRight item);

    /// <summary>
    /// Löscht eine Zuordnung
    /// </summary>
    /// <param name="item">Ein Element auf der linken Seite, welches als Schlüssel dient</param>
    bool Remove(TLeft item);

    /// <summary>
    /// Löscht eine Zuordnung
    /// </summary>
    /// <param name="item">Ein Element auf der rechten Seite, welches als Schlüssel dient</param>
    bool Remove(TRight item);

    /// <summary>
    /// Löscht alle Zuordnungen
    /// </summary>
    void Clear();

    /// <summary>
    /// Gibt die Anzahl der vorhandenen Zuordnungen zurück
    /// </summary>
    int Count { get; }
  }

Gruss
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von edsplash am .
using Skill
private Nachricht | Beiträge des Benutzers