| 
			| 
					
						| Olegator | 
								|  | «  : 09-11-2005 11:53 »  |  | 
 
 Можно ли решать программистскую задачу как в том анекдоте про математиков и физиков:Анекдот:
 Первая задача. Надо согреть воду в кастрюле. Дано: пустая кастрюля, кран. И дано решение этой задачи: Наливается вода из крана в кастрюлю. Кастрюля ставится на огонь и вода подогревается.
 Вторая задача. Надо согреть воду в кастрюле. Дано: наполненная водой кастрюля, кран. А решения нет.
 Что делает физик. Просто ставит кастрюлю на огонь и вода подогревается.
 Что делает математик. Выливает из кастрюли воду. И таким образом приводит начальные условия этой задачи к начальным условиям первой задачи, у которой решение уже существует. И дальше не думая, действует по решению первой задачи.
 
 Можно ли так же сделать в случае с рекурсией и итерацией.
 
 Т.е., допустим, для меня решить задачу рекурсивно проще, чем итеративно, но выходит расточительно по отношению к ресурсам компьютера. Я делаю рекурсивно, а потом переделываю итеративно. Не выйдет ли так проще.
 
 Хотя может это не очень удачный пример. Может кто-то этот принцип в других случаях использует.
 
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #1 : 09-11-2005 12:26 »  |  | 
 
 Olegator, опытный проектировщик тем и отличается от дилетанта, что подходит к задаче творчески.
 Во-первых, есть замечательная книга Дж. Джонса "Методы проектирования", посвященное философии проектирования как процесса (при этом не очень важно, что именно проектируется; как ни странно, имеются достаточно универсальные методы для решения широкого круга задач). Один из принципов, которым я часто следую, является "проектирование в трех временных плоскостях". Вот тебе пример: имеется задача, которую нужно было решить еще вчера. Ты:
 
 1. Быстренько набрасываешь решение, которое временно затыкает дыру.
 2. Выиграв время, спокойно решаешь задачу достаточно приемлемым путем.
 3. Параллельно обдумываешь будущее задачи, если она имеет будущее (например, стыковку с другими системами или расширение ее применимости).
 
 Книга весьма редкая, однако я встречал на букинистических инет-аукционах. Если обзаведешься, не пожалеешь.
 
 Во-вторых, в программировании действует своя разновидность закона Парето, согласно которой примерно 10% кода занимает примерно 90% времени выполнения программы. Отсюда следует, что далеко не все, что напимано неоптимально, непременно следует оптимизировать. Например, у тебя есть массив из 10 элементов, в котором ты ищешь один раз простым перебором. Ты можешь показать класс кодирования, реализовав его в виде ассоциативного массива с быстрым доступом, сбалансированного В-дерева и т.д. В результате окажется, что программа стала выполняться на 50 микросекунд быстрее при общем времени выполнения полчаса. То есть твое рабочее время, потраченное  на оптимизацию, было потеряно практически впустую.
 
 Я в своей группе практикую следующий подход:
 
 решить задачу наиболее простым способом;
 оценить качество решения;
 если оно приемлемо, приступить к следующей задаче;
 иначе при помощи трассировки и профилирования определить часть программы, выполнение которой занимает больше всего времени;
 разработать альтернативное, более оптимальное решение;
 реализовать его с применением рефакторинга  (программа не должна терять работоспособности в процессе переделки);
 вернуться в цикл.
 
 Всегда помни, что лучше иметь приемлемое решение сегодня, чем идеальное через год. Хотя никто не мешает продолжать работать над идеальным, выиграв время при помощи уловки Джонса.
 
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #2 :  09-11-2005 16:25 »   |  | 
 
 Я спрашивал про разделение не по времени, а по мышлению. Как в анекдоте. Ну не вижу я, что можно сразу поставить кастрюлю на огонь и всё тут. Вот и привожу начальные условия второй задачи к начальным условиям первой. Т.е. если я бы мог совершенно (или почти совершенно) формально из рекурсивно решённой задачи сделать итеративно решённую. Это ведь проще по отношению к тем задачам которые рекурсивно решаются проще. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #3 : 09-11-2005 21:34 »  |  | 
 
 Я как раз не о разделении времени говорил, а об использовании имеющегося задела (возможно, неоптимально работающего).
 Основная идея, которую я пытался донести, такова. Если у тебя уже есть отработанное решение для случая пустой кастрюли, в первую очередь используй его - выливай воду, а затем действуй по проверенному рецепту. Это гарантирует, что кипяток ты получишь максимально быстро (хотя, возможно, он обойдется неколько дороже, чем мог бы). Именно это в большинстве случаев и нужно при програмировании в промышленных условиях - получить результат, которого от тебя ждут, с максимальной скоростью, чтобы не тормозить работу пользователей. Медленная программа лучше, чем вовсе никакая. Пока жаждущие наслаждаются кипятком, ты можешь спокойно проанализировать задачу, увидеть, что воду выливать ни к чему, и заменить программу кипячения, не прерывая технологического процесса. В противном случае все сидели бы и ждали, пока на тебя сойдет озарение.
 
 То же самое и с твоим вопросом. Если для тебя очевидно рекурсивное решение - реализуй его, пусть программа работает. Если выяснится, что рекурсивная версия работает слишком медленно и требует слишком много ресурсов, причем виноват в этом действительно сам механизм рекурсии, а не дефект алгоритма, ты можешь попробовать реализовать итеративный алгоритм, делающий то же самое. При этом тебя никто не толкает в шею, программа худо-бедно работает. Если ты аккуратно продумал межмодульные интерфейсы, замена рекурсивного модуля итеративным не потащит за собой тотальную переделку программы.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #4 : 09-11-2005 22:03 »  |  | 
 
 Меня не интересует ни скорость, ни промышленные условия, ни задел. Я как раз не о разделении времени говорил, а об использовании имеющегося задела (возможно, неоптимально работающего).
 Если для тебя очевидно рекурсивное решение - реализуй его, пусть программа работает. Если выяснится, что рекурсивная версия работает слишком медленно и требует слишком много ресурсов, причем виноват в этом действительно сам механизм рекурсии, а не дефект алгоритма, ты можешь попробовать реализовать итеративный алгоритм, делающий то же самое.
 Всё это я прекрасно понял. Мне не нужно пробовать потом реализовывать итеративный вариант. Допустим, я вообще, в принципе, не могу решить задачу итеративно. Т.е. рассмотрим совсем крайний случай, чтобы ты понял, что я имею ввиду. Но я могу решить рекурсивно. Могу преобразовать (именно преобразовать, а не сделать заново) рекурсивную задачу в итеративную. И таким образом я всё-таки прихожу к задаче решённой итеративно. Т.е. есть три задачи. 1. Придти к решению итеративно. – не могу решить. 2. Придти к решению рекурсивно. – могу решить. 3. Преобразовать рекурсивное решение в итеративное – могу решить. Представь себе треугольник, в котором гипотенуза – первая задача, а катеты вторая и третья задача. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Finch 
								СпокойныйАдминистратор    Offline 
								Пол:    
								Пролетал мимо
								
								
								
								
								
							 | 
								|  | « Ответ #5 : 09-11-2005 22:07 »  |  | 
 
 А можно примерчики таких задач. Даже стало интересно. Что за такие задачи, по которым рекурсивный метод сразу очевиден. Для меня очевидна рекурсия в основном в задачах по проходу деревьев (директорий и т.д.). Итерактивные методы здесь намного сложнее. Поскольку самому приходится организовывать своеобразный стэк состояний. |  
						| 
								|  |  
								| « Последнее редактирование: 09-11-2005 22:14 от Finch » |  Записан | 
 
 Не будите спашяго дракона.              Джаффар (Коша) |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #6 : 09-11-2005 23:16 »  |  | 
 
 ...Допустим, я вообще, в принципе, не могу решить задачу итеративно. Т.е. рассмотрим совсем крайний случай, чтобы ты понял, что я имею ввиду. Но я могу решить рекурсивно. Могу преобразовать (именно преобразовать, а не сделать заново) рекурсивную задачу в итеративную. И таким образом я всё-таки прихожу к задаче решённой итеративно.
 Вообще-то в принципе рекурсия и итерация эквивалентны - для каждого рекурсивного алгоритма существует парный итеративный, и наоборот. Если ты построил корректный рекурсивный алгоритм, а затем корректно преобразовал его в итеративный, скорее всего, получишь тот же результат, что и при построении итеративного с нуля. То есть ты можешь пройти от одной вершине к другой по гипотенузе, а можешь и по двум катетам, все равно в результате окажешься там же. Не понял суть проблемы. Тебя смущает то, что такой путь оказывается более долгим и трудоемким? Или что-то иное? |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #7 : 10-11-2005 07:55 »  |  | 
 
 Возможно по двум катетам получится проще. Не знаю. Это моё предположение. Надо попробовать с обходом дерева, как Finch сказал.Хотя может есть и более удачные примеры такого подхода.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #8 : 10-11-2005 08:04 »  |  | 
 
 Дерево так дерево, надо же с чего-то начинать. Только полезно было бы сделать такую вещь: реализовать и рекурсивную, и итеративную версии и сравнить их по объему и сложности исходного кода, по скорости работы и требуемым ресурсам. Может оказаться, что возня с преобразованием рекурсии в итерацию не имеет никакого смысла.
 Если нет на примете задачи, могу предложить такую: построить дерево каталоговой структуры диска, в котором для каждой ветки хранится суммарный размер файлов в ней. IMHO такая штука пригодится, если хочешь разобраться, куда подевалось дисковое пространство. Сделай два варианта, сравним.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Finch 
								СпокойныйАдминистратор    Offline 
								Пол:    
								Пролетал мимо
								
								
								
								
								
							 | 
								|  | « Ответ #9 : 10-11-2005 13:24 »  |  | 
 
 Не все алгоритмы можно реализовать рекурсивно. Например обычная стандартная школьная задача по программированию, как нахождение корней квадратного уравнения. Или задача, которая пробегала недавно на форуме, насчет выяснения свойств треугольника. Я лично не представляю, как можно это реализовать рекурсией. |  
						| 
								|  |  
								|  |  Записан | 
 
 Не будите спашяго дракона.              Джаффар (Коша) |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #10 : 10-11-2005 13:29 »  |  | 
 
 Рекусия двойственна по отношению к итерации. Разве для решения квадратного уравнения нужны итерации? Или для того же треугольника? Точно так же и рекурсия там совершенно ни к чему. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #11 : 10-11-2005 13:41 »  |  | 
 
 Квадратное ур-иеСмотря каким способом решать. Если аналитически, то да. А если численно то методом деления отрезка пополам решается как раз рекурсивно и итеративно. Правда для полинома второй степени это наверно избыточно.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #12 : 10-11-2005 13:45 »  |  | 
 
 Квадратное уравнение - методом деления отрезка?.. Сколько лет программирую, но такого фокуса еще не видел   Это покруче, чем воду из кастрюли выливать... |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #13 : 10-11-2005 14:15 »  |  | 
 
 Ладно, соглашусь с Alf`ом. Перебор. Решать таким способом квадратное уравнение кроме как в учебных целях не стоит. Отрезок в котором находится решение замучаешся искать. Только если точно знаешь где этот отрезок находится. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #14 : 10-11-2005 14:20 »  |  | 
 
 Так любую задачу легче решать, когда решение известно заранее   |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #15 : 10-11-2005 14:21 »  |  | 
 
 Не решние а отрезок в котором находится решение |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #16 : 10-11-2005 14:24 »  |  | 
 
 Оно заведомо находится на отрезке от минус бесконечности до плюс бесконечности. Легче нам от этого? |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Olegator | 
								|  | « Ответ #17 : 10-11-2005 14:38 »  |  | 
 
 Продолжу основную тему. Так можно ли совершенно формально преобразовать рекурсию в итерацию. Ты вот говоришь: Вообще-то в принципе рекурсия и итерация эквивалентны - для каждого рекурсивного алгоритма существует парный итеративный, и наоборот. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #18 : 10-11-2005 14:50 »  |  | 
 
 На 100% не уверен, но по-моему, это можно достаточно формально сделать через конечные автоматы. Правда, может потребоваться (даже в большинстве случаев наверняка потребуется) автомат со стековой памятью. Поскольку рекурсия использует стек автоматически, а итерация должна управлять им явно, итеративное решение может оказаться в этом случае более громоздким.
 Вообще если первым на ум приходит рекурсивное решение, IMHO имет смысл реализовать именно его. В большинстве случаев миф о якобы расточительности рекурсии поддерживается теми, кто не владеет предметом. Каждый конкретный случай имеет смысл рассматривать отдельно, не поддаваясь на провокацию любую рекурсию заменять итерацией.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| npak | 
								|  | « Ответ #19 : 10-11-2005 17:19 »  |  | 
 
 Вообще-то в принципе рекурсия и итерация эквивалентны - для каждого рекурсивного алгоритма существует парный итеративный, и наоборот. 
 Хм ... при первом чтении я легко принял это утверждении, но когда дошёл до автомата со стековой памятью, эквивалентность итерации и рекурсии для меня стала не очевидной.  Я согласен, что для рекурсивного алгоритма можно построить эквивалентный итеративный с ручным управлением стеком (эквивалентность в смысле последовательности операций с данными, но в общем случае могут возникнуть дополнительные накладные расходы на поддержание своего стека). Верно ли обратное утверждение?   Дело в том, что не все итеративные алгоритмы используют стек (LIFO очередь).  Например, алгоритмы обхода графа "в ширину" используют FIFO-очередь, и я затрудняюсь предложить рекурсивный алгоритм, который был бы эквивалентен (то есть производил в точности те же операции в той же последовательности) обходу "в ширину". Но это замечание не относится к теме обсуждения, просто мысли вслух. |  
						| 
								|  |  
								|  |  Записан | 
 
 |  |  | 
	| 
			| 
					
						| Alf 
								Гость
 | 
								|  | « Ответ #20 : 11-11-2005 07:33 »  |  | 
 
 Мне и самому интуитивно кажется, что на практике с дуальностью итерация-рекурсия не все так уж гладко. Однако теория настаивает на их эквивалентности, а опровергнуть ее мне не под силу. Зачастую я не вижу рекурсивной либо итеративной альтернативы, но вполне допускаю, что она существует, просто находится за пределами моего понимания.
 Касательно "обхода в ширину" - возможно, удастся реализовать его, если предварительно строить вспомогательный список вершин одного уровня и затем уже его обрабатывать рекурсивно. Уверен, что алгоритм получится уродливый, непрактичный и будет представлять лишь академический интерес (если вообще будет). Впрочем, ручное управление стеком при итерации тоже элегантным решением не назовешь.
 
 Мое сугубое IMHO - следует выбирать наиболее адекватный решаемой задаче подход и не заморачиваться необоснованной переделкой итерации в рекурсию и наоборот. Например, если сущность по природе рекурсивна, обрабатывать ее итерацией столь же неестественно, как массив - рекурсией. Как мы уже обсуждали ранее, фобии по отношению к рекурсии чаще следствие непонимания, чем точного расчета.
 
 P.S. По ходу пришла мысль - FIFO можно имитировать двумя стеками LIFO, сначала набив первый, а потом переложив элементы во второй. Полнейшее уродство, но ведь теория вовсе не обещает равной эффективности итерации и рекурсии, речь ведь только о принципиальной реализуемости.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	|  |