Forum: Offtopic Schokolade aufteilen ist doch einfach! Oh wait (Programmierknobelaufgabe inside!)


von D. I. (Gast)


Lesenswert?

Hallo liebe Lötkolbenschwinger, Programmierfreunde und sonstige 
Knobelfans,
das Wetter ist gerade maximal beschissen (trotz Gewitter schwül wie 
sau). Was gibt es da besseres als seine eingerosteten Programmierkünste 
etwas nachzufeilen?
Da habe ich doch direkt ein nettes kleines Problemchen aufgetan, welches 
eigentlich garnicht so tragisch anmutet:

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2795

als PDF:

https://icpcarchive.ecs.baylor.edu/external/47/4794.pdf

Meine CPP-Lösung kommt auf 322 LOC und knapp durchs Zeitlimit (2.8xx 
Sekunden), da frage ich mich doch was die geballte Kompetenz hier 
zustande bringt und ob Yalu auch hier einen Hammer auspackt der mit 
0.xxx läuft. Auch alle anderen sind herzlich eingeladen zu knobeln, zu 
tüfteln und zu lösen bei so einem bescheidenen Abend :)

von D. I. (Gast)


Angehängte Dateien:

Lesenswert?

Ich habs noch geschafft die LOC um ein gutes Stück zu reduzieren und den 
Algo etwas zu verbessern, aber die Laufzeit liegt noch bei 2,6s.

Fühlt sich jemand bemüßigt und hat ne Idee ob es algorithmisch noch 
besser geht oder ob nur noch Optimierungspotenzial in den verwendeten 
Datenstrukturen liegt, da map, set, etc und Objekte ja doch etwas 
Laufzeit kosten, aber auf Pure C habe ich da keinen Bock.

Source siehe Anhang.

von D. I. (Gast)


Lesenswert?

Yalu, bist du im Urlaub :D

von Yalu X. (yalu) (Moderator)


Lesenswert?

D. I. schrieb:
> Yalu, bist du im Urlaub :D

Nein, nein, keineswegs :)

Deinen ersten Beitrag habe ich schlicht übersehen, beim zweiten habe ich 
(wieder einmal) erfolglos versucht, mich bei icpcarchive.ecs.baylor.edu 
zu registrieren (eine Anfrage diesbezüglich blieb bisher unbeantwortet), 
dann habe ich gesehen, dass es dieselbe Aufgabe auch auf 
uva.onlinejudge.org gibt, habe dort eine Lösung und ein paar optimierte 
Derivate davon eingereicht, die aber alle das Zeitlimit überschritten. 
Zudem habe ich im Moment gerade etliche andere Dinge zu tun 
(Autoschrauben, Fahrradschrauben, Backen, geschraubtes Fahrrad benutzen 
usw.).

Ich werde aber die Knobelei auf all meinen Wegen mit mir herumtragen. 
Außerdem esse ich gerade Schokolade, um einen besseren Bezug zum Problem 
zu bekommen. Irgenwann kommt dann sicher der Geistesblitz, wie man den 
Algorithmus noch um ein paar Größenordnungen beschleunigen kann :)

Deinen Algorithmus werde ich mir erst anschauen, wenn wenigstens einer 
meiner Versuche akzeptiert worden ist. Vielleicht können wir dann unsere 
Ideen vereinen und damit den Highscore knacken :D

Hast du vielleicht noch ein paar gute Testcases? Meine eigenen sind zu 
leicht und werden alle im zweistelligen Millisekundenbereich gelöst. Da 
ist es schwer, Ansatzpunkte für eine Optimierung zu finden, zumal ich 
natürlich auch keine Ahnung habe, um wieviel mein aktueller Algorithmus 
zu langsam ist.

von D. I. (Gast)


Lesenswert?

Yalu X. schrieb:
> Deinen ersten Beitrag habe ich schlicht übersehen, beim zweiten habe ich
> (wieder einmal) erfolglos versucht, mich bei icpcarchive.ecs.baylor.edu

Das ist sehr merkwürdig, Typo in der Mailadresse? Welcher Mailanbieter? 
Mails brauchen dort ewig bis sie benatwortet werden, evtl ne andere 
Adresse versuchen.

Yalu X. schrieb:
> Ich werde aber die Knobelei auf all meinen Wegen mit mir herumtragen.
> Außerdem esse ich gerade Schokolade, um einen besseren Bezug zum Problem
> zu bekommen. Irgenwann kommt dann sicher der Geistesblitz, wie man den
> Algorithmus noch um ein paar Größenordnungen beschleunigen kann :)

:)

Yalu X. schrieb:
> Deinen Algorithmus werde ich mir erst anschauen, wenn wenigstens einer
> meiner Versuche akzeptiert worden ist. Vielleicht können wir dann unsere
> Ideen vereinen und damit den Highscore knacken :D

Sehr löblich, ja wenn dus raus hast bin ich sicher kann man noch was 
machen.

Yalu X. schrieb:
> Hast du vielleicht noch ein paar gute Testcases? Meine eigenen sind zu
> leicht und werden alle im zweistelligen Millisekundenbereich gelöst. Da
> ist es schwer, Ansatzpunkte für eine Optimierung zu finden, zumal ich
> natürlich auch keine Ahnung habe, um wieviel mein aktueller Algorithmus
> zu langsam ist.

Also ein TC der mir gereicht hat mich ins TL laufen zu lassen war:
1
15
2
10 12
3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
4
0

von D. I. (Gast)


Lesenswert?

Ich hab meine Lösung auch mal auf UVA abgeschickt:
1
11944653   1099   Sharing Chocolate   Accepted   C++   2.978   2013-06-23 15:31:42

Also ganz knapp vorm TLE, immerhin 3/10s langsamer auf der UVA Seite.

von D. I. (Gast)


Lesenswert?

Yalu X. schrieb:
> habe dort eine Lösung und ein paar optimierte
> Derivate davon eingereicht, die aber alle das Zeitlimit überschritten.

Wie lautet denn deine aktuelle Lösung, rein interessehalber? Also die 
algorithmische Idee, nicht den Source :)

von Mikro 7. (mikro77)


Lesenswert?

D. I. schrieb:
> Also ein TC der mir gereicht hat mich ins TL laufen zu lassen war:
> 15
> 10 12
> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> 0

Lag das vllt. daran, dass es dafür keine Lösung gibt?!

von D. I. (Gast)


Lesenswert?

S. J. schrieb:
> D. I. schrieb:
>> Also ein TC der mir gereicht hat mich ins TL laufen zu lassen war:
>> 15
>> 10 12
>> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
>> 0
>
> Lag das vllt. daran, dass es dafür keine Lösung gibt?!

Auch ein Aspekt, ja. Aber in meiner Lösung rührte der TLE daher, dass 
ich eine Optimierung gemacht habe die dann doch keine war :)

von Arc N. (arc)


Lesenswert?

https://www.coursera.org/course/optimization
Da sind allerdings nur NP-vollständige Rätsel im Angebot ;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

So, ich habe gestern meinen Algorithmus umgeschrieben, so dass er jetzt 
effizientere Datenstrukturen benutzt. Zusammen mit ein paar weiteren 
Optimierungen läuft er im UVA-Online Judge in 0,482s, was immerhin für 
die Top 20 gereicht hat :)

Aber:

Beim Versuch, das Ganze weiter zu optimieren, erhielt ich eine "Wrong 
Answer"-Meldung und habe den Verdacht, dass schon die vorige Version 
fehlerhaft war, der Fehler aber bei der Ausführung auf dem UVA-Server 
durch (un-)glückliche Umstände nicht aufgetreten ist :(

Ich werde heute Abend versuchen, den Fehler zu beheben und dann noch 
Informationen zum Algorithmus liefern.

von Arsch G. (arschgwaf)


Lesenswert?

Sry hier den Thread so zu stören aber da hier im Moment die richten 
Leute unterwegs sind:

Hat jemand ne Buchempfehlung zu Algorithmik in C++?

C und C++ behersche ich bereits mehr oder weniger. Da aber der größte 
Teil über learning-by-doing gelaufen ist fehlt mir etwas der Blick dafür 
effiziente Algorythmen zu finden und wie ich Lösungen optimiere und 
bessere Strukturen wähle.

Das Buch sollte aber schon bei den Grundlagen der Algorithmik anfangen, 
da es bei Bubblesort bei mir schon wieder aufhört mit dem Wissen.

von D. I. (Gast)


Lesenswert?

Yalu X. schrieb:
> Zusammen mit ein paar weiteren
> Optimierungen läuft er im UVA-Online Judge in 0,482s, was immerhin für
> die Top 20 gereicht hat :)

Sauber, net schlecht, bin gespannt. Jetzt kannst du ja auch mal in meine 
Lösung gucken.

Arsch Gwaf schrieb:
> Das Buch sollte aber schon bei den Grundlagen der Algorithmik anfangen,
> da es bei Bubblesort bei mir schon wieder aufhört mit dem Wissen.

Das Standardwerk zu Algorithmen ist der Cormen:

http://www.amazon.de/Introduction-Algorithms-Thomas-H-Cormen/dp/0262533057/ref=sr_1_1?ie=UTF8&qid=1372242393&sr=8-1&keywords=introduction+to+algorithms

Der ist programmiersprachenunabhängig, bietet aber einen fundierten 
Einstieg in die Materie.

von Paul B. (paul_baumann)


Lesenswert?

Ich verstehe das Rätsel nicht richtig.
Eine Tafel Schokolade soll so unter Leuten aufgeteilt werden, daß jeder
die gewünschte Anzahl Stücke erhält. Das kann doch nicht funktionieren,
wenn einer nur ein Stück möchte und ein Vielfraß den Rest verschlingt.

Ich habe mir das übersetzen lassen, aber so richtig klar wird die 
Aufgabe
nicht.

Das geht offenbar nicht nur mir so, denn sonst wäre die Beteiligung an
der Lösung doch viel größer.

MfG Paul

von Mikro 7. (mikro77)


Angehängte Dateien:

Lesenswert?

Moin,

ich habe auch mal mitgemacht.

Da bei icpcarchive.ecs.baylor die Registrierung nicht klappt, bin ich 
nun auch auf UVa: 
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=problem_stats&problemid=3540&category=. 
Meine Implementierung scheint gut auf die Testsuit abgestimmt zu sein. 
:-)

Wer noch Test Cases sucht, ich habe ein paar generiert (Anhang).

Grüße, mikro

btw: Der (nicht lösbare) Test Case von  D. I. hat mich fast zu 
Verzweiflung getrieben: Da gibt es scheinbar sehr viele Kombinationen.

von Vlad T. (vlad_tepesch)


Lesenswert?

Paul Baumann schrieb:
> Ich verstehe das Rätsel nicht richtig.

DAs Problem ist, dass du keine Ecken Rausbrechen darfst, und jeder die 
geforderte Stückzahl in Form eines zusammenhängenden 
Schokoladentafelstücks will.
das heißt du darfst jemanden, der 4 Stück will nicht (2x 1x2)  oder (1x3 
+ 1x1) geben, sondern entweder 1x4 oder 2x2.

Das Problem besteht also darin, die Tafel so aufzuteilen, dass sie mit 
geraden Bruchkanten in die geforderten Stücke zerlegt werden kann.
(bzw zu entscheiden, ob dies möglich ist, oder nicht)

von D. I. (Gast)


Lesenswert?

S. J. schrieb:
> Meine Implementierung scheint gut auf die Testsuit abgestimmt zu sein.
> :-)

Top 1 zu machen ist mal ein Statement, Respekt! Wie lang hast du 
getüftelt?

von Yalu X. (yalu) (Moderator)


Lesenswert?

S. J. schrieb:
> Da bei icpcarchive.ecs.baylor die Registrierung nicht klappt, bin ich
> nun auch auf UVa:

Ah, dann habe also nicht nur ich das Problem :)

> Meine Implementierung scheint gut auf die Testsuit abgestimmt zu sein.
> :-)

Mist! Mein Ziel für heute Abend war, meinen Algorithmus noch einmal um 
den Faktor 2 zu beschleunigen, dann wäre ich mit 2,4s auf Platz 1 
gelandet. Jetzt muss ich mich schon deutlich mehr anstrengen, und die 
Hoffnung sinkt gewaltig ;-)

Auf jeden Fall herzlichen Glückwunsch zur Verbesserung des bestehenden 
Rekords um fast Faktor 2 :)

Wenn sich noch 17 weitere mikrocontroller.net-Teilnehmer aufraffen, 
schaffen wir es vielleicht, die Hiscore-Liste komplett zu erobern :D

> Wer noch Test Cases sucht, ich habe ein paar generiert (Anhang).

Danke, die Beispiele werde ich mal testen. Ich habe zwar mittlerweile 
auch einen einfachen Testgenerator gebastelt, aber inzwischen sind die 
damit generierten Beispiele schon wieder zu leicht :)

> btw: Der (nicht lösbare) Test Case von  D. I. hat mich fast zu
> Verzweiflung getrieben: Da gibt es scheinbar sehr viele Kombinationen.

Die Zeit dafür liegt bei meinem Algorithmus im Millisekundenbereich. 
Wenn man das Einlesen und Ausgeben der Daten nicht mitrechnet, sind es 
sogar nur etwa 14µs (auf einem alten PC) :)

Das Beispiel ist aber auch wirklich einfach. Viel schwieriger ist es, 
aus einer 10×12-Tafel ein rechteckiges Stück mit 13 Feldern 
herausbrechen ;-)

von D. I. (Gast)


Lesenswert?

Ist an euren Algorithmen grundlegend was anders als bei mir oder 
"beschränken" sich eure Optimierung auf effizienteres Ausschließen von 
unmöglichen Fällen. Eine offensichtliche Optimierung, die ich bei mir 
sehe ist die Teilmengen effizienter zu generieren.

Ich hätte da noch ein Aufgäbchen, die erstmal garnicht so schwer 
scheint, aber da fehlt mir noch die Idee, zumindest erscheint sie mir 
dann doch nicht so einfach.

Aber ich stelle sie einfach mal zur Disposition:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=257&page=show_problem&problem=3535

Ist sogar aus demselben Wettbewerb.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Paul Baumann schrieb:
> Ich verstehe das Rätsel nicht richtig. Eine Tafel Schokolade soll so
> unter Leuten aufgeteilt werden, daß jeder
> die gewünschte Anzahl Stücke erhält. Das kann doch nicht funktionieren,
> wenn einer nur ein Stück möchte und ein Vielfraß den Rest verschlingt.

Genau. Du hast die Aufgabe also offensichtlich doch verstanden.

Ein einfaches Beispiel wäre: Eine klassische 6×4-Tafel imit 24 Feldern 
und zwei Esser. Der eine will 23 Felder, der andere soll nur eins 
bekommen. Das geht mit einem geradlinigen Bruch nicht, also muss das 
Programm ausgeben:
1
Case 1: No

Möchte aber der eine 8 Felder und der andere 16, ist das kein Problem, 
also
1
Case 2: Yes

von Paul B. (paul_baumann)


Lesenswert?

@Vlad & Yalu

Ich bedanke mich für Eure Erklärungen und Ausführungen.

Yalu schrub:
>Viel schwieriger ist es,
>aus einer 10×12-Tafel ein rechteckiges Stück mit 13 Feldern
>herausbrechen ;-)

Ja, soweit war ich vorhin auch schon einmal, als ich versuchte, einen
Algorithmus dafür auf Kästchenpapier durchzuspielen....


Das ist wirklich eine harte Nuss, die mich zu der Überlegung brachte,
Gästen nur ein Glas Nutella und einen Löffel zur Verfügung zu stellen.
;-)
MfG Paul

von Yalu X. (yalu) (Moderator)


Lesenswert?

D. I. schrieb:
> Ist an euren Algorithmen grundlegend was anders als bei mir oder
> "beschränken" sich eure Optimierung auf effizienteres Ausschließen von
> unmöglichen Fällen.

Im Prinzip passiert bei mir genau das. Und es sind nicht einmal arg 
viele Kriterien, die dabei berücksichtigt werden. Allerdings verwende 
ich noch Tabellen, um Dinge möglichst nicht mehrfach berechnen zu 
müssen. So etwas verwendest du aber teilweise auch, wie ich gerade 
gesehen habe.

> Eine offensichtliche Optimierung, die ich bei mir
> sehe ist die Teilmengen effizienter zu generieren.

Die Teilmengengenerierung ist bei mir seit gestern so ultraeffizient, 
dass ein Gelegenheits-C-Programmierer kaum eine Chance hat, sie auf 
Anhieb zu verstehen ;-)

Es ist tatsächlich so, dass pro Testfall etwa 95% der Rechenzeit in 
einen einzigen memset-Aufruf verbraten wird, der zu Beginn des 
Algorithmus eine größeres Array mit Nullen belegt. Gestern Abend gelang 
es mir, die Größe dieser Tabelle durch dichteres Packen der Inhalte zu 
halbieren, was sofort eine Verringerung der Laufzeit von 0,9s auf unter 
0,5s bewirkte. Aber dazu heute Abend mehr.


Paul Baumann schrieb:
> Das ist wirklich eine harte Nuss, die mich zu der Überlegung brachte,
> Gästen nur ein Glas Nutella und einen Löffel zur Verfügung zu stellen.

1 Glas und 1 Löffel für n Gäste? Wenn das nicht Tote und Verletzte 
gibt :)

von Yalu X. (yalu) (Moderator)


Lesenswert?

@D. I.:

Ich habe gerade mal kurz deinen Code überflogen. Er entspricht in vielen 
Punkten dem meinigen. Du hast in computeHeight ein paar überflüssige 
Berechnungen und Abfragen drin, so z. B.
1
        if (width * height == currentSize)
2
3
        if (possibleSize + complimentarySize != currentSize) continue;
4
5
        if (possibleSize % width == 0 && complimentarySize % width == 0)    // nur rechter Operand von &&
6
7
        if (possibleSize % height == 0 && complimentarySize % height == 0)  // nur rechter Operand von &&

Diese Abfragen sind immer wahr, wenn das Programm ansonsten korrekt ist. 
Ich habe ähnliche Abfragen als Asserts eingebaut, um potentielle Fehler 
aufzudecken, sie hinterher aber wieder entfernt.

Dass deine Teilmengengenerierung nicht optimal ist, hast du ja schon 
geschrieben:
1
    for (int possiblePieceSet = 1; possiblePieceSet < pieceSet; possiblePieceSet++)
2
    {
3
        ...
4
        //is it a proper subset
5
        if ((pieceSet & possiblePieceSet) != possiblePieceSet) continue;
6
        ...
7
    }

Wenn nur das erste und das letzte Element in der Menge enthalten sind, 
macht die Schleife 16381 unnötige Durchläufe, in denen zwar nicht viel 
gerechnet wird, aber in Summe doch einiges an Rechenzeit verbraten wird.

Dann scheint es so, dass du bei der Aufteilung der Menge in Teilmenge 
und Komplementärmenge nicht berücksichtigst, dass die beiden 
austauschbar sind. So hast du pro Rekursionsebene den doppelten Aufwand, 
da jede Teilmenge in einem anderen Schleifendurchlauf als 
Komplementärmenge auftaucht.


Ich habe zusätzlich noch folgende Features eingebaut:

Wenn der größte Primfaktor eines Zielstücks größer als die Länge und 
Breite eine Tafelstücks ist, kann es nicht aus diesem herausgebrochen 
werden. Man kann also an dieser Stelle die Rekursion abbrechen. Diese 
größten Primfaktoren werden natürlich für jede mögliche Teilmenge 
vorausberechnet.

Die Teilegrößen werden vorsortiert. Wenn die größten Zielstücke zuerst 
bearbeitet werden, treten potentielle Sackgassen tendenziell schon in 
den höheren Rekursionsebenen auf, was unnötig tiefes Absteigen in den 
Suchbaum vermeidet. Der mittelere Gewinn dieser Maßnahme (etwa 30%) ist 
aber geringer, als ich ewartet hatte.

Bei der horizontalen und vertikalen Teilung der Tafel kann man sich eine 
von beiden sparen, wenn die Tafel quadratisch ist. Das bringt auch nicht 
viel, kostet aber auch fast nichts.

Das Ergebnis jeder geprüften Teilmenge und Tafelgeometrie wird in einer 
Tabelle abgelegt. Jeder Eintrag dieser Tabelle ist 2 Bits groß, um die 
drei Zustände "unbekannt", "unlösbar" und "lösbar" zu speichern. Es gibt 
maximal 2¹⁵·(100·(100+1)/2+100)≈1.7E8 Einträge. Tafelgeometrien mit 
vertauschter Länge und Breite belegen dabei den gleichen Eintrag. Um die 
Indizes leicht aus der Teilmenge und der Tafelgeometrien berechnen zu 
können, werden diese zusammen als 15+13-Bit-Zahl codiert. Damit hat die 
Tabelle eine Größe von 64 MiByte, weswegen das Löschen so lange dauert. 
Ich habe aber mittlerweile festgestellt, dass von den 1.7E8 Einträgen im 
Mittel nur ein paar Hunderttausend belegt werden, so dass man mit einer 
anderen Datentstruktur (evtl. Hash-Table) trotz der etwas längeren 
Zugriffszeiten in Summe besser liegt.

Und die Schleife zur Generierung aller möglichen Paare aus Teil- und 
Komplementärmenge sieht bei mir so aus:
1
  set1 = set & ~-set;
2
  for (subset = -set1 & set1; subset; subset = -(~subset & set1) & set1) {
3
    complset = set ^ subset;
4
    ...
5
  }

Das ist die etwas kürzere und obfusziertere Variante, von der ich 
ursprünglich glaubte, dass der Compiler daraus maximal schnellen Code 
generiert. Sie ist aber (zumindest theoretisch) nicht portabel, weil sie 
die Darstellung negativer Zahlen im Zweierkomplement voraussetzt. Die 
portable, aber immer noch ausreichend undurchsichtige Variante habe ich 
verschlampt, kann sie aber bei Bedarf noch einmal aus irgendwelchen 
Schmierzetteln rekonstruieren. Sie hat ein paar Zeichen mehr Quellcode, 
der GCC erzeugt daraus aber exakt den gleichen, effizienten 
Assembler-Code (was beweist, dass er diesen kryptischen Code 
tatsächlich verstanden hat).

Ich habe noch ein paar andere Dinge ausprobiert:

Die Teilung in jedem Rekursionsschritt wird erst in Längs- und dann in 
Querrichtung gemacht, in der Hoffnung, dass dies einen ähnlich positiven 
Effekt wie die Vorsortierung (s.o.) hat.

Bei der Rekursion wird die kleinere der beiden Teilmengen (oder auch die 
kleinere der beiden Tafelteile) zuerst bearbeitet.

Gebracht hat das aber außer LOCs so gut wie nichts.

Ich habe noch ein paar weitere Ideen im Hinterkopf, die ich aber heute 
wahrscheinlich nicht mehr testen werde. Jetzt wird erst einmal gegessen 
:)

von Paul B. (paul_baumann)


Lesenswert?

Yalu schrub:
>Jetzt wird erst einmal gegessen :)

Ein Stück Schokolade?
Flücht

MfG Paul

von D. I. (Gast)


Lesenswert?

Danke Yalu für deinen ausführlichen Post. Nicht alle aber einige Ideen 
davon hatte ich auch, aber mir hat dann der sportliche Ehrgeiz gefehlt 
die noch zu Coden nachdem das Problem mit dem eigentlichen Algorithmus 
gelöst war, aber trotzdem sehr cool.

Yalu X. schrieb:
> Und die Schleife zur Generierung aller möglichen Paare aus Teil- und
> Komplementärmenge sieht bei mir so aus:
>   set1 = set & ~-set;
>   for (subset = -set1 & set1; subset; subset = -(~subset & set1) & set1)
> {
>     complset = set ^ subset;
>     ...
>   }

Das wiederrum war sehr lehrreich und bis dato die absolut kaputteste Art 
Bit-Teilmengen zu erzeugen, die ich je gesehen habe.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Hier ist der Vollständigkeit noch die IMHO etwas leichter verständliche 
Version der Teilmengengenerierung:
1
  set1 = set & (set - 1);
2
  for (subset = (~set1 + 1) & set1; subset; subset = ((subset | ~set1) + 1) & set1) {
3
    complset = set ^ subset;
4
    ...
5
  }

Entgegen meiner Aussage von oben ist diese Version sogar etwas schneller 
(1 Instruktion weniger in der Schleife), da der konstante Ausdruck ~set1 
vor der Schleife berechnet wird.

von D. I. (Gast)


Lesenswert?

Wäre ich noch WiMi und Klausuraufgaben stellen, wär das ein toller 
Kandidat für die 1er Bremse ;)

Jetzt betätige ich mich erstmal wie vor ein paar Posts erwähnt als 
Kanalgräber, was sich als deutlich kniffliger erweist als als 
Schokoladenbrecher... :/ das wird wohl ne kurze Nacht.

von Mikro 7. (mikro77)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe mir gerade  D.I.s Quelltext angeguckt. Offensichtlich benutzen 
wir alle den gleichen Ansatz.

Das mit den Bitfeldern ist ne hübsche Idee. Allerdings braucht man nicht 
wirklich alle Kombinationen. Ich benutze einen Zähler, der durch die 
notwendigen Kombinationen läuft. Solange das Problem lösbar ist, bringt 
das möglicherweise einen Vorteil.

Zum Speichern meiner Zwischenergebnisse habe ich einige Varianten 
probiert. Letztendlich war eine klassische Hash-Table (Kollisionen 
werden entfernt und ggf. später wieder neu berechnet) am effizientesten. 
Die Performance von Maps, Multimaps und Hashed Lists liegt aber auch 
nicht so weit weg.

Um die Komplexität des Alorithmus zu verringern, müßte man imho iwie die 
Teilergebnisse in den Algorithmus einfließen lassen (also eben nicht 
über Lookup). Da fällt mir aber auf Anhieb nicht ein, wie man das 
sinnvoll lösen könnte (falls es überhaupt möglich ist).

Meine submitteten Code habe ich mal angehangen. Ist relativ lang und 
mehr oder weniger dokumentiert. Ist auch noch Debug Zeugs drin. 4% 
Performancegewinn sind drin, wenn der Zähler die Chunk Size mitzählt 
(ohne Compileroptimierung sind es gar 20%).

Grüße, mikro

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

Yalu X. schrieb:
> Beim Versuch, das Ganze weiter zu optimieren, erhielt ich eine "Wrong
> Answer"-Meldung und habe den Verdacht, dass schon die vorige Version
> fehlerhaft war, der Fehler aber bei der Ausführung auf dem UVA-Server
> durch (un-)glückliche Umstände nicht aufgetreten ist :(

Ich habe den Fehler nun behoben. Es war natürlich, wie so oft, ein ganz 
dummer. Ihn zu finden hat aber trotzdem unendlich viel Zeit gekostet, 
weil er bei meinen eigenen Testfällen nicht auftrat und ich ihn nur 
durch entsprechend viele Testläufe auf dem Online-Judge eingrenzen 
konnte.

Yalu X. schrieb:
> Ich habe aber mittlerweile festgestellt, dass von den 1.7E8 Einträgen im
> Mittel nur ein paar Hunderttausend belegt werden, so dass man mit einer
> anderen Datentstruktur (evtl. Hash-Table) trotz der etwas längeren
> Zugriffszeiten in Summe besser liegt.

Der Cache ist jetzt als Hash-Tabelle mit 512ki Einträgen realisiert.

Beides zusammen reduziert die Laufzeit auf 0,086s.

Den aktuellen Quellcode habe ich angehängt.

von D. I. (Gast)


Lesenswert?

Yalu X. schrieb:
> Den aktuellen Quellcode habe ich angehängt.

Wer braucht schon CleanCode :) 86ms ist klasse und der Code ist wirklich 
kurz und knackig. Mich würden original Contestlösungen interessieren. 
Soweit ich mich erinnern kann wurde diese Aufgabe nach 55 Min gelöst. 
Also zwischen Conteststart damals bis zur korrekten Lösung im Contest 
brauchte es nur 55 Min.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.