Kreisverpackung in einem Rechteck

8

Ihre Aufgabe ist es, ein Programm zu schreiben, das den größten Radius findet, den N Kreise haben können und der dennoch in ein Rechteck passt, das X mal Y Pixel groß ist. (ähnlich wie in diesem Wikipedia-Artikel ) Ihr Programm muss den größtmöglichen Radius und die optimale Position dieser N Kreise finden, damit

  1. Keine zwei Kreise überlappen sich
  2. Alle Kreise passen in das Rechteck.

Ihr Programm sollte dies dann auf der Konsole drucken:

Highest possible radius: <some_number>
Circle 1 Focus: (x, y)
Circle 2 Focus: (x, y)
...
Circle N-1 Focus: (x, y)
Circle N Focus: (x, y) 

(Natürlich müssen Sie some_number durch den Radius ersetzen, den Ihr Programm berechnet, N durch die Anzahl der Kreise, die Sie am Ende verwenden, und x und y durch die tatsächlichen Koordinaten des Kreises.)

Zuletzt sollte ein Bild mit diesen gezeichneten Kreisen erstellt und gespeichert werden.

Regeln:

  1. Dieses Programm muss mit einer beliebigen Größe eines Rechtecks ​​und einer beliebigen Anzahl von Kreisen ausgeführt werden und dennoch eine gültige Antwort erhalten. Sie können Befehlszeilenargumente, Benutzereingaben, fest codierte Variablen oder alles andere verwenden, was Sie möchten.

  2. Wenn sich ein Kreis überlappt oder nicht vollständig in das Feld passt, ist Ihre Einreichung ungültig.

  3. Jeder Kreis muss den gleichen Radius haben.

  4. Um dies vernünftig und machbar zu machen, müssen alle Zahlen auf die 2. Dezimalstelle genau sein.

Dies ist Code Golf, also gewinnt das kürzeste Programm (Stand 28.10.2014).

James
quelle
6
Zu Ihrer Information, dies optimal zu tun, ist nicht gerade einfach. "Lösungen (nicht unbedingt optimal) wurden für jeweils N ≤ 10.000 berechnet."
Calvins Hobbys
1
Würden Sie eine Lösung zulassen, die alle Möglichkeiten durchläuft? Damit meine ich jeden möglichen Mittelpunkt und Radius für jeden Kreis bis zur Maschinengenauigkeit, nicht jede qualitative Konfiguration.
xnor
4
Ganz zu schweigen davon, dass das Programm, wenn es mit einer beliebigen Anzahl von Kreisen ausgeführt werden muss, für die Beispielausgabe einen englischen Namen für eine beliebige Anzahl erstellen muss.
Peter Taylor
3
Meinten Sie "auf die zweite Dezimalstelle"? Weil das Hundertstel weder sehr vernünftig noch sehr machbar erscheint. ;) Wenn du nur so wenig Präzision willst, können wir genauso gut alles in ganzen Zahlen machen. Wir können es sowieso nicht genauer zeichnen, wenn 1 Einheit 1 Pixel entspricht.
Martin Ender
3
Diese Herausforderung ist zumindest 2014 buchstäblich unmöglich. Die optimale Lösung für dieses Problem ist der Mathematik nicht bekannt, daher ist es völlig unmöglich zu überprüfen, ob die Antworten gültig sind oder nicht. Ich denke, es könnte möglich sein, dies mit ein wenig Nachdenken zu speichern, z. B. indem Sie sagen, dass Ihr Programm ungültig wird, wenn jemand eine bessere Lösung für Werte von x, y und N findet. Oder indem Sie es einfach mit der Punktzahl herausfordern auf dem größten Radius, den Ihr Programm finden kann. Aber wie es jetzt geschrieben steht, gibt es keine Möglichkeit, gültige Antworten zu erhalten.
Nathaniel

Antworten:

8

JavaScript 782 725 Zeichen

erster Beitrag, sei sanft!

Das Programm wird nun über die Wrapped-Funktion aufgerufen. ZB : (function(e,f,g){...})(100,200,10).

function C(e,f,g,c,a,d){if(0>g-a||g+a>e||0>c-a||c+a>f)return d;for(var b in d)if(Math.sqrt(Math.pow(d[b].x-g,2)+Math.pow(d[b].y-c,2))<2*a)return d;d.push({x:g,y:c});for(b=0;b<Math.PI;)XX=Math.cos(b)*a*2+g,YY=Math.sin(b)*a*2+c,d=C(e,f,XX,YY,a,d),b+=.01;return d}
(function(e,f,g){var c=e+f,a,d;for(a=[];a.length<g;)a=d=c,a=C(e,f,a,d,c,[]),c-=.01;console.log("Highest possible radius: "+Math.round(100*c)/100);e='<svg width="'+e+'" height="'+f+'"><rect width="'+e+'" height="'+f+'" style="fill:red" />';for(var b in a)console.log("Circle "+b+" Focus: ("+Math.round(100*a[b].x)/100+", "+Math.round(100*a[b].y)/100+")"),e+='<circle cx="'+a[b].x+'" cy="'+a[b].y+'" r="'+c+'" fill="blue" />';console.log(e+"</svg>")})(400,300,13);


Test 1

(function(e,f,g){...})(200,200,4)

Highest possible radius: 49.96
Circle 1 Focus: (49.97, 49.97) 
Circle 2 Focus: (149.91, 49.97) 
Circle 3 Focus: (149.99, 149.91) 
Circle 4 Focus: (50.05, 149.91) 
<svg width="200" height="200"><rect width="200" height="200" style="fill:blue;" /><circle cx="49.97000000021743" cy="49.97000000021743" r="49.960000000217434" fill="white" /><circle cx="149.9100000006523" cy="49.97000000021743" r="49.960000000217434" fill="white" /><circle cx="149.98958489212322" cy="149.90996831285986" r="49.960000000217434" fill="white" /><circle cx="50.04958489168835" cy="149.90996831285986" r="49.960000000217434" fill="white" /></svg>

Natürlich würden wir erwarten, dass der Radius genau 50 beträgt, aber aus Gründen, die in den Kommentaren der Frage erörtert wurden, konnte ich dies nicht vernünftigerweise erreichen. Die SVG sieht so aus ...

200 200 4


Test 2

(function(e,f,g){...})(100,400,14)

Highest possible radius: 26.55
Circle 1 Focus: (26.56, 26.56) 
Circle 2 Focus: (79.68, 26.56) 
Circle 3 Focus: (132.8, 26.56) 
Circle 4 Focus: (185.92, 26.56) 
Circle 5 Focus: (239.04, 26.56) 
Circle 6 Focus: (292.16, 26.56) 
Circle 7 Focus: (345.28, 26.56) 
Circle 8 Focus: (372.63, 72.1) 
Circle 9 Focus: (319.52, 73.25) 
Circle 10 Focus: (265.47, 72.64) 
Circle 11 Focus: (212.35, 73.25) 
Circle 12 Focus: (159.23, 72.64) 
Circle 13 Focus: (106.11, 73.25) 
Circle 14 Focus: (52.99, 72.64) 
<svg width="400" height="100"><rect width="400" height="100" style="fill:blue;" /><circle cx="26.560000000311106" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="79.68000000093332" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="132.80000000155553" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="185.92000000217774" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="239.04000000279996" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="292.16000000342217" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="345.2800000040444" cy="26.560000000311106" r="26.550000000311105" fill="white" /><circle cx="372.6271770491687" cy="72.09972230654316" r="26.550000000311105" fill="white" /><circle cx="319.5195599732359" cy="73.24663493712801" r="26.550000000311105" fill="white" /><circle cx="265.47097406711805" cy="72.63752174440503" r="26.550000000311105" fill="white" /><circle cx="212.35454341475625" cy="73.25330971030218" r="26.550000000311105" fill="white" /><circle cx="159.23097406587362" cy="72.63752174440503" r="26.550000000311105" fill="white" /><circle cx="106.11454341351183" cy="73.25330971030218" r="26.550000000311105" fill="white" /><circle cx="52.99097406462921" cy="72.63752174440503" r="26.550000000311105" fill="white" /></svg>

Und die SVG sieht so aus ...

Geben Sie hier die Bildbeschreibung ein


Test 3

(function(e,f,g){...})(400,400,3)

Highest possible radius: 101.68
Circle 1 Focus: (101.69, 101.69) 
Circle 2 Focus: (298.23, 153.98) 
Circle 3 Focus: (154.13, 298.19) 
<svg width="400" height="400"><rect width="400" height="400" style="fill:blue;" /><circle cx="101.69000000059772" cy="101.69000000059772" r="101.68000000059772" fill="white" /><circle cx="298.2343937547503" cy="153.97504264473156" r="101.68000000059772" fill="white" /><circle cx="154.13153961740014" cy="298.19269546075066" r="101.68000000059772" fill="white" /></svg>

Und die SVG sieht so aus ...

Geben Sie hier die Bildbeschreibung ein

Sie sind nicht alle hübsch.


Wie es funktioniert

Ungolfed Code unten. Dieses Programm hat zwei Annahmen:

  1. Ein Kreis wird immer in der Ecke sein. Dies scheint eine ziemlich sichere Wette zu sein.
  2. Jeder Kreis berührt immer einen anderen Kreis. Ich verstehe nicht, warum sie es nicht wären.

Das Programm berechnet zunächst einen großen Radius anhand der Kastenabmessungen. Es wird dann versucht, einen Kreis in die Ecke der Box zu passen. Wenn dieser Kreis passt, verlängert er eine Durchmesserlinie von diesem Kreis und versucht, am Ende der Linie einen Kreis zu erstellen. Wenn der neue Kreis passt, wird eine weitere Linie vom neuen Kreis verlängert. Wenn es nicht passt, schwingt die Linie um 360 Grad und sucht nach offenen Stellen. Wenn sich das Feld füllt, bevor die gewünschte Anzahl von Kreisen erstellt wurde, wird der Radius verringert und alles beginnt von vorne.


Ungolfed Code (Ausschnitt)

// this functions attempts to build a circle
// at the given coords. If it works, it will
// spawn additional circles.
function C(x, y, X, Y, r, cc){

	// if this circle does not fit in the rectangle, BAIL
	if(X-r < 0 || X+r > x || Y-r < 0 || Y+r > y)
		return cc;
	
	// if this circle is too close to another circle, BAIL
	for(var c in cc){
		if( Math.sqrt(Math.pow(cc[c].x - X, 2) + Math.pow(cc[c].y - Y, 2)) < (r*2) )
			return cc;
	}
	
	// checks passed so lets call this circle valid and add it to stack
	cc.push({"x": X, "y": Y});
	
	// now rotate to try to find additional spots for circles
	var a = 0; // radian for rotation
	while(a < Math.PI){
		XX = Math.cos(a)*r*2 + X;
		YY = Math.sin(a)*r*2 + Y;
		cc = C(x, y, XX, YY, r, cc);
		a += .01; // CHANGE FOR MORE OR LESS PRECISION
	}
	
	return cc;
}

// this function slowly reduces the radius
// and checks for correct solutions
// also prints svg graphic code

(function B(x, y, n){

	var r = x + y; // pick a big radius
	var X, Y; // these are the coords of the current circle. golf by combining this with `var r..`?
	var cc = []; // array of coordinates of the circles
	
	// while we cant fit n circles, reduce the radius and try again
	while(cc.length < n){
		X = Y = r;
		cc = C(x, y, X, Y, r, []);
		r-=.01; // CHANGE FOR MORE OR LESS PRECISION
	}
	
	console.log('Highest possible radius: ' + Math.round(r*100)/100);
	var s = '<svg width="' + x + '" height="' + y + '"><rect width="' + x + '" height="' + y + '" style="fill:red" />';
	for(var c in cc){
		console.log('Circle '+c+' Focus: (' + Math.round(cc[c].x*100)/100 + ', ' + Math.round(cc[c].y*100)/100 + ')');
		s += '<circle cx="' + cc[c].x + '" cy="' + cc[c].y + '" r="' + r + '" fill="blue" />';
	}
	s += '</svg>';
	console.log(s);
	document.write(s);
})(150, 150, 5);

Rip Leeb
quelle