Страницы

Поиск по вопросам

Показаны сообщения с ярлыком рекурсия. Показать все сообщения
Показаны сообщения с ярлыком рекурсия. Показать все сообщения

среда, 15 апреля 2020 г.

Рекурсия и ее проблемы

#рекурсия #алгоритм

                    
К каким проблемам может привести использование рекурсии и как их избежать?    


Ответы

Ответ 1



Оверхед на вызовы. Немного время, а главное -- стек. При большой глубине рекурсии быстро расходуется. Метод борьбы -- использование хвостовой рекурсии, которую нормальные компиляторы трансформируют в итерации. Добавлено. На тему "заменить рекурсию стеком". Рекурсивная функция вычисления факториала. fact.c++ int fact0(int k, int n) { if (n > 1) return fact0(k*n, n-1); else return k; } fact0.s .file "fact0.c++" .text .p2align 4,,15 .globl _Z5fact0ii .type _Z5fact0ii, @function _Z5fact0ii: .LFB0: .cfi_startproc .cfi_personality 0x0,__gxx_personality_v0 pushl %ebp .cfi_def_cfa_offset 8 movl %esp, %ebp .cfi_offset 5, -8 .cfi_def_cfa_register 5 movl 12(%ebp), %edx movl 8(%ebp), %eax cmpl $1, %edx jg .L5 jmp .L2 .p2align 4,,7 .p2align 3 .L7: movl %ecx, %edx .L5: leal -1(%edx), %ecx imull %edx, %eax cmpl $1, %ecx jne .L7 .L2: popl %ebp .p2align 4,,2 ret .cfi_endproc .LFE0: .size _Z5fact0ii, .-_Z5fact0ii .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits Что тут заменять и на что?

Ответ 2



Слишком глубокую рекурсию всегда можно заменить используя стэк.

четверг, 9 апреля 2020 г.

Как рекурсивно создать двунаправленный список зная только количество элементов, Java

#java #рекурсия #структуры_данных #списки #связывание_данных

                    
Есть DDS DoubleNode, которая является двунаправленным списком:

public class DoubleNode {
public int value;
public DoubleNode prev;
public DoubleNode next;

public DoubleNode(int value, DoubleNode prev, DoubleNode next) {
    this.value = value;
    this.prev = prev;
    this.next = next;
}


а вот что я пытаюсь сделать, так это построить такую Node, зная только количество
элементов в ней (значения элементов выбираются случайно). При всем при этом построить
хочу рекурсивно, сваял вот такой метод:

    public static DoubleNode doubleNodeGenRec (int length) {
    return (length == 0) ? null : new DoubleNode((int)(Math.random()*10), null, doubleNodeGenRnd(--length));
}


как новому элементу передать ссылку на DoubleNode prev;?

выходит, что строится Node в одну сторону, т.к. в методе туда передается просто null,
и в результате выходит просто связанный список. чем заменить этот null?
    


Ответы

Ответ 1



public DoubleNode(int value) { this.value = value; } public static DoubleNode doubleNodeGen(int length) { return doubleNodeGenRec(length, null); } private static DoubleNode doubleNodeGenRec(int length, DoubleNode prev) { if (length == 0) { return null; } int value = (int) (Math.random() * 10); DoubleNode node = new DoubleNode(value); node.prev = prev; node.next = doubleNodeGenRec(length - 1, node); return node; }

вторник, 31 марта 2020 г.

Рекурсивная функция для поиска пути в List-ах

#c_sharp #рекурсия #списки #словари


Есть n-ое количество элементов в словаре:

Dictionary> dict = new Dictionary>();


В List лежат ключи связующих словарей. То есть, например, словарь с ключом '1' связывается
со словарями с ключами 1, 6, 8, 10. А словарь с ключом 90 связывается со словарями
с ключами 8, 3, 92, 138. 

dict[1] = new List(){ 87, 6, 8, 10 };
dict[90] = new List() { 8, 3, 92, 138 };


Задача - найти кратчайший путь между словарями dict[1] и dict[90] по их связям. То
есть сначала сравнить все элементы в списке словаря с ключом 1 с 90. 
Потом сравнить по-очереди каждый элемент списка в словаре с ключом '87' с 90, потом
каждый элемент списка в словаре с ключом '6' и так далее, пока не будет достигнуто
совпадение с 90.
Полученный путь нужно запомнить в отдельный List, в котором будут ключи словарей в пути.

Пример:

dict[1] = new List(){ 3, 5 };
dict[3] = new List(){ 1, 8 };
dict[5] = new List(){ 1 };
dict[8] = new List(){ 3, 90 };
dict[90] = new List() { 8 };


Нужно найти кратчайшее расстояние от dict[1] до dict[90]. Сначала сравниваем все
элементы в dict[1] с 90. Элемента 90 в списке словаря '1' нет. Ищем дальше: берём первый
элемент списка словаря с ключом '1' (3) и сравниваем элементы словаря dict[3] с 90.
Элемента 90 в списке словаря '3' нет. Берём следующий элемент списка словаря '1' (5).
Там сравниваем всего один элемент и тоже нет совпадений. 

Теперь углубляемся и рассматриваем элементы словаря '3' как отдельные словари. Т.е.
проверяем dict[1] - ничего нет. Проверяем dict[8] - совпадение. Поиск закончен!

При этом нужно запоминать путь. В качестве вывода использовать

List output = new List();


В который по порядку будут записываться ключи словарей в пути. В примере он будет
состоять из элементов:

output.Add(1);
output.Add(3);
output.Add(8);
output.Add(90);


Очевидно, нужно использовать рекурсию для прохождения по всем элементам списков.
    


Ответы

Ответ 1



Рекурсию можно использовать, но не нужно. Задача сводится к обходу графа в ширину. Где словари - это вершины графа. А ключи доступа - ребра. Замечу, что это орграф. Извиняйте за введение собственной системы ввода данных, писал, чтобы проверить работоспособность. А раз написал, не грех и поделиться. На вход поступает 2 числа, N - кол-во ключей и M - кол-во записей о этих ключах. В следующих M строках описываются ключи в формате: 1-e число - ключ, последующие через пробел - ключи других словарей. Далее программа требует два числа X и Y. От какого словаря требуется найти путь к другому. Программа выводит кратчайший путь, если таковой существует, иначе "Don't exist". string[] Inp = Console.ReadLine().Split(); // Заносим данные ==>> int N = Convert.ToInt32(Inp[0]), M = Convert.ToInt32(Inp[1]); Dictionary> dict = new Dictionary>(); for (int i = 0; i < N; i++) dict[i] = new List(); for (int i = 0; i < M; i++) { Inp = Console.ReadLine().Split(); int Temp = Convert.ToInt32(Inp[0]) - 1; for (int j = 1; j < Inp.Length; j++) { dict[Temp].Add(Convert.ToInt32(Inp[j]) - 1); } } Inp = Console.ReadLine().Split(); int X = Convert.ToInt32(Inp[0]) - 1, Y = Convert.ToInt32(Inp[1]) - 1; // <<== Все еще заносим Queue Work = new Queue(); // BFS работает через очередь. bool[] Mark = new bool[N]; // Массив, в котором будем помечать посещенные словари int[] Log = new int[N]; // Потребуется для вывода найденного пути bool Exist = false; // Существует ли наш путь вообще Work.Enqueue(X); // см. Алгоритм BFS (обход в глубину) Mark[X] = true; Log[X] = -1; while (Work.Count > 0) { int v = Work.Dequeue(); for (int i = 0; i < dict[v].Count; i++) { if (!Mark[dict[v][i]]) { if (dict[v][i] == Y) Exist = true; Work.Enqueue(dict[v][i]); Mark[dict[v][i]] = true; Log[dict[v][i]] = v; } } } if (Exist) { List output = new List(); // Восстанавливаем путь for (int v = Y; v != -1; v = Log[v]) output.Add(v); output.Reverse(); for (int i = 0; i < output.Count; i++) Console.Write(output[i] + 1 + " "); Console.WriteLine(); } else Console.WriteLine("Don't exist"); Вот пример работы: Входные данные: 100 6 1 2 3 4 5 5 2 3 1 45 42 13 54 42 13 3 45 42 54 1 13 Выходные данные: 1 3 45 13 Надеюсь, помог.

понедельник, 30 марта 2020 г.

Условие выхода из рекурсивной функции (считаем косинус через ряд Маклорена)

#python #алгоритм #математика #рекурсия


Отвечая на этот вопрос, я попытался реализовать рекурсивную функцию для вычисления
косинуса при помощи ряда Маклорена до указанной точности (эпсилон).

Формула:



Никак не могу придумать, как реализовать проверку точности для завершения/выхода
из рекурсии.

Вот набросок функции без проверки точности:

import math

def cosine_rec(x, i=0, err=10**9, eps=1e-5):
    if i < 1:
        return 1
    # как проверить текущую ошибку ???
    res = (-1)**i * (x**(2*i) / math.factorial(2*i))
    return res + cosine_rec(x, i+1, err=err, eps=eps)


PS с итеративным алгоритмом все просто, поэтому нерекурсивные решения прошу не предлагать.
    


Ответы

Ответ 1



def cosine_rec(x, i=0, err=10**9, eps=1e-5): if i < 0: return 1 res = x**(2*i) / math.factorial(2*i) if res <= eps: return (-1)**i * res return (-1)**i * res + cosine_rec(x, i+1, err=err, eps=eps)

вторник, 25 февраля 2020 г.

Рекурсивный вывод числа наоборот

#java #рекурсия


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

public class MyClass {
    public static int consoleInputFirstNumber() {
        Scanner scan = new Scanner(System. in );
        System.out.print("Enter number: ");
        if (scan.hasNextInt()) {
            return scan.nextInt();
        } else {
            System.out.println("Entered not number. Try again.");
            return consoleInputFirstNumber();
        }
    }

    public static void numberReverseOrder(int number) {
        if (number == 0) {
            return;
        } else {
            System.out.print(number % 10);
            numberReverseOrder(number / 10);
        }
    }

    public static void consoleOutput(int result) {
        System.out.println(result);
    }

    public static void main(String[] args) {
        int input = consoleInputFirstNumber();
        numberReverseOrder(input);
    }
}


Кто знает, что нужно что бы решить мою проблему дайте подсказку или напишите решение.
Заранее спасибо.
    


Ответы

Ответ 1



Вообще-то ваша программа вроде как работает. Она только для 0 ничего не выводит, поэтому непонятно, чего вы хотите. Но вот для разнообразия моё решение. Проще такую задачу решать, преобразовав число в строку и сведя её к выводу перевёрнутой строки. Кроме того я бы разделил преобразование строки к перевёрнутой и собственно вывод. Вот как вашу задачу можно решить рекурсивно: import java.util.Scanner; public class RecursiveReverse { public static String reverse(String str) { return str.isEmpty() ? "" : reverse(str.substring(1)) + str.charAt(0); } public static void main(String[] args) { try (Scanner scan = new Scanner(System.in)) { System.out.print("Enter number: "); System.out.println("Reversed: " + reverse(String.valueOf(scan.nextInt()))); } } }

Ответ 2



Вот мой простой ответ: static int reverse(int num){ return num<10 ?num:Integer.parseInt(String.valueOf(num%10)+reverse(num/10)); }

Ответ 3



Вариант без использования строки. Класс IntRef нужен для имитации передачи параметра по ссылке. public class RecursiveReverse { private static class IntRef { int value; public IntRef( int value ) { this.value = value; } } public static int reverse( int arg ) { IntRef result = new IntRef( 0 ); reverseRec( arg, 1, new IntRef( 0 ), result); return result.value; } private static void reverseRec( int arg, int curDepth, IntRef maxDepth, IntRef result ) { if ( arg < 10 ) { maxDepth.value = curDepth; result.value = arg; } else { reverseRec( arg/10, curDepth * 10, maxDepth, result ); result.value = result.value + (arg % 10) * maxDepth.value / curDepth; } } public static void main( String[] args ) { int[] tests = new int[] { 0, 1, 5, 10, 11, 15, 100, 123, 555, 375, 1000, 1040, 12345 }; for ( int test : tests ) { System.out.printf( "%6d | %d%n", test, reverse( test ) ); } } }

понедельник, 24 февраля 2020 г.

Рекурсия и стек

#javascript #рекурсия #стек


В общем единственная тема, которую я никак не могу понять - это рекурсия. Написал
маленький код, но не понимаю как он работает до конца.

function power (base, exponent){
   if (exponent == 0)
       return 1;
   else
       return base * power(base, exponent -1);
}

console.log(power(2, 3));


Можете ли объяснить по шагам как тут всё устроено? 
    


Ответы

Ответ 1



Вызываем функцию с параметрами (2,3), т.е. функция имеет данный вид: function power (2, 3){ if (3 == 0) return 1; else return 2 * power(2, 3 - 1); } Далее функция(назовем ее внешняя) вызывает сама себя и создает новый уровень вложенности( назовем клон 1 ), (PS внешняя не может завершиться пока не завершатся все внутренние(клоны)) далее клон 1 вызывает сам себя и создает новый уровень вложенности( назовем клон 2 ), далее клон 2 вызывает сам себя и создает новый уровень вложенности( назовем клон 3 ), далее на клоне 3 срабатывает условие if, значит return 1 и клон 3 возвращает единицу клону 2. На клоне 2 срабатывает условие else т.е. 2 * 1( единицу вернул клон 3) , и результат(двойка) возвращается клону 1 На клоне 1 срабатывает условие else т.е. 2 * 2( двойку вернул клон 2 ), и результат(четверку) возвращает внешней функции На внешней функции срабатывает условие else т.е. 2 * 4( четверку вернул клон 1 ), и возвращает результат(восьмерку ) в консоль. Нарисовал схему работы данной функции:

Ответ 2



function power (base, exponent){ //Посчитаем силу базы (хз что это, но надо так надо :) //Если экспонента точно равна нулю, то мы знаем ответ! if (exponent == 0) { return 1; //и он равен нулю } else { //Но если экспонента нулю не равна, то ответ мы не знаем //Попробуем уменьшить экспоненту на 1 и посчитать снова exponent = exponent - 1; //результатом работы функции будет число, умноженное на результат работы ЭТОЙ же функции, но с экспонентой, уменьшенной на 1 return base * power(base, exponent); } //Если придет значение экспоненты меньше нуля, то все, браузеру хана :) }

воскресенье, 16 февраля 2020 г.

Рекурсия и стек

#javascript #рекурсия #стек


В общем единственная тема, которую я никак не могу понять - это рекурсия. Написал
маленький код, но не понимаю как он работает до конца.

function power (base, exponent){
   if (exponent == 0)
       return 1;
   else
       return base * power(base, exponent -1);
}

console.log(power(2, 3));


Можете ли объяснить по шагам как тут всё устроено? 
    


Ответы

Ответ 1



Вызываем функцию с параметрами (2,3), т.е. функция имеет данный вид: function power (2, 3){ if (3 == 0) return 1; else return 2 * power(2, 3 - 1); } Далее функция(назовем ее внешняя) вызывает сама себя и создает новый уровень вложенности( назовем клон 1 ), (PS внешняя не может завершиться пока не завершатся все внутренние(клоны)) далее клон 1 вызывает сам себя и создает новый уровень вложенности( назовем клон 2 ), далее клон 2 вызывает сам себя и создает новый уровень вложенности( назовем клон 3 ), далее на клоне 3 срабатывает условие if, значит return 1 и клон 3 возвращает единицу клону 2. На клоне 2 срабатывает условие else т.е. 2 * 1( единицу вернул клон 3) , и результат(двойка) возвращается клону 1 На клоне 1 срабатывает условие else т.е. 2 * 2( двойку вернул клон 2 ), и результат(четверку) возвращает внешней функции На внешней функции срабатывает условие else т.е. 2 * 4( четверку вернул клон 1 ), и возвращает результат(восьмерку ) в консоль. Нарисовал схему работы данной функции:

Ответ 2



function power (base, exponent){ //Посчитаем силу базы (хз что это, но надо так надо :) //Если экспонента точно равна нулю, то мы знаем ответ! if (exponent == 0) { return 1; //и он равен нулю } else { //Но если экспонента нулю не равна, то ответ мы не знаем //Попробуем уменьшить экспоненту на 1 и посчитать снова exponent = exponent - 1; //результатом работы функции будет число, умноженное на результат работы ЭТОЙ же функции, но с экспонентой, уменьшенной на 1 return base * power(base, exponent); } //Если придет значение экспоненты меньше нуля, то все, браузеру хана :) }

пятница, 14 февраля 2020 г.

JAVA: Проверка входных данных, Рекурсия, Исключения

#java #исключения #рекурсия #ввод


Такая проблемка возникла:

Нужно чтобы пользователь вводил число.
Если число не int (например буква), то должно выбрасываться исключение. 
А я как программист должен сделать так, чтобы программа не завершалась и не "пропускала"
пользователя дальше, пока он не введёт корректное значение.

Пробовал через рекурсию. Но тк в стеке творится неведомая **** (на скрине), думаю
этот способ не лучший. 

Посоветуйте что сделать

При корректировке моего кода, учитывайте 1 и 2 задание лабораторной (на скрине)




    


Ответы

Ответ 1



Одно из решений - цикл while. boolean check = true; int fN; while(check) { try { Scanner sc = new Scanner(System.in); check = false; //ставим false, что бы не вводить больше данные System.out.println(...); fN = sc.nextInt(); } catch(Exception ex) { System.out.println(...); check = true; //если вышло исключение, ставим обратно true, что бы опять вводить данные } }

Ответ 2



Нормальный способ. Просто разделите вывод сообщения об ошибке и рекурсивный вызов функции небольшой задержкой, типа Thread.sleep(20);

Ответ 3



Может как-то так попробовать: public static void main(String[] args) throws Exception { System.out.println(InputFirstNumber()); } static int InputFirstNumber() throws IOException { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) { try { while (true) { System.out.println("Input first number!"); return Integer.parseInt(bufferedReader.readLine()); } } catch (NumberFormatException ex) { System.err.println("\tError!"); System.err.println("\tIncorrect data!"); return InputFirstNumber(); } } }

Ответ 4



Решил таким способом (скрин) В итоге от try и catch пришлось отказаться. Хотя я думаю если добавить Thread.sleep(20); (как советовал тут один человек) То мой изначальный код с рекурсией, будет работать правильно Всем спасибо за помощь) Поставил лайки, но тк у меня рейтинг ниже 15, они кажется не отобразятся......

воскресенье, 9 февраля 2020 г.

RecursionError: maximum recursion depth exceeded - как преодолеть?

#python #алгоритм #рекурсия


Есть задача в вопросе; есть ее решение, где по изяществу Python "победил" C#, но
вот у же на х=512 вылетает RecursionError:
что посоветуете, отцы? Как преодолеть, если хочется посчитать для х=1024 скажем?

import functools 
from math import sqrt

@functools.lru_cache()
def f(x):
    if x <=1: return 0
    return 1 + min([ f(m + x // m - 2)  for m in range(1,int(sqrt(x))+1) if x%m ==0])


добавлю после первого ответа:   

Кто предложит как изменить алгоритм?

Кстати. Добавил количество рекурсии до 5000. Получается странная фигня. для 1024
считает, делая 3600 итераций, а для 2048 - "выбивает" интерпретатор на 1000+ рекурсии.
Вообще не пойму - в чем проблема....
    


Ответы

Ответ 1



Я python не знаю... Но на С++ решение этой задачи - вот в этом ответе: https://ru.stackoverflow.com/a/770528/195342 Как видите, на ideone решило миллиард за 0.04с без особого напряжения по стеку. Ключевые моменты: Перебор с отсечением Мы сразу отсекаем особо длинные рекурсии, которые, очевидно, не приведут к оптимальному решению. Начинаем со значений, близких к корню Такая вот эвристика - там цепочки самые короткие, и мы сразу найдем решение, которое позволит отсечь большое количество заведомо плохих решений и не лезть глубоко в рекурсии. Мемоизация Позволит не считать повторно то, что уже посчитано. Кстати, подозреваю, что она не сильно и нужна, но сохранилась с предыдущих попыток решить задачу. Возможно, имеет смысл заменить массив, все-таки с немалым размером, на map (словарь). Это уже надо проверять и экспериментировать, но увы, сегодня на это уже нет времени. Да, это не так красиво выглядит... Но зато решает с очень малой глубиной рекурсии и очень быстро. Еще раз извиняюсь за незнание python... Вроде у малого в школе на следующий год обещают его преподавать - ну, вот тогда придется изучить и исправиться :) Update Использовал вместо массива на миллиард байт просто map. На моей машине миллиард вместо 77ms стал считаться 104ms, но зато при этом потребовалось только 44675 элементов в map. Экономию памяти оцените сами :) При полном отказе от мемоизации не получается восстановить решение, но сама величина - 8 - для миллиарда находится за примерно 580ms.

Ответ 2



Судя по всему вы преодолели порог глубины рекурсии. exception RecursionError This exception is derived from RuntimeError. It is raised when the interpreter detects that the maximum recursion depth (see sys.getrecursionlimit()) is exceeded. New in version 3.5: Previously, a plain RuntimeError was raised. Ссылка на источник Решением вашей проблемы будет ручное указание глубины рекурсии вызовом метода sys.setrecursionlimit(limit): sys.setrecursionlimit(limit) Set the maximum depth of the Python interpreter stack to limit. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python. The highest possible limit is platform-dependent. A user may need to set the limit higher when they have a program that requires deep recursion and a platform that supports a higher limit. This should be done with care, because a too-high limit can lead to a crash. If the new limit is too low at the current recursion depth, a RecursionError exception is raised. Changed in version 3.5.1: A RecursionError exception is now raised if the new limit is too low at the current recursion depth. Ну и ссылка на источник.

Ответ 3



Я размещу код, предложенный @Harry на С++, переведенный мною на Python для сохранения "тематики" языка на странице. Решение принадлежит ему. В любом случае - как всегда решает на язык, а мозг Ж-) import numpy as np from math import sqrt MAXSIZE = 1000000000 m = np.zeros(MAXSIZE+1, dtype=np.int16) def f(x, level=0, curmin = MAXSIZE) -> int: if level > curmin: return -1 # Отсечение - нет смысла лезть вглубь # при наличии более короткого решения if x == 1: return 0 if m[x]: return m[x] # Сохраненное значение res = MAXSIZE; for i in range(int(sqrt(x))+1, 0, -1): if x%i: continue; k = f(i+x//i-2, level+1, res); if k < 0: continue; if k < res: res = k m[x] = res+1 return m[x] n = int(input("дай целое!")) print( n, ": ", f(n)) вывод подробностей решения: cur = m[n] while n>1: for i in range(1,int(sqrt(n))+1): if n%i: continue if m[i+n//i-2] == cur-1: n = i+n//i-2 cur -=1 print(i, end=' ') break print("") Разница между этим кодом и кодом приведенным в вопросе - такова. Он работает для числа 1 000 000 000, в то время как исходный код "вырубал" интерпретатор уже на 2048. Ручное кэширование оказалось надежнее встроенного в пайтон, и - важнее всего -отсечение неправильных вариантов заметно сократило время.

суббота, 8 февраля 2020 г.

Рекурсивный импорт в Python

#python #рекурсия


Просьба подробно объяснить мне глупому поведение циклического импорта из книги

Марка Лутца # Изучаем Python [4-ое издание]:

Часть 5. Модули >> Упражнение 7. Циклический импорт:

# recur1.py

X = 1

import recur2

Y = 2


# recur2.py

from recur1 import X

from recur1 import Y


В зависимости от порядка импорта каждого файла и прямого их запуска в интерактивной
оболочке. Итого = 4 ситуации.

Всю голову себе изломал, но никак не въеду.
    


Ответы

Ответ 1



recur1.py: print('x = 1, hi from recur1! We are in recur1 file') x = 1 print('import recur2... We are in recur1 file') import recur2 print('y = 2, hi from recur1! We are in recur1 file') y = 2 recur2.py: print('import x from recur1... We are in recur2 file') from recur1 import x print('import y from recur1... We are in recur2 file') from recur1 import y print('hi from recur2! We are in recur2 file') В ходе импортирования инструкции в файле выполняются сначала и до конца. Если выполним следующее: >>> import recur1 То получим ошибку: ImportError: cannot import name 'y' Это происходит по очень простой причине. В модуле recur1 сначала создается переменная x, а потом сразу импортируется recur2, в котором мы используем from. Таким образом, у нас будет доступ только к тем именам, которые уже были определены в этом модуле. То есть доступ только к x, y не будет доступен, так как к нему будет присвоено значение после импорта в recur1. from recur1 import y # а у еще нет! Лучше не использовать при рекурсивном импорте инструкцию from. При рекурсивном импорте интерпретатор не будет повторно выполнять инструкции. Это легко можно проверить: >>> import recur2 import x from recur1... We are in recur2 file x = 1, hi from recur1! We are in recur1 file import recur2... We are in recur1 file y = 2, hi from recur1! We are in recur1 file import y from recur1... We are in recur2 file hi from recur2! We are in recur2 file Ошибки не происходит, потому что при первом импорте мы получаем x, потом снова импортируем из recur1 recur2, повторного определения переменной х происходить не будет (см. выше), поэтому сразу получим y, и когда уже захотим импортнуть его из recur2, также повторно инструкции не будут выполняться. Идем дальше: >>> from sys import modules >>> 'recur1' in modules.keys() False >>> 'recur2' in modules.keys() False >>> import recur2 # вывод см. выше >>> 'recur2' in modules.keys() True >>> 'recur1' in modules.keys() True И когда после этого мы захотим сразу сделать: >>> import recur1 То ошибки в from recur1 import y не будет, так как y уже будет объявлен в recur1: >>> help(modules['recur1']) Вернет: NAME recur1 DATA x = 1 y = 2 Если же запустим python recur1.py Получим: x = 1, hi from recur1! We are in recur1 file import recur2... We are in recur1 file import x from recur1... We are in recur2 file x = 1, hi from recur1! We are in recur1 file import recur2... We are in recur1 file y = 2, hi from recur1! We are in recur1 file import y from recur1... We are in recur2 file hi from recur2! We are in recur2 file y = 2, hi from recur1! We are in recur1 file Как пишет Лутц: Когда модули запускаются как самостоятельные программы, они не импортируются, поэтому здесь возникает тот же эффект, как и при импортировании recur2 в интерактивной оболочке, – recur2 является первым импортируемым модулем. А при python recur2.py В выводе увидим: import x from recur1... We are in recur2 file x = 1, hi from recur1! We are in recur1 file import recur2... We are in recur1 file import x from recur1... We are in recur2 file import y from recur1... We are in recur2 file ImportError: cannot import name 'y' То есть мы еще не объявили в recur1 переменную y, поэтому ее и нет. Если допустил какие-то неточности, заранее прошу прощения. Ответ получился большим, мог и упустить что-то.

Ответ 2



Я аналогично предварительно экспериментировал с трассировкой, но объяснения Лутцом рекурсивного импорта порядком меня запутали. А потом всё встало на свои места: Создание объекта модуля инструкцией import происходит лишь единожды, далее - интерпретатор пропускает все последующие вызовы import этого модуля и занимается выполнением иных инструкций с дополнением пространства имён. Инструкция from является расширенной версией инструкции import с дополнительной операцией копирования имён импортируемого модуля в пространство имён импортирующего модуля. Кстати, решение Лутца Решение? Не используйте инструкцию from в операции рекурсивного импорта (в самом деле!). Интерпретатор не зациклится, если вы все-таки сделаете это, но ваша программа попадет в зависимость от порядка следования инструкций в модулях. ... Если от циклов не удается избавиться полностью, попробуйте отсрочить обращение к именам модуля, используя инструкции import и полные имена (вместо инструкции from), recur1.py print('x = 1, hi from recur1! We are in recur1 file') x = 1 print('import recur2... We are in recur1 file') import recur2 print('y = 2, hi from recur1! We are in recur1 file') y = 2 recur2.py print('import x from recur1... We are in recur2 file') import recur1 # Вместо from recur1 import x, y print('x =', recur1.x) print('y =', recur1.y) не актуально: >>> import recur1 x = 1, hi from recur1! We are in recur1 file import recur2... We are in recur1 file import x from recur1... We are in recur2 file x = 1 AttributeError: 'module' object has no attribute 'y' Спасибо за развёрнутый ответ! Отблагодарить репутация на сайте не позволяет. (Голос за requires 15 reputation.)

среда, 5 февраля 2020 г.

Рекурсия вызваная из базового класса

#cpp #классы #функции #рекурсия


Все мы знаем обычную рекурсию, например:

функция имя(){
    условие_завершения;
    имя();
}


Понятно что эта функция будет существовать в памяти в нескольких экземплярах до завершения
всей цепочки вызовов.

Встретил вот такой код:

class SimpleDelegate : public QStyledItemDelegate {
public:
    void paint(QPainter*                   pPainter,
               const QStyleOptionViewItem& option,
               const QModelIndex&          index
              ) const
......
условие_выхода
......
    {
        QStyledItemDelegate::paint(pPainter, option, index); // рекурсия ?
    }
};


Вроде бы тоже рекурсия но как она работает? То ли копия базового объекта создается
то ли просто метод дублируется для текущего объекта, то ли.. , в общем не понятно.
    


Ответы

Ответ 1



В данном определении функции-члена класса рекурсии нет class SimpleDelegate : public QStyledItemDelegate { public: void paint(QPainter* pPainter, const QStyleOptionViewItem& option, const QModelIndex& index ) const ...... условие_выхода ...... { QStyledItemDelegate::paint(pPainter, option, index); // рекурсия ? } }; Имеются две различные функции. Одна функция - это SimpleDelegate::paint , другая функция - это QStyledItemDelegate::paint. Первая функция в области определения производного класса скрывает объявление второй функции с тем же самым именем в базовом классе. Поэтому если вы хотите из функции производного класса вызвать одноименную функцию базового класса, то вам нужно указывать ее квалифицированное имя, что и делается в приведенном примере. Рассмотрите данную демонстрационную программу #include #include int main() { struct A { void f(const char *) const { std::cout << "Hello"; } void f(char) const { std::cout << ' '; } }; struct B : A { void f(const std::string &s ) const { A::f(nullptr); A::f(' '); std::cout << s << std::endl; } }; B().f(std::string("perfect")); return 0; } Ее вывод на консоль Hello perfect Здесь функция, объявленная в производном классе B скрывает одноименные функции, объявленные в базовом классе A. Поэтому чтобы обратиться к этим функциям базового класса в области определения производного класса, следует использовать квалифицированные имена функций базового класса. Другой альтернативный подход при условии, что нет неоднозначности в перегрузке функций, это использовать using объявления в производном классе. Например, struct B : A { using A::f; void f(const std::string &s ) const { f(nullptr); f(' '); std::cout << s << std::endl; } }; Однако, если сигнатуры одноименных функций в базовом и производном классах совпадают, то, чтобы исключить неоднозначность, придется явно указывать квалифицированные имена функций для базового класса.

Ответ 2



QStyledItemDelegate::paint(pPainter, option, index); вызовет метод paint у класса QStyledItemDelegate, который является базовым, относительно SimpleDelegate. Это не является рекурсивным вызовом. Понятно что эта функция будет существовать в памяти в нескольких экземплярах у функций нет экземпляров.

воскресенье, 2 февраля 2020 г.

В каких случаях рекурсия более эффективна итерации ? (java)

#java #циклы #рекурсия


В каких случаях рекурсия более эффективна итерации ? (java)
    


Ответы

Ответ 1



Однозначно, когда работаете с древовидными структурами и вы не знаете сколько вложенностей. И без рекурсии вам никак по нему не пройти.

Ответ 2



Все, что можно решить рекурсией, можно решить итерацией. Но дополнительный минус рекурсии состоит в том, что при ее использовании аппаратный стек системы проваливается все глубже и для больших наборов данных это чревато падением приложения. Так что практического смысла рекурсия не имеет, только как учебная задача с целью более полного осознания возможностей программиста. Пользуйтесь циклом и Ваш стек никогда не провалится. А если ваши промежуточные данные надо укладывать в контейнер-стек, то его переполнение можно контролировать программно, в отличие от аппаратного стека системы.

суббота, 1 февраля 2020 г.

Алгоритм: все возможные целые из наборов от .. до

#рекурсия #комбинаторика #алгоритм #javascript


Вроде простая задача, но что-то лыжи не едут.

Дан массив диапазонов — пар мин. и макс. значений. Только целые числа.
Нужно найти все возможные целые, получаемые суммированием «допустимых» значений из
произвольного комплекта идущих подряд диапазонов, по одному из каждого участвующего.
Например, дано три диапазона:
[{"min":1,"max":2},{"min":3,"max":4},{"min":5,"max":6}]

Можно получить числа [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12]. 
На деле пар больше, значения веселее. Может, есть известный красивый алгоритм?
В общем смысл моего, далеко не оптимального, решения, такой (перебор): 

распаковать пары min:max до массивов допустимых значений [[1,2],[3,4],[5,6]]
перебирать массивы слева направо;
для каждого варианты глубины вправо от 0 (только себя), до правого края;
перебрать все возможные комбинации значений между этими массивами, по одному зн.
из каждого. Суммировать, уникальные значения сохранить в результат.

Интересно ещё решить обратную задачу: даны опять эти пары, и число. Разложить его
в идущие подряд допустимые значения. 
Усложнённая условием про «подряд» Subset Sum Problem? Подскажите, как называются
алгоритмы для похожих задач?    


Ответы

Ответ 1



Складывая два интервала [a,b]+[c,d] получим итервал [a+c,b+d], если интервалов больше то будет [a1+a2+...+an,b1+b2+...+bn]. Таким образом мы избавляемся от лишних вычислений, в частности от рекурсии совсем. Нам достаточно знать для каждой комбинации интервалов сумму их минимумов и сумму их максимумов. Чтобы перебрать все комбинации интервалов будем двигать границы в списке интервалов. Т.е. сначала будем смотреть первый интервал, потом первый и второй, потом с первого по n-й, затем второй, со второго по третий и т.д. var a = [{"min":2,"max":3},{"min":2,"max":2},{"min":2,"max":2}]; var a_l = a.length; var res = []; for (var i=0;i

Ответ 2



вот такое решение не подойдет ? var ranges=[{"min":2,"max":3},{"min":1,"max":20},{"min":3,"max":5},{min: 2,max: 10}]; //var ranges=[{"min":2,"max":3},{"min":2,"max":2},{"min":2,"max":2}] function range2array(range) { var ret=[]; for (var i=range.min;i<=range.max;i++) ret.push(i); return ret; } function arrays_summ(array1,array2) { var ret=[]; if (array2===undefined) return array1; for (var i in array1) for (var n in array2) ret.push(array1[i]+array2[n]); return ret; } function process(ranges) { var lastarray; var result={}; for (var i in ranges) { lastarray=arrays_summ(range2array(ranges[i]),lastarray); for (var n in lastarray) result[lastarray[n]]=1; } return result; } function keys2array(hash){ var ret=[]; for (var i in hash) { ret.push(i); } return ret.sort(function(a,b) {return (a*1)>(b*1)?1:-1;}) } console.log(keys2array(process(ranges))); http://jsfiddle.net/oceog/mhT5Z/

Ответ 3



Пример здесь http://jsfiddle.net/8cdez/7/ Осталось только обернуть в красивую функцию, но само решение не очень красивое, плюсики ответу не ставьте, пожалуйста. Пусть будет. test=[{"min":2,"max":3},{"min":2,"max":2},{"min":2,"max":2}]; res=new Array(); for (var x=test[0].min; x<=test[0].max; x++) { for (var y=0; y<=test[1].max; y++ ? y!=0: y=test[1].min) { for (var z=0; z<=test[1].max; z++ ? z!=0: z=test[2].min) { res.push(x+y+z); } } }; res.push(test[1].min+test[2].min); res.push(test[1].max+test[2].max); res=_.unique(res.sort()); alert(res); Используется UnderScore (функция unique).

пятница, 31 января 2020 г.

Почему при сериализации получается рекурсия?

#c_sharp #wpf #json #рекурсия #serialize


Почему когда я хочу сериализовать в JSON какой нибудь контрол, то вылазит рекурсия?

JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(label1);

    


Ответы

Ответ 1



Потому что у контрола есть ссылка на родительский контрол, а у того список ссылок на детей - круг замкнулся. Можно написать свою обертку, у которой только те свойства, которые Вы хотите сохранить. public class ControlWrapper { private Control fControl; public ControlWrapper(Control aControl) { fControl = aControl; } public int Width { get { return fControl.Width; } } ... } JavaScriptSerializer serializer = new JavaScriptSerializer(); string json = serializer.Serialize(new ControlWrapper(label1));

Ответ 2



Потому что вы пытаетесь сериализировать несериализируемое: UI-контрол. Контролы не предназначены для сериализации, т. к. у них есть внутреннее состояние и внутренние привязки (например — подписки на события), которые невозможно сохранить при сериализации. Делайте правильно, сохраняйте модель, а не представление. Срезать углы не выйдет.

Ответ 3



Раз вы используете wpf, можно попробовать XAML-сериализацию.

среда, 29 января 2020 г.

Как работает сложение длинных целых в этом примере с рекурсией?

#javascript #рекурсия


Встретился такой код:

function res(a, b, t, c){
  if(a.length == 0 && b.length == 0 && !c)
    return t;
  var l = parseInt(a.pop() || '0') + parseInt(b.pop() || '0') + (c || 0);
  return res(a, b, l + (t || ""), l > 9? 1:0);
}

function add(a, b) {
  return res(a.toString().split(""), b.toString().split(""), "").toString();
}


Судя по всему, код реализует сложение «длинных» целых (функция res() – вспомогательная).
Но, похоже, делает это с ошибками. Например, для add(5,5) результат 110, вместо ожидаемого
10, хотя для add(2,3) получается правильный: 5.

Я не могу понять, как этот код работает, и где может быть ошибка. Объясните, пожалуйста,
доступно.
    


Ответы

Ответ 1



Здесь происходит поразрядное сложение. Таким способом можно складывать как угодно большие числа, не только ограниченные максимально допустимым значением, для js это 1.79E+308, поэтому это называется длинной арифметикой. Поскольку обыкновенные числа ограничены, результат выдаётся в виде строки. Для начала рассмотрим функцию add function add(a, b) { return res(a.toString().split(""), b.toString().split(""), "").toString(); } Предположительно она принимает числа, но может принимать и строку с цифрами. В ней происходит вот что. Сначала, если a не было строкой, оно превращается в строку. Затем при помощи функции split разбивается на массив из отдельных цифр. То же происходит со вторым слагаемым — b. Затем вызывается функция res, которая и будет вычислять наш ответ. Поскольку число может быть как угодно длинное, то ответ мы будем собирать по одной цифре, в виде строки. Начальное значение строки-ответа "", на каждом рекурсивном вызове res мы будем вычислять по одной цифре. Дальше вызывается функция res с начальными параметрами, запускает рекурсию. Теперь по функции res: данная функция как раз и производит сложение. Практически обыкновенное, привычное школьное сложение в столбик. Если немного переименовать параметры: function res(a, b, result, carry) { У нас слагаемые a и b, промежуточный результат в result, перенос из предыдущего разряда в carry. Если и левое, и правое слагаемое пустое, и нет переноса, то результат готов, его и возвращаем. if(a.length == 0 && b.length == 0 && !carry) return result; Вынесем сложные выражения в локальную переменную для простоты. Мы собираемся получить младшую цифру каждого из слагаемых, и убрать эту цифру из дальнейшего рассмотрения. Для этого мы пользуемся функцией pop, которая убирает последний элемент массива, отдаёт нам его. var left = parseInt(a.pop() || '0', 10); var right = parseInt(b.pop() || '0',10); То есть, для массива ['9', '8', '7'] функция pop() вернёт '7', а от массива останется лишь ['9', '8']. Если массив на самом деле был пустой, функция вернёт undefined. Тут-то нам и пригодится трюк с || '0': если результат был цифрой, она при этом не испортится, а вот undefined превратится в '0'! Полученную цифру-строку мы превратили в число при помощи parseInt. Дальше мы складываем левую и правую цифры, и не забываем добавить перенос: var l = left + right + (carry || 0); Если перенос не указан, при помощи того же трюка превращаем его в 0. Осталось вызвать функцию рекурсивно, чтобы она провела ту же операцию со старшими разрядами. Только нужно пересчитать новый перенос: return res(a, b, l + (result || ""), l > 9? 1:0); } Но, как правильно заметил @Ni55aN, данная функция не всегда корректно работает. Нужно немного изменить ее. Добавить основание, например 10. Дело в том, что мы складываем по одной цифре. Но мы можем, по идее, и складывать большими кусками. Это была бы оптимизация. Затем, у нас баг: мы к результату добавляем часть, не «откусив» от неё перенесённый старший разряд. Это неправильно, надо убирать лишнее. Для случая двух слагаемых перенос не больше 1, но если мы захотим обобщить код на случай большего числа слагаемых, точное значение нужно будет вычислять. Напишем точное вычисление «на вырост». function res(a, b, result, carry, base) { if(a.length == 0 && b.length == 0 && !carry) return result; //берем младшие разряды var left = parseInt(a.pop() || '0', 10); var right = parseInt(b.pop() || '0', 10); //складываем и добавляем перебор с прошлой итерации var l = left + right + (carry || 0); //вызываем для следующих разрядов, правильно вычисляя добавленную цифру и цифру переноса return res(a, b, l % base + (result || ""), Math.floor(l/base), base); } function add(a, b) { return res(a.toString().split(""), b.toString().split(""), "","",10).toString(); } Ещё одна мелочь: мы накапливаем результат в параметре, чтобы можно было вызвать функцию рекурсивно в конце самой функции. Это так называемая хвостовая рекурсия, её многие компиляторы умеют эффективно обрабатывать, не занимая стек. Пример: $(function() { $('#left,#right').change(function(){ var left = $('#left').val().trim(); var right = $('#right').val().trim(); console.log(left,right,isNaN(left),isNaN(right)); if(isNaN(left) || isNaN(right)) return; $('#sum').html(add(left,right)); }); function res(a, b, result, carry, base) { if (a.length == 0 && b.length == 0 && !carry) return result; //берем младшие разряды var left = parseInt(a.pop() || '0', 10); var right = parseInt(b.pop() || '0', 10); //складываем и добавляем перебор с прошлой итерации var l = left + right + (carry || 0); //вызываем для следующих разрядов return res(a, b, l % base + (result || ""), Math.floor(l / base), base); } function add(a, b) { return res(a.toString().split(""), b.toString().split(""), "", "", 10).toString(); } }); left:
right:
sum:

вторник, 28 января 2020 г.

Создание ascii-art при помощи рекурсии

#python #рекурсия


Необходимо написать рекурсивную функцию, которая выводит на экран ASCII-art трапецию
следующего вида, в зависимости от числа n. Пример приведен для n = 4.

Теперь суть вопроса: по какой логике должна работать подобная рекурсивная функция?
У меня была мысль печатать определенно кол-во пробелов и звездочек, и передавать измененный
аргумент дальше по рекурсии, но что должно являться условием выхода из рекурсии, и
какое кол-во пробелов ставить я не знаю. 

       * * 
     * * * * 
   * * * * * * 
 * * * * * * * * 

    


Ответы

Ответ 1



def draw(n, indent=0): if n == 0: return draw(n-1, indent+1) print("{}{}".format(' ' * 2 * indent, ' '.join('*' * 2 * n)))

Ответ 2



Если совсем примитивно, то у Вас трапеция состоит из двух равносторонних треугольников, стороны которых соответствуют n: >>> def f(n): ... e = 0 ... for i in range(n): ... e += 1 ... print(' '*(n-e) + ' * *'*e + ' '*(n-e)) ... >>> f(4) * * * * * * * * * * * * * * * * * * * *

Ответ 3



Попытка в хвостовую рекурсию: def draw(n, i=0): width = 2 * (i + n) stars_count = 2 * i + 2 indent = (width - stars_count) // 2 print(' ' * indent + ' *' * stars_count) if n <= 1: return draw(n - 1, i + 1)

В чем польза рекурсии?

#cpp #алгоритм #рекурсия


Читал что "рекурсия обычно замедляет работу программы и расходует лишнюю память".
Это во всех случаях? И какая еще от нее есть польза кроме как компактной записи кода?
И в каких случаях она необходима?
    


Ответы

Ответ 1



Бытует мнение, что рекурсия является очень выразительным|естественным|няшечка|etc средством для реализации некоторых действий. Рекурсия общего вида: Замедляет, т.к. много ( в т.ч. бесконечно ) вызовов функций. Расходует память, т.к. каждый вызов функции хранит данные в стеке Есть ещё, т.н., хвостовая рекурсия. ( ИМХО - не рекурсия вовсе, а просто сахар для цикла ): Суть в том, что результат вызова итерации хвостовой рекурсии - не обрабатывается предыдущим шагом, и копилятор/интерпритатор может заменить работу с такой функцией - на цикл, который лишён недостатков указанных выше. 1) Пример хвостовой рекурсии ( JS, факториал ): function fact( step, res ){ if ( step < 2 ) return res; else return fact( step - 1, res * step ); } fact_10 = fact( 10, 1 ); 2) Тот-же смысл, но обычная рекурсия: function fact( step ){ if ( step < 2 ) return 1; else return step * fact( step - 1 ); } fact_10 = fact( 10 ); Разница, как уже указывал: Результат вызова никак не используется ( просто проброс на уровень выше ) Результат вызова перед возвращением подвергается некой операции. Вариант 1 легко ( алгоритм простой ) оптимизируется до такого кода: function fact( num ){ var res = 1; while( num > 1 ){ res *= num; num--; } return res; } Либо совсем кратко ( но в asm - тоже самое ): function fact( num ){ var res = 1; while( num > 1 ) res *= num--; return res; }

Ответ 2



Некоторые задачи (например обход дерева, перебор вариантов и т.д.) достаточно просто (я бы даже сказал, элегантно) решаются с помощью рекурсии. Те же задачи могут решаться с помощью цикла, но с бОльшими усилиями со стороны программиста и примерно с такими же затратами памяти на запоминание предыдущих состояний. Если компилятор языка программирования не поддерживает оптимизацию хвостовых рекурсий, то хвостовую рекурсию лучше преобразовать в цикл вручную.

воскресенье, 26 января 2020 г.

Найти максимум одномерного массива c помощью рекурсии C++

#cpp #рекурсия


Не знаю как сделать поиск максимального элемента массива с помощью рекурсии. Есть идеи?

#include  

using namespace std;
const int n = 5;
float a[n + 1];
int fmax(float a[], int nach, int kon); 

int main() {
    int i, k; for(i = 1; i <= n; i++) {
        cout << "\n введи a[" << i << "] ";
        cin >> a[i];
    } for(i = 1; i <= n; i++) {
        cout << " a[" << i << "]=" << a[i];
    }
    cout << "\n";
    cout << fmax(a[5], 0, 4); return(0);
}
int fmax(float a[], int nach, int kon)
{
    int k, kmax;
    float max; 
    kmax = nach;
    max = a[nach]; 
    for(k = nach; k <= kon; k++)
    {
        if(a[k] > max)
        {
            max = a[k]; kmax = k;
        }
    } 
    return kmax;
}

    


Ответы

Ответ 1



Как и для других рекурсивных функций, достаточно реализовать простейший случай и вызвать эту же функцию с меньшей задачей: template ForwardIt max_element(ForwardIt first, ForwardIt last, ForwardIt largest) { if (first == last) // no more elements to compare return largest; if (*largest < *first) // compare with the first element largest = first; ++first; return max_element(first, last, largest); // compare the rest } если слово template не ясно, то чтобы не отвлекаться (это не важно для понимания рекурсии), можно просто заменить ForwardIt на float*. Простейшим случаем здесь является пустой массив (first == last), в этом случае функция просто возвращает largest аргумент. Чтобы уменьшить размер задачи, можно отбросить первый элемент first—обновив largest, если необходимо—и вызвать функцию рекурсивно c остатками ввода, чтобы завершить решение задачи. Для удобства использования, можно определить функцию с двумя параметрами, передавfirst в качестве начального значения для largest—это работает и для пустых массивов, в этом случае возвращается значение равное last: template ForwardIt max_element(ForwardIt first, ForwardIt last) { return max_element(first, last, first); } Пример использования: #include int main() { float a[] = {1, -2, 3, 0.5}; std::cout << *max_element(a, a + sizeof(a) / sizeof(*a)) << '\n'; } Чтобы запустить: $ g++ max_recursive.cc && ./a.out 3 В данном случае max_element() является так называемой tail-recursive функцией—рекурсивный вызов является хвостовым (последним) в функции и может не потреблять стек. Некоторые компиляторы умеют автоматически преобразовывать подобный код в циклы, например, gcc -O2 для ForwardIt=int* может сгенерировать вот такой ассемблер: # first : %rdi # last : %rsi # largest: %rdx # result : %rax max_element(int*, int*, int*): cmpq %rsi, %rdi # x = cmp(first, last) movq %rdx, %rax # result = largest je .L2 # if(first == last) return result // if(!x) .L4: # do { movl (%rdi), %ecx # y = *first cmpl %ecx, (%rax) # z = cmp(*result, y) cmovl %rdi, %rax # if (*result < y) result = first //if(z<0) addq $4, %rdi # ++first cmpq %rdi, %rsi # x = cmp(last, first) jne .L4 # } while (last != first) // while(x) .L2: rep ret # return result // rep is amd // brancher bug workaround Абсолютно такой же код получается из итеративной версии: int* max_element(int* first, int* last, int* largest) { for ( ; first != last; ++first) if (*largest < *first) largest = first; return largest; }

Ответ 2



Можно, например, вот так: #include #include #include int max(const std::vector::const_iterator& begin, std::vector::const_iterator& end, int curentMax) { if(begin == end) return curentMax; if(*begin > curentMax) return max(begin + 1, end, *begin); return max(begin + 1, end, curentMax); } int main() { std::vector array{1, 55, 17, 77, 88, 13, 45, 72, 11}; std::cout << max(array.сbegin(), array.сend(), std::numeric_limits::min()) << "\n"; } Есть массив,— вектор, в котором находится некоторый набор данных. Я в функцию max передаю итераторы(можно считать указатели) на начало и конец(на одну позицию за концом) массива. Базовым случаем рекурсии будет случай, когда элементы массива кончились, т.е. начало и конец, переданные в аргументах, совпадают. В рекурсивном шаге мы проверяем, является ли элемент, который находится в начала переданного промежутка большим, по отношению к уже найденному максимальному, если да, то используем его, если нет, то используем ранее найденный max.

Ответ 3



Вот еще вариант для массива (не вектора), с делением такового пополам (быстрее от этого, конечно, он не работает :)) // Максимальный элемент в массиве array с индексами [start,stop) int maxel(int * array, int start, int stop) { if (start == stop-1) return array[start]; int mid = (start + stop)/2; int m1 = maxel(array,start,mid), m2 = maxel(array,mid,stop); return (m1 > m2) ? m1 : m2; } int main(int argc, const char * argv[]) { int x[] = { 1,2,15,2,41,18,-4,2 }; cout << maxel(x,0,sizeof(x)/sizeof(x[0])); }

Ответ 4



Как рекурсию всегда можно представить итерацией, так и наоборот. В своем примере поиска максимума Вы используя итерацию перебираете все элементы массива, соответственно простейшим будет вот такой естественный "однострочник" с рекурсией, возвращающий максимум: float rfmax (float *a, int n, float cmax) { return n > 0 ? rfmax(a + 1, --n, cmax > *a ? cmax : *a) : cmax; } который на каждом следующем шаге рекурсии сдвигает начало массива на следующий элемент и уменьшает количество элементов в нем на один. Вызывать можно вот так: int n = ... float a[n]; ... printf("max: %f\n", rfmax(a + 1, n - 1, a[0])); Однако, в Вашем примере несколько другая процедура, которая перебирает элементы некоторого диапазона массива и возвращает индекс максимального в нем. Здесь по всем шагам рекурсии нам надо вместе с текущим максимумом тащить еще и его индекс, который в конце-концов и возвращается как результат. Пожалуй, однострочник для нее выглядит несколько вычурно, соответственно, рекурсивный вариант, максимально похожий на Ваш пример, можно написать так: int ixfmax (float a[], int start, int end, float cmax, int icmax) { if (start > end) return icmax; if (cmax < a[start]) cmax = a[icmax = start]; return ixfmax(a, start + 1, end, cmax, icmax); } и вот так использовать его для поиска максимального элемента во всем массиве int imax = ixfmax(a, 1, n - 1, a[0], 0); printf("max: a[%d] = %f\n", imax, a[imax]); Довольно естественно, что если при поиске мы сначала полагаем, что начальный элемент это максимум, то поиск проводим со следующего элемента. В самом деле, зачем его сравнивать с самим собой?

четверг, 23 января 2020 г.

Рекурсивный запрос в postgresql

#база_данных #postgresql #рекурсия


Есть такая таблица   

Мне надо получить иерархию в виде 

Сделал рекурсивный запрос 

with  recursive rec(scopename , parentname ) as (
   select scopename , parentname  from _geoimage_scope 
   union all
   select rec.scopename , rec.parentname  from rec,_geoimage_scope where rec.scopename
= _geoimage_scope.parentname
)

select * from rec where parentname is null 


Но не выходит,  получается бесконечная рекурсия. Кто может подсказать, помочь? Пользуюсь
 postgresql
    


Ответы

Ответ 1



У Вас получился бесконечный запрос потому, что (представим пример на одной из записей): На первом шаге Вы отобрали запись scopename='utilitiesconsumption' И теперь в rec находится эта одна запись. На втором шаге Вы берете полученную запись и делаете запрос вида (примерно) select rec.scopename, rec.parentname from ( select cast('utilitiesconsumption' as text) scopename, null parentname ) rec, _geoimage_scope where rec.scopename = _geoimage_scope.parentname Который возвращает Вам точно такую же запись, что сейчас есть в rec, только, в Вашем случае, четыре раза. Получается, что теперь у Вас в rec находится 5 одинаковых записей. На третьем шаге Вы берете следующую запись из rec и, т.к. она у Вас точно такая же как и первая запись, то полностью повторяется шаг 2. В результате чего количество записей в rec становится уже 9 (и все они одинаковые) и так до бесконечности. Вам должен помочь такой запрос: with recursive rec(scopename, parentname ) as ( select scopename, parentname from _geoimage_scope where parentname is null union all select _geoimage_scope.scopename, _geoimage_scope.parentname from _geoimage_scope, rec where _geoimage_scope.parentname = rec.scopename ) select * from rec Тестовый пример можно посмотреть здесь

среда, 22 января 2020 г.

Правильно ли я понял рекурсию?

#javascript #рекурсия


Неделю боролся с осознанием рекурсии, изучаю учебник Learn.Javascript. Пока в голове
такая каша:


Рекурсия это вызов функции внутри себя же, но уже с другим параметром
В решении конкретной задачи параметр должен уменьшаться или увеличиваться
Параметр будет уменьшаться/увеличиваться до тех пор пока не запнётся на условии
Условие на котором запнётся рекурсия должно вернуть простейшее решение уже не требующее
очередного вызова функции
Все предыдущие функции ждут решения следующей, в том числе простейшего решения. Место
где они ждут называется Стек (с англ. Stack == стог, стопка)
Когда рекурсия запнётся на простейшем решении, результаты начнут возвращаться в обратном
порядке. Из конца в начало, с глубины на поверхность. Функции которые получили решение
будут уходить из Стека начиная с последней. Останется только довольная первая, которая
всю эту заварушку устроила.


Следует различать три вещи: 


Рекурсия это общее явление, оно встречается не только в программировании - в природе,
рисовании и т.д....
Чтобы использовать это явление для решения задач, оно должно возвращать нам результат.
Нужно объяснить компьютеру с каким шагом двигаться, где остановиться и что возвращать.
А вот Стек это уже чисто программное явление, т.к. это ячейки компьютерной памяти.
На каждом шаге компьютер будет записывать всё что происходит чтобы не заблудиться именно
в Стек.


Можно такой пример привести: В армии генерал отправил майора за спиртом, майор отправил
капитана за спиртом, капитан отправил лейтенанта за спиртом, лейтенант отправил сержанта
за спиртом, сержант отправил рядового за спиртом. 
Рядовому некого отправлять, это очевидно - он пошёл и нашёл спирт. Затем спирт пошёл
обратно по цепочке рядовой - сержант - лейтенант - капитан - майор - генерал. Генерал
доволен, всё чётко.

Друзья, сейчас очень нужна ваша критика - дополните или поправьте чтобы лучше понять
рекурсию.
    


Ответы

Ответ 1



Практически все из написанного верно, но: Рекурсия это вызов функции внутри себя же, но уже с другим параметром Не с другим параметром, а с другим значением параметра или параметров. В решении конкретной задачи параметр должен уменьшаться или увеличиваться Не совсем верно. Параметр или параметры могут быть просто неким признаком, в общем всем тем, что можно проверить в условии, которое сможет разветвиться на два и более решений, при этом одно или несколько из этих решений могут как вести на очередной виток рекурсии, так и на выход из нее. Условие на котором запнётся рекурсия должно вернуть простейшее решение уже не требующее очередного вызова функции Решение не то, что бы простейшее, оно просто решение, некий результат некоего сравнения.