Устранение рекурсивного хвостового вызова для декомпозиции дерева квадрантов в Python

Можно ли с помощью Python устранить множественные рекурсивные хвостовые вызовы (преобразовать в итерацию) в реализации дерева квадрантов, которая назначает точки (X, Y) поддоменам (также известным как конечные узлы)? Вот не очень псевдокод:

def decompose(inX, inY, xmin, xmax, ymin, ymax, maxP): 
#Paramters: Lists of X and Y coordinates, boundaries, max. #points per subdomain

if len(inX) <= maxP: #base case
            --write out list of points and boundaries--
        else:
            xB = (xmin + xmax) / 2 #calculate new boundaries
            yB = (ymin + ymax) / 2

        for  x, y in zip(inX, inY): #assign points to subdomains (a.k.a. children-nodes)
            if x < xB:
                if y < yB:
                    R1X.append(x), R1Y.append(y)
                else:
                    R2X.append(x), R2Y.append(y)
            else:
                if y < yB:
                    R3X.append(x), R3Y.append(y)
                else:
                    R4X.append(x), R4Y.append(y) 

        decompose(R1X, R1Y, xmin, xB, ymin, yB, maxP) #recursive tail calls (which I want to eliminate)
        decompose(R2X, R2Y, xmin, xB, yB, ymax, maxP)
        decompose(R3X, R3Y, xB, xmax, ymin, yB, maxP)
        decompose(R4X, R4Y, xB, xmax, yB, ymax, maxP)

Мне известны методы устранения хвостовых вызовов, но примеры, которые я видел, не демонстрируют шаблона разветвления, присущего рекурсивной декомпозиции дерева квадрантов (несколько независимых рекурсивных хвостовых вызовов). Моя мотивация состоит в том, чтобы написать код, который не исчерпывает стек, если я использую его на миллионах точек, которые могут демонстрировать существенную кластеризацию, что приводит к очень глубокой рекурсии. Кроме того, я хочу включить временное измерение и реализовать пространственные буферы, чтобы точки можно было назначать нескольким подобластям (мне это нужно для дальнейшей обработки). Из-за большого количества настроек существующие реализации quadtree могут оказаться непригодными. Если вам надоел еще один новичок, задающий тот же старый вопрос, продолжайте и хорошего дня, спасибо за чтение. В противном случае вы могли бы либо указать мне на ресурсы, которые помогут мне решить проблему, либо опубликовать потенциальное решение. Я был бы рад, если бы вы это сделали.


person Happy Tree    schedule 30.06.2015    source источник
comment
Возможно, вы захотите взглянуть на y-комбинатор. С его помощью можно построить бесконечную рекурсию. Обычно вы должны иметь возможность переформатировать код, чтобы вместо этого использовать forloops.   -  person Loïc Faure-Lacroix    schedule 30.06.2015
comment
Это то, с чем я еще не сталкивался. Я посмотрю на это. Спасибо, что поделился.   -  person Happy Tree    schedule 30.06.2015
comment
@ Loïc Faure-Lacroix Y-комбинатор не устраняет хвостовые вызовы. Это позволяет выполнять рекурсивные вызовы из лямбда-функции, но при этом размер стека Python будет увеличиваться.   -  person Thomas Baruchel    schedule 04.11.2015


Ответы (1)


В вашем примере, я думаю, вы немного неправильно понимаете хвостовую рекурсию. Есть только один конец, и это будет строка:

decompose(R4X, R4Y, xB, xmax, yB, ymax, maxP)

Таким образом, чтобы открыть код этой хвостовой рекурсии, измените if len(inX)... на while len(inX)... и измените последнее разложение на

inX, inY, ... = R4x, R4Y ...
person rocky    schedule 30.06.2015
comment
Спасибо, что развеяли мою путаницу по поводу хвостовых вызовов. Не уверен, что понимаю остальное из того, что вы пишете. То, что вы предлагаете, оставит мне 3 рекурсивных вызова функций и последнюю строку в вашем ответе. Три вызова по-прежнему исчерпали бы стек, а последний вызов декомпозиции имеет дело только с одним квадрантом. Если вы хотите избавиться от трех вызовов, у меня все еще остается проблема, связанная с тем, что три других квадранта не учитываются. - person Happy Tree; 30.06.2015
comment
Дональд Кнут сказал, что преждевременная оптимизация — корень зла. Не путайте тот факт, что могут быть миллионы очков, с размером стека, имеющим миллионы очков. В целом у него будет log4 (миллион), что значительно меньше. Поэтому, если вы на самом деле не запускали этот код и не исчерпали стек, сначала попробуйте простой подход. - person rocky; 30.06.2015
comment
В любом случае, насколько я знаю, cpython не выполняет оптимизацию хвостовых вызовов. Так что это не имело бы большого значения, даже если бы в коде был только один хвостовой вызов. Здесь: stackoverflow.com/questions/13591970/ - person Loïc Faure-Lacroix; 30.06.2015
comment
Ответ показывает, как вы открываете код хвостовой рекурсии. - person rocky; 30.06.2015