Círculo de embalagem em um retângulo

8

Sua tarefa é escrever um programa que encontre o maior raio que N círculos possam ter e ainda caibam dentro de um retângulo com X por Y pixels de largura. (semelhante a este artigo da wikipedia ) Seu programa deve encontrar o maior raio possível e a posição ideal desses N círculos para que

  1. Não há dois círculos sobrepostos
  2. Todos os círculos se encaixam dentro do retângulo.

Seu programa deve imprimi-lo no console:

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, você deve substituir some_number pelo raio que o seu programa calcula, N pelo número de círculos que você acaba usando, e xey pelas coordenadas reais do círculo)

Por fim, ele deve criar e salvar uma imagem com esses círculos desenhados.

Regras:

  1. Este programa deve ser executado com qualquer tamanho de retângulo e qualquer número de círculos e ainda obter uma resposta válida. Você pode usar argumentos de linha de comando, entrada do usuário, variáveis ​​codificadas ou qualquer outra coisa que desejar.

  2. Se algum círculo se sobrepuser ou não couber totalmente dentro da caixa, seu envio será inválido.

  3. Cada círculo deve ter o mesmo raio.

  4. Apenas para tornar isso razoável e factível, todos os números devem ser precisos até a 2ª casa decimal.

Este é o código de golfe, pelo que o programa mais curto (a partir de 28/10/2014) vence.

James
fonte
6
FYI fazer isso da melhor maneira possível não é exatamente fácil. "Soluções (não necessariamente ótimas) foram calculadas para cada N≤10.000."
Hobbies de Calvin
1
Você permitiria uma solução que itere sobre todas as possibilidades? Com o que quero dizer todo centro e raio possível para cada círculo até a precisão da máquina, nem toda configuração qualitativa.
Xnor
4
Sem mencionar que, se o programa precisar ser executado com qualquer número de círculos, a saída de amostra exigiria que ele pudesse produzir um nome em inglês para qualquer número.
Peter Taylor
3
Você quis dizer "até a segunda casa decimal"? Porque o centésimo não parece muito razoável nem muito factível. ;) Embora, se você deseja apenas tão pouca precisão, podemos fazer tudo em números inteiros. Na verdade, não podemos plotá-lo com mais precisão, se uma unidade corresponde a 1 pixel.
Martin Ender
3
Esse desafio é literalmente impossível, pelo menos em 2014. A solução ideal para esse problema não é conhecida pela matemática, portanto será completamente impossível verificar se as respostas são válidas ou não. Eu acho que pode ser possível salvar isso com um pouco de reflexão, por exemplo, dizendo que seu programa se torna inválido se alguém encontrar uma solução melhor para quaisquer valores de x, ye N. Ou apenas tornando -o um desafio de código , com a pontuação baseada no maior raio que seu programa pode encontrar. Mas, como está escrito agora, não há como obter respostas válidas.
Nathaniel

Respostas:

8

Javascript 782 725 caracteres

primeiro post, seja gentil!

O programa agora é chamado através da função agrupada. Por exemplo: (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);


Teste 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, esperávamos que o raio fosse exatamente 50, mas, por razões discutidas nos comentários da pergunta, eu não poderia razoavelmente fazer isso acontecer. O SVG fica assim ...

200 200 4


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

E o SVG fica assim ...

insira a descrição da imagem aqui


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

E o SVG fica assim ...

insira a descrição da imagem aqui

Eles não são todos bonitos.


Como funciona

Código não destruído abaixo. Este programa tem duas suposições:

  1. Um círculo estará sempre no canto. Parece uma aposta bastante segura.
  2. Cada círculo sempre estará tocando em outro círculo. Não vejo por que eles não seriam.

O programa começa calculando um raio grande com base nas dimensões da caixa. Em seguida, tenta ajustar um círculo no canto da caixa. Se esse círculo se encaixar, ele estende uma linha de diâmetro a partir desse círculo e tenta criar um círculo no final da linha. Se o novo círculo se encaixar, outra linha será estendida a partir do novo círculo. Se não couber, a linha oscilará 360 graus, verificando se há espaços abertos. Se a caixa for preenchida antes da criação do número desejado de círculos, o raio será reduzido e tudo começará novamente.


Código Ungolfed (snippet)

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