[Community Puzzle] Cylinders

Thank you very much. I converted my recursive method in a simple iterative one. I still cannot manage to run, couse time out… Can i ask help for a revision of the code to someone ? Thanks ! :slight_smile:

Please describe your latest approach in detail, and state which tests and validators you’ve failed first.

Hi ! Thank you very much, i appriciate. I’ll try to be short and exaustive. I’ ve changed from recursive to iterative, and i cannot pass the 3rd and 4th test-case, although i’ve launched every test in my local environment and i give in output the correct answer always. So is “only” a timeout problem. I resolve 7-radii in 0.1/0.2 sec, 8-radii in 1/1.5 sec and 9-radii in 10/15 sec. The structure is this:
i take the distinct permutations: #(tried to use set(), but it appears to be slower than dict)

    dict_permutations = {p: None for p in permutations(list_radii)} #permutation() from itertools
    distinct_permut_list = list(dict_permutations)

loop into distinct permutations, calculate the length and if is less of the best_score i update it.
The lenght of a given permutation is calculated like this:
I manage 2 object in the method : a tuple (radius, x_coord_of_the_circle_center, left “prominence” of the circle (can ben less than 0 ), right “prominence” of the circle) and a list of this tuple. For example, only for the 1st circle i manualy append tuple(r,r,0,2r).
Then loop throught the list of radii in the permutation (starting from the second circle) and calculate the distance of its center from the center of the previous circle (math.sqrt((r_i_minus_1+r_i)**2 - (r_i-r_i_minus_1)**2)), this is my “provisory_x” (this is the Delta_X ofc, it has to be sum with the x- coordinate of the center of the previous circle). To accept or update it i check for the intersections, creating a list (filtered_list_pos_centers) containing only tuples relative to circles that actually intersect with the circle i’m positioning:

listBooleanIntersections = [check_If_Intersect(x[1],provisory_x, x[0], r_2) for x in x_coordinates_list ] #i tried to explicit the loop and actually calculate the intersections only if DeltaX < Sum of Radii to evitate obvius non-intersecting case for circles far-apart (suggestion from java_cofee), but changed almost nothing

filtered_list_pos_centers = [x for x, flag in zip(x_coordinates_list, listBooleanIntersections ) if flag]

my check_if_intersect function return (x_r2 - x_r1)**2 + (r1 - r2)**2 < (r1 + r2)**2 # little speed-up not doing sqrt()

Then if the filtered_list_pos_centers is empty then there are no intersections and the position is valid , so i accept the provisory_x and append its relative tuple to the list of tuple, if not i invoke the method delegate to move the circle until it has no more intersections with previous ( i think the speed problem is this method ofc, but cant understand why and how to improve it)

def non_recursive_intersections(listaTuple_with_intersect, radius):

stack = [listaTuple_with_intersect]
while stack:
	current_list = stack.pop()   
	if not current_list:
		continue
	last_circle = current_list[-1]
	new_dist = distance(last_circle[0], radius)
	New_Provisory_Position = last_circle[1] + new_dist
	if not any(check_If_Intersect(x[1], New_Provisory_Position, x[0], radius) for x in current_list[:-1] ):
		return New_Provisory_Position
    else:
        stack.append(current_list[:-1])
return New_Provisory_Position

At the end, the last new_provisroy_position will be the effective and permanent position. At the end of the loop trought the radii of the circles i simply return, as the lenght of the single permutation, the difference between max() among the right “prominence” and min() among left “prominence”; if its less then the best_score i update the best score.
Thank you very very much if u had the patience to read !! :slight_smile:

Are you really writing your code in these styles? You are torturing the CPU. Rethink how Algebra can help.

Am i missing something huge ? Yes they can be simplified rispectively to math.sqrt(4r_1r_2) and (x_2 - x_1) **2 < 4r1r2 , but it doesnt change much, looking at the structure of my code do u think the problems arise in the actual numeric calculation ? Or maybe i completely missunderstood your advice and cant see deeply in your suggestion ?

[Spoiler - Do not open the hint if you do not want to see too much]

hint


My draft notes

I was doing this, without any sqr function, without preset a value and then reset by recursion. The costly calculation is the sqrt().

If necessary, I can reduce the number of this calculation by NOT processing some combinations (see my previous comments).

Hi, thanks for suggestioni but maybe im still missing something. Also After algebraic manipulation (ive already implemented like in your hint), the sqrt for calcolate the distance must be done,isnt It? d= 2sqrt(ab). The check for intersections Is done only if deltaX < sum of radii (to evitate to calculate if surely they dont intersect couse are far apart). Also i’ve reduced the pool of permutations taking only the distinct, and in the method wich calculate the length i out a break condition if, at some point in the calculation, i already excedeed the best score. :frowning: there Is something that i cant see for decreasing the permutations list tò check ? Thanks!

If there is still a speed problem, you have to get data about your speed.

Add a timer and a counter to your code to count how many ms were used in this function, and how many iterations were done to get a correct answer. And how many ms totally were used to get the answer.

Serve yourself with these numbers to analyze the exact causes of timeout.

P.S. CG reminds me I am replying you too many times. I should stop now.

At last I have finished this one… This was horrible to get the time limit in Python and I am all the more frustrated as the Python codes of other players are very simple and elegant…

If you get the answers correctly and do not know at all how to optimize, here are some tips

Tip 1

Cache the square root function
Cache also the 2 * sqrt(x1) * sqrt(x2) function (this the distance d function described by java_coffee_cup in the image)

Tip 2

If you choose a position for the biggest cylinders, the left group and the right group cannot touch themselves. Thus, you can do the permutations independently.
Example : [1 2 3 4 5 6 7 8 9]
Now imagine that you position your 9 at position 3 (as you will do at one point, anyway)
There are 56 ways (3 among 8) to distribute the remaining cylinders
[1 2 3] 9 [4 5 6 7 8]
Finding the best first subset will require 6 permutations (3!)
Finding the best second subset will require 120 permutations (5!)
In the end, you will have explored 56 * (6 + 120) = 7056

By brute force, you would have examined 8! = 40320 permutations (ordering the 8 remaining)

This works because if you use the biggest cylinder as a pivot, the left group and the right group cannot touch themselves, so you can compute the lengths independently and sum them after.

Tip 3

Do not position your biggest cylinder after the first half. In the [1 2 3 4 5 6 7 8 9] example, try the 9 at positions 0, 1, 2, 3 and 4. It is enough because, if symmetrical positions yield the same lengths… So if there is a solution with the 9 after the middle, there is a solution with the 9 before the middle.

1 Like