ProgrammieraufgabenHier wird regelmäßig eine neue Programmieraufgabe gestellt, die dann gelöst werden soll und in Zusammenarbeit mit den Moderatoren auch besprochen werden kann.
Wieviele Steine passen in eine Fläche?
Diskussion: Wieviele Steine passen in eine Fläche? im Forum Programmieraufgaben, in der Kategorie Code Kitchen; Anzeige
Also, Jungs (bis auf mauralix und lightsaver):
Ihr macht Euch die Aufgabe zu einfach.
Auch wenn es nur um ...
Anzeige Also, Jungs (bis auf mauralix und lightsaver):
Ihr macht Euch die Aufgabe zu einfach.
Auch wenn es nur um rechteckige Bausteine geht und auch wenn diese alle gleich aussehen, ist das Problem ein Optimierungsproblem.
Ihr könnte mit der einfachen Flächendivision einen Grenzwert bestimmen, der eventuell erreicht werden KANN.
Sofern die beiden Bedingungen "rechteckig" und "alle gleich" nicht auf die Bausteine und die auszulegende Fläche zutreffen, wird auf jeden Fall eine Aufgabe daraus, die exponentiellen Lösungsaufwand erfordert und bei der noch nicht mal feststeht, daß die Bausteine horizontal oder vertikal ausgerichtet eine optimale Flächendeckung ergeben.
MIT diesen beiden Bedingungen müßte man jedoch auch erstmal beweisen, daß die Flächennutzung bei horizontaler und vertikaler Ausrichtung optimal werden muß. Erst dann lohnt es sich, einen Algorithmus dafür zu schreiben.
Mal als Beispiel:
Baustein 3*5
Fläche 13*14
Grenzwert nach Flächendivision: 12, Rest 2
tatsächlich mögliche Auslastung: 11,
aber nur, wenn die Bausteine etwa (vom Prinzip her) SO angeordnet werden:
geht man geringfügig anders ran, kann man eventuell nur noch 10 unterbringen.
GANZ so trivial ist die Aufgabe also nicht...
Falls die Annahme zutrifft, daß Optimalität erreiche werden kann, wenn man die Fläche damit auszulegen beginnt, daß man Bausteine einfach nur in einer Richtung ausgerichtet und in einer Ecke angesetzt erstmal bis auf die Reststreifen verteilt und dann jene zu füllen versucht, indem man die Bausteine 90° gedreht und wieder in einer verbleibenden Ecke angesetzt verteilt, müßt Ihr immer noch mindestens zwei Varianten davon probieren und die bessere auswählen!
Oder mit irgendwelchen Teilerverhältnis-Betrachtungen vorneweg die Optimallösung voraussagen.
Hmm also wie ich das verstanden hab, soll man die Steine auch drehen,
um die optimale Fläche zu erhalten.
z.B:
Fläche 5x3
Stein: 2x2
Dann nicht nur einfach
[XX_]
[XX_]
[XX_]
[XX_]
[XX_]
(X= steinfeld, _=leer)
sondern
[XXO]
[XXO]
[XXO]
[XXO]
[XX_]
(O= gedrehter Stein)
Original von 0wnZ
schmeißt nur errors aus :o
Sorry, mehr Infos
Also: Unter WinXP mit Konsole sagt es, dass bei "Passt nicht" in den Zeilen 12, 14, 16, 18, 28 ein kEND erwartet wird anstelle von tCONSTANT
und in Zeile 35 wird ein $end anstelle eines kEND erwartet.
Ich hab den Code 1 zu 1 in Scite übernommen und einfach gesichert.
edit2: Ich sehs, da fehlen 2 "n"s nach dem \ für den Zeilenumbruch in den Zeilen 5 und 9 ! Damit wird das Anführungszeichen escaped und der String nicht beendet !
Ja stimmt, ich hatte es schon bemerkt, dachte aber dass ich es schon richtig gepostet hätte
Hmmm, vielleicht solltet Ihr Euch erstmal untereinander klar werden, was Ihr überhaupt wollt? Bzw. was ihr meint?
Wenn Ihr von Problemen in 3 Dimensionen redet, solltet Ihr Euch ALLE darauf festlegen.
Wenn Ihr in zwei oder gar nur in einer Dimension denken wollt, natürlich dasselbe.
Sonst verhalten sich Eure Postings wie Socken zu Wackelpudding.
Ich vermute mal, daß das ursprünglich genannte Problem mitnichten darin besteht, zu beweisen, daß man simple Input- und Print-Operationen und Grundrechenarten in irgendeiner Programmiersprache beherrscht.
Aber vielleicht sagt der Aufgabensteller nochmal ein Wort dazu?
----
Ein paar weitere Gedanken:
Schon ein triviales Einstiegs-Beispiel zeigt, daß die Überlegungen zu Vereinfachungen allesamt Murks sind:
Man nehme längliche Bausteine 1*n und einen Kasten mit einer Seitenlänge von (n-1) * (2n-1). Senkrecht paßt nichts. Waagerecht paßt genau eine Reihe von n-1 Bausteinen, die aber ein riesiges Loch von (n-1)*(n-1) ungefüllt läßt. Dieses kann jedoch noch mit einer Menge von schräg liegenden Bausteinen gefüllt werden. Und eventuell wird der verbleibende Restplatz dadurch verringert, daß man sämtliche Bausteine von vornherein (und dann weniger schräg) einsetzt.
Nachdem ich jetzt zu dieser Erkenntnis gekommen bin, daß schräge Plazierung - falls sie vom Aufgabensteller nicht explizit ausgenommen wird - notwendig sein kann, halte ich die Aufgabe für überhaupt alles andere als trivial. Ideen zur bedingungslos und garantiert optimalen Lösung fehlen mir jedenfalls zur Zeit noch.
Als Optimierungen ist folgende Prämisse eingeflossen:
Optimale Platzausnutzung kann (nur) erreicht werden, wenn die Steine zusammenhängend platziert werden (also keine "freistehenden" Steine existieren).
Der Algorithmus ist aber immernoch ziemlich langsam (Er schafft das 13x14 Feld mit 3x5 Bausteinen Beispiel unter einer Minute. Vor der Optimierung lief der stundenlang fröhlich vor sich hin...). Das "Urbeispiel" (200x250 Feld mit 15x10 Bausteinen) würde wahrscheinlich seeeeeehr lange laufen.... Ohne weitere intelligente Optimierungen ist der Algo einfach zu langsam für solche Dimensionen.. (@weitere Optimierungen: Sowas wie beim Damenproblem beispielsweise. Das Hauptproblem in immer kleinere Teilprobleme zerteilen und am Ende wieder alles zusammenführen. Oder eine Art von caching ausarbeiten. Aber das war mir jetzt zu "fancy" ;) Bin froh das es überhaupt funktioniert.)
Ich komm insgesamt auf 451 Zeilen Code (stdlib.h und stdio.h nicht mitgezählt).
Naja, dafür hab ich on-the-way auch noch das maximal-rectangle-problem gelöst (getBiggestFree()), ohne das es direkt was mit der Aufgabenstellung zu tun hätte. (Wird auch nicht im Algorithmus verwendet.)
rect.c
Erstmal das Hauptprogramm, beziehungsweise das userinterface, rect.c:
Code:
#include <stdio.h>
#include <stdlib.h>
#include "rect.h"
/*
* Es soll ein Algorithmus entwickelt werden der die maximale Anzahl an
* Bausteinen bestimmt die in eine bestimmte Fläche passen.
*
* z.B: ich gebe "ihm" eine Fläche von 2m x 2,5m vor und sage das ein Baustein 0,15m x 0.1m groß ist.
* Nun soll der Algorithmus die Bausteine so anordnen das die Große Fläche möglichst vollständig
* abgedeckt wird. und es so wenig wie möglich Lücken bleiben.
*
* Die anzahl de genutzten Bausteine und wenn möglich die Form in der die
* Bausteine verlegt worden sind, sollen ausgegeben werden.
*/
int main(int argc, char *argv[]) {
if (argc != 5) {
fprintf(stderr, "usage: %s <fieldxdim> <fieldydim> <rectxdim> <rectydim>\n", argv[0]);
exit(EXIT_FAILURE);
}
int xdim, ydim;
xdim = atoi(argv[1]);
ydim = atoi(argv[2]);
field *f = createField(xdim, ydim);
int rectxsize, rectysize;
rectxsize = atoi(argv[3]);
rectysize = atoi(argv[4]);
rect *r = createRect(0, 0, rectxsize, rectysize);
fillField(f, r);
return EXIT_SUCCESS;
}
Dann die eigentliche Implementierung:
rect.h
Code:
#include <stdio.h>
#include <stdlib.h>
#ifndef XALLOC_H
#include "xalloc.h"
#endif
#include "voidStack.h"
typedef struct {
int x1, y1, x2, y2; /* with x2 > x1 and y2 > y1 */
} rect;
typedef struct {
int **f;
rect *r; /* the rect describing the field */
int xdim, ydim; /* redundant, since those infos are already contained within
the supplied rect, but it's easier to read that way.. */
} field;
void printField(field *f);
field *createField(int xdim, int ydim);
void delField(field *f);
void setField(field *f, rect *r, int val);
void copyField(field *src, field *dest);
int isFree(field *f, rect *r); /* returns 1 if the area described by rect is all zeros in field */
int getFreeCount(field *f);
rect *getBiggestFree(field *f); /* gets biggest free rect in field *f */
stack getAllFree(field *f, rect *r);
void fillField(field *f, rect *r);
int fillFieldRec(field *f, rect *r, int rectcount, int *max, field *sol);
rect *createRect(int startx, int starty, int xsize, int ysize);
void copyRect(rect *src, rect *dest);
void setRect(rect *r, int x1, int y1, int x2, int y2);
int getArea(rect *r);
void delRect(rect *r);
void moveRect(rect *r, int x, int y);
void resizeRect(rect *r, int xsize, int ysize);
void rotateRect(rect *r); /* rotates *r by 90 degrees. (the top left corner will be the same) */
int isInRect(rect *r, int x, int y);
int isSubRect(rect *r, rect *sub);
int areCongruent(rect *r, rect *s);
void fillField(field *f, rect *r) {
int max = 0;
field *solution = createField(f->xdim, f->ydim);
fillFieldRec(f, r, 1, &max, solution);
printField(solution);
}
int fillFieldRec(field *f, rect *r, int rectcount, int *max, field *sol) {
stack p = getAllFree(f, r);
rect *new = shift(p);
if (new == NULL) {
destroyStack(p);
if (rectcount > *max) {
*max = rectcount;
copyField(f, sol);
if (getFreeCount(f) < getArea(r)) {
return 1;
}
printField(f); /* print intermediate solutions */
}
return 0;
}
while (new != NULL) {
setField(f, new, rectcount);
if (fillFieldRec(f, r, rectcount + 1, max, sol)) {
return 1;
}
setField(f, new, 0);
delRect(new);
new = shift(p);
}
destroyStack(p);
return 0;
}
rect *createRect(int startx, int starty, int xsize, int ysize) {
rect *r = xmalloc(sizeof(rect));
r->x1 = startx;
r->y1 = starty;
r->x2 = startx + xsize - 1;
r->y2 = startx + ysize - 1;
return r;
}
void delRect(rect *r) {
free(r);
}
void setRect(rect *r, int x1, int y1, int x2, int y2) {
r->x1 = x1;
r->y1 = y1;
r->x2 = x2;
r->y2 = y2;
}
void copyRect(rect *src, rect *dest) {
/* sets *dest to describe the same area as *src */
dest->x1 = src->x1;
dest->y1 = src->y1;
dest->x2 = src->x2;
dest->y2 = src->y2;
}
int getArea(rect *r) {
return ( ((r->x2 - r->x1) + 1) * ((r->y2 - r->y1) + 1) );
}
void resizeRect(rect *r, int xsize, int ysize) {
r->x2 = r->x1 + xsize;
r->y2 = r->y1 + ysize;
}
void moveRect(rect *r, int x, int y) {
int xsize, ysize;
xsize = r->x2 - r->x1;
ysize = r->y2 - r->y1;
r->x1 = x;
r->y1 = y;
resizeRect(r, xsize, ysize);
}
void rotateRect(rect *r) {
/* rotates *r by 90 degrees. (the top left corner will stay the same) */
int xsize, ysize;
xsize = r->x2 - r->x1;
ysize = r->y2 - r->y1;
resizeRect(r, ysize, xsize);
}
field *createField(int xdim, int ydim) {
int *rows = xmalloc(xdim * ydim * sizeof(int));
int **cols = xmalloc(ydim * sizeof(int *));
int i;
for (i = 0; i < ydim; i++) {
cols[i] = &rows[i * xdim];
}
rect *r = xmalloc(sizeof(rect));
r->x1 = 0;
r->y1 = 0;
r->x2 = xdim - 1;
r->y2 = ydim - 1;
field *f = xmalloc(sizeof(field));
f->f = cols;
f->r = r;
f->xdim = xdim;
f->ydim = ydim;
setField(f, f->r, 0);
return f;
}
void delField(field *f) {
free(f->r);
int i;
for (i = 0; i < f->ydim; i++) {
free(f->f[i]);
}
free(f->f);
free(f);
}
void setField(field *f, rect *r, int val) {
if (! isSubRect(f->r, r)) {
return;
}
int x, y;
for (y = r->y1; y <= r->y2; y++) {
for (x = r->x1; x <= r->x2; x++) {
f->f[y][x] = val;
}
}
}
void copyField(field *src, field *dest) {
int x, y;
for (y = 0; (y < src->ydim && y < dest->ydim); y++) {
for (x = 0; (x < src->xdim && x < dest->xdim); x++) {
dest->f[y][x] = src->f[y][x];
}
}
}
int isFree(field *f, rect *r) {
/* returns true if the area described by *r is all zeros in *f */
if (! isSubRect(f->r, r)) {
return 0;
}
int x, y;
for (y = r->y1; y <= r->y2; y++) {
for (x = r->x1; x <= r->x2; x++) {
if (f->f[y][x]) {
/* nonzero */
return 0;
}
}
}
return 1;
}
int getFreeCount(field *f) {
int x, y, count = 0;
for (y = 0; y < f->ydim; y++) {
for (x = 0; x < f->xdim; x++) {
if (f->f[y][x] == 0) {
count++;
}
}
}
return count;
}
rect *getBiggestFree(field *f) {
int x1, y1, x2, y2, size, maxsize;
rect *tmp = createRect(0, 0, 1, 1);
rect *max = createRect(0, 0, 1, 1);
maxsize = 0;
for (x1 = 0; x1 < f->xdim; x1++) {
for (y1 = 0; y1 < f->ydim; y1++) {
for (x2 = x1; x2 < f->xdim; x2++) {
for (y2 = y1; y2 < f->ydim; y2++) {
setRect(tmp, x1, y1, x2, y2);
size = getArea(tmp);
if (size > maxsize && isFree(f, tmp)) {
maxsize = size;
copyRect(tmp, max); /* max now describes the same rect as tmp */
}
}
}
}
}
delRect(tmp);
if (getArea(max) == 1) {
delRect(max);
return NULL;
}
return max;
}
stack getAllFree(field *f, rect *r) {
/* gets free areas within *f where *r fits (actually not all, see optimization below) */
int x1, y1, x2, y2;
stack s = newStack(2);
rect *tmp = createRect(0, 0, 1, 1);
int xmin, ymin;
xmin = r->x2 - r->x1;
ymin = r->y2 - r->y1;
for (x1 = 0; x1 < f->xdim; x1++) {
for (y1 = 0; y1 < f->ydim; y1++) {
for (x2 = x1; x2 < f->xdim; x2++) {
for (y2 = y1; y2 < f->ydim; y2++) {
if (
((x1 == 0) || f->f[y1][x1-1]) /* only allow "continuous" placement */
&& ((y1 == 0) || f->f[y1-1][x1])
) {
setRect(tmp, x1, y1, x2, y2);
if (areCongruent(tmp, r) && isFree(f, tmp)) {
push(s, tmp);
tmp = createRect(0, 0, 1, 1);
}
}
}
}
}
}
push(s, NULL);
return s;
}
void printField(field *f) {
int x, y;
for (y = 0; y < f->ydim; y++) {
for (x = 0; x < f->xdim; x++) {
printf("%c ", f->f[y][x] ? f->f[y][x] + 64 : '.');
}
printf("\n");
}
printf("-- \n");
}
int isInRect(rect *r, int x, int y) {
if (x >= r->x1 && x <= r->x2 && y >= r->y1 && y <= r->y2) {
return 1;
}
return 0;
}
int isSubRect(rect *r, rect *sub) {
if (isInRect(r, sub->x1, sub->y1) && isInRect(r, sub->x2, sub->y2)) {
return 1;
}
return 0;
}
int areCongruent(rect *r, rect *s) {
int rxsize, rysize, sxsize, sysize, success = 0;
rxsize = r->x2 - r->x1;
rysize = r->y2 - r->y1;
sxsize = s->x2 - s->x1;
sysize = s->y2 - s->y1;
if (
(rxsize == sxsize && rysize == sysize)
|| (rxsize == sysize && rysize == sxsize)
) {
success = 1;
}
return success;
}
Der Header für den "typenlosen" void pointer Stack:
$ time ./rect 13 14 3 5
A A A D D D G G G I I I .
A A A D D D G G G I I I .
A A A D D D G G G I I I .
A A A D D D G G G I I I .
A A A D D D G G G I I I .
B B B E E E H H H J J J .
B B B E E E H H H J J J .
B B B E E E H H H J J J .
B B B E E E H H H J J J .
B B B E E E H H H J J J .
C C C C C F F F F F . . .
C C C C C F F F F F . . .
C C C C C F F F F F . . .
. . . . . . . . . . . . .
--
A A A C C C C C H H H H H
A A A C C C C C H H H H H
A A A C C C C C H H H H H
A A A D D D D D I I I I I
A A A D D D D D I I I I I
B B B D D D D D I I I I I
B B B E E E E E J J J J J
B B B E E E E E J J J J J
B B B E E E E E J J J J J
B B B F F F G G G K K K .
. . . F F F G G G K K K .
. . . F F F G G G K K K .
. . . F F F G G G K K K .
. . . F F F G G G K K K .
--
A A A C C C C C H H H H H
A A A C C C C C H H H H H
A A A C C C C C H H H H H
A A A D D D D D I I I I I
A A A D D D D D I I I I I
B B B D D D D D I I I I I
B B B E E E E E J J J J J
B B B E E E E E J J J J J
B B B E E E E E J J J J J
B B B F F F G G G K K K .
. . . F F F G G G K K K .
. . . F F F G G G K K K .
. . . F F F G G G K K K .
. . . F F F G G G K K K .
--
real 0m47.021s
user 0m44.408s
sys 0m0.008s
Da er Zwischenlösungen ausgibt wird die Lösung bereits wenige Millisekunden nach dem Start ausgegeben, allerdings braucht er dann noch ne Weile um festzustellen, ob das wirklich die Beste Lösung ist.. (Btw kann man (wenn man denn will) am Anfang der fillFieldRec() Funktion einfach ein printField(f); hinpacken und sich die Ausgabe dann animiert mittles desselben Perl-Skripts vom Springerproblemthread ausgeben lassen.)