Empaque circular en un rectángulo

8

Su tarea es escribir un programa que encuentre el radio más grande que puedan tener N círculos y que aún quepa dentro de un rectángulo que es X por Y píxeles grandes. (similar a este artículo de Wikipedia ) Su programa debe encontrar el radio más grande posible y la posición óptima de estos N círculos para que

  1. No hay dos círculos superpuestos
  2. Todos los círculos caben dentro del rectángulo.

Su programa debería imprimir esto en la consola:

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) 

(Obviamente, debe reemplazar some_number con el radio que calcula su programa, N con el número de círculos que termina utilizando, y x e y con las coordenadas reales del círculo)

Por último, debe crear y guardar una imagen con estos círculos dibujados.

Reglas:

  1. Este programa debe ejecutarse con cualquier tamaño de rectángulo y cualquier número de círculos y aún así obtener una respuesta válida. Puede usar argumentos de línea de comando, entrada del usuario, variables codificadas o cualquier otra cosa que desee.

  2. Si algún círculo se superpone o no cabe completamente dentro del cuadro, su envío no es válido.

  3. Cada círculo debe tener el mismo radio.

  4. Solo para hacer esto razonable y factible, todos los números deben ser precisos al segundo decimal.

Este es el código de golf, por lo que gana el programa más corto (a partir del 28/10/2014).

James
fuente
66
Para su información, hacer esto de manera óptima no es exactamente fácil. "Se han calculado soluciones (no necesariamente óptimas) por cada N≤10,000".
Hobbies de Calvin
1
¿Permitirías una solución que recorra todas las posibilidades? Con lo cual quiero decir cada centro y radio posibles para cada círculo hasta la precisión de la máquina, no todas las configuraciones cualitativas.
xnor
44
Sin mencionar que si el programa debe ejecutarse con cualquier número de círculos, la salida de muestra requerirá que pueda producir un nombre en inglés para cualquier número.
Peter Taylor
3
¿Quiso decir "al segundo decimal"? Porque la centésima no parece muy razonable ni muy factible. ;) Aunque, si solo quieres tan poca precisión, también podríamos hacer todo en enteros. De todos modos, no podemos trazarlo con mayor precisión si 1 unidad corresponde a 1 píxel.
Martin Ender
3
Este desafío es literalmente imposible, al menos en 2014. La solución óptima para este problema no es conocida por las matemáticas, por lo que será completamente imposible verificar si las respuestas son válidas o no. Creo que es posible guardar esto con un poco de reflexión, por ejemplo, diciendo que su programa se vuelve inválido si alguien encuentra una mejor solución para cualquier valor de x, y y N. O simplemente haciendo un desafío de código , con la puntuación basada en el radio más grande que pueda encontrar su programa. Pero como está escrito ahora, no hay forma de que pueda obtener respuestas válidas.
Nathaniel

Respuestas:

8

JavaScript 782 725 caracteres

primer post, se gentil!

El programa ahora se llama a través de la función envuelta. Por ejemplo: (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);


Prueba 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>

Obviamente, esperaríamos que el radio fuera exactamente 50, pero por las razones discutidas en los comentarios de la pregunta, no pude hacerlo razonablemente. El SVG se ve así ...

200 200 4


Prueba 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>

Y el SVG se ve así ...

ingrese la descripción de la imagen aquí


Prueba 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>

Y el SVG se ve así ...

ingrese la descripción de la imagen aquí

No todos son bonitos.


Cómo funciona

Código sin golf a continuación. Este programa tiene dos supuestos:

  1. Un círculo siempre estará en la esquina. Esto parece una apuesta bastante segura.
  2. Cada círculo siempre estará tocando otro círculo. No veo por qué no lo serían.

El programa comienza calculando un radio grande basado en las dimensiones de la caja. Luego trata de encajar un círculo en la esquina de la caja. Si ese círculo encaja, extiende una línea de diámetro desde ese círculo e intenta crear un círculo al final de la línea. Si el nuevo círculo encaja, se extenderá otra línea desde el nuevo círculo. Si no encaja, la línea oscilará 360 grados, buscando espacios abiertos. Si el cuadro se llena antes de que se cree el número deseado de círculos, el radio se reduce y todo comienza de nuevo.


Código sin Golf (fragmento)

// 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
fuente