| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Алгоритмы > Думал, что придумал алгоритм, но оказалось - нет. |
| Автор: neutrino 9.6.2003, 15:20 | ||||||
| Приветствую! Значит так, задача состоит в следующем: надо инвертировать двусвязный список, скажем длинной n от места k до места i. Скорость нужна очень высокая. Вот что я придумал: 1) просто поменять местами k<->i, k+1<->i-1, k+2<->i-2 ... k+(i-k)/2<->i-(i-k)/2 не подходит, долго работает. 2) каждому элементу в списке (структуре) добавить флаг, который бы указывал на каком из двух указателей лежит следующий элемент. Это ускорит работу где-то в два раза, т.к. функция "переворачиваниа" очень проста - с 1 на 0 и обратно и не нужно будет физически менять местами данные. Таким образом, алгоритм следующий: Для начала объявим структуру:
Во всех элементах, начиная от k и кончая i инвертировать бит направления - Direction. Для этого достаточно одной команды: not [esi].Direction. Так, если мы будем считывать после переворота список, то будем знать в какой указатель (Link[]) нам нужно идти, чтобы дойти до следующего элемента. Но я пошел дальше... 3) Допустим, что у всех элементов, указатель Link[1] - указывает на предыдущий, а Link[0] - на следующий. Далее, если мы делаем инверсию между элементами А1 и А4 (не включаиа их), то: Обозначим A2=A1->Link[0] (следующий от А1) A3=A4->Link[1] (предыдущий от А4) Алгоритм следующий:
Теперь попробуем прочитать этот список таким правилом: если мы натыкаемся на 1 в Direction, то меняем текущее направление на противоположное. И так рассмотрим пример для большей ясности: A={1,2,3,4,5,6,7}; Представим как двусвязный список: введем обозначение - [p|An|D|n] p - указатель на предыдущий элемент; n - указатель на следующий элемент; An - данное в структуре (число); D - флаг направления: 1 - инвертировать, 0 - оставить прежнее. <-[1|A=1|0|0]<->[1|A=2|0|0]<->[1|A=3|0|0]<->[1|A=4|0|0]<->[1|A=5|0|0]<->[1|A=6|0|0]<->[1|A=7|0|0]-> Сделаем инверсию с А1 по А6 (не включая границы) А2=2, А3=5 после выполнения алгоритма <-[1|A=1|0|0]<->[0|A=5|1|1]<->[0|A=4|0|1]<->[0|A=3|0|1]<->[0|A=2|0|1]<->[1|A=6|1|0]<->[1|A=7|0|0]-> Как обходить такой список? А очень просто: берем переменную булевского типа и инициализируем ее нулем. Далее легко понять по какой схемме меняется направление: если было направление 0 и в элементе флаг 0, то далее направление будет 0 если было направление 0 и в элементе флаг 1, то далее направление будет 1 если было направление 1 и в элементе флаг 0, то далее направление будет 1 если было направление 1 и в элементе флаг 1, то далее направление будет 0 т.е. простым русским языком это XOR и так можно написать такую программу для обхода:
Процедура инверсии работает и делает это очень быстро. А теперь внимание, вопрос: Если в цепочке будет уже один раз перевернута подцепочка, то такая цепочка неправильно будет инверсированна. Как этого избежать? Какие ваши идеи? Заранее благодарен. |
| Автор: Chingachguk 10.6.2003, 08:51 |
| Ты не мог бы закинуть весь свой код в последней редакции ? |
| Автор: neutrino 11.6.2003, 12:56 | ||
Это мне нужно для написания эвристик для решения транспортной задачи. Ниже я привел код своей программы (из него убраны нерелевантные участки). Не обращайте внимания на некоторые, казалось бы, ненужные функции; дело в том, что я буду их "зашивать" в DLL. Я написал пояснения к строкам, так, Вам будет много легче разбирать этот код. Программа должна оптимизировать путь через семь городов известной эвристикой 2-OPT. Проблема этой программы заключается в том же самом: цепочка, в которой уже присутствует другая инверсированная цепочка, будет инверсирована неправильно.
З.Ы. Чингачгук, извиняй, что раньше не написал пояснения. Просто перевод их на русский заняло время. |
| Автор: Mnior 5.9.2003, 16:08 | ||||
Это мне напоминает задачи типа:
Вариантов куча: - менять данные, - менять ссылки, - менять способ использования ссылки (т.е. считать в конкретном месте проги, что не первая ссылка на следующий элемент, а вторая) - добавить элемент направления (Direction) для данного элемента - добавить элемент направления указывающий что с текущего элемента все ссылки считаются наоборот - и т.д. и т.п. И каждый вариант умеет столькоже преимуществ сколько и недостатков. Да типа бул мал (но ваще нет т.к. он в памяти не занимает бит а минимум байт, а ваще как integer - смотря где), но увеличив скорость инвертирования, уменьшилась скорость считывания. А ваще я не разбирался в проге, лень и большая и не читаема (для меня, я привык к ПроЛогу), так что может этот вариант оптимален для данной цели. Но надо точнее формулировать задачу. Ваще этот цикл оветов посвящен алгоритмическому мышлению - с низким уровнем обобщенности. Кто-то занимается кодированием проца, а кто-то вводом (накоплением) информации. Кто-то занимается компиляцией, а кто-то оставляет это компилятору. |
| Автор: neutrino 5.9.2003, 21:33 |
| Хмм... Во как!! Да мне и не нужно память экономить. Скорость нужна высокая. А что это Вы своим постом хотели сказать? Я ничего из него не понял. Какой-то набор слов... |
| Автор: Mnior 6.9.2003, 10:06 | ||||
А вот сумарная скорость осталось большой? ..... А из:
Почему твой метод быстрее? Например, в сравнение с 3-им или с 5-ым? |
| Автор: neutrino 7.9.2003, 11:36 |
| Потому что вместо того, чтобы менять местами, скажем, 100 элементов; можно изменить значения 4-х указателей. Да и вообще мой метод - это тот, что у тебя под пунктом 4 и 5. Это и есть мой способ! |
| Автор: Unregistered 8.9.2003, 01:18 | ||
| Что ты преципился к 1-му пункту - его не существет. Для танкистов: Пункты 4 и 5 не пересекаются. Т.к. для 4-го Direction считается только для текужего элемента. А в 5-м для всех последующих (ну теги в http(xml)). Что, у тебя оба варианта используются А как насчет 3-го: (repeat:) в конкретном месте проги, в конкретной процедуре (функции) в конкретно цикле имеется дополнительная переменная (константа) указывающая на порядок считывания ссылок. Т.е. не списки "типа" инвертировать, а прогу так завернуть, чтоб они не инвертировались "на самом деле" (да и чтоб никто разобраться в коде не мог А насчет:
... и Соционика дает свое: челы делятся на "Искателей" недокументированных возможностях проги, функции, команды и "Системщиков" (иль "Аналитиков") окружающих "фактов", реалий окружающих явлений. У которых решение проблемы уложены в некие (довольно абстрактные) рамки алгоритма решения, у которых меняется цель: "описание" сути проблемы, а не поиск алгоритма ее достижения и "ускорении" каждого его шага. Ибо из "описания" следует глобально эффективнейший алгоритм, и для всех вариантов целей проблемы. Если ты видишь решение задачи глобально, и твой алгоритм афигительный, то непонятно: зачем ты даешь маленький его кусочек? Он не сравним с самой "всей видимостью проблемы". Решение задачи, например: "инвертирования двухсвязного списка", многообразно и лишь только для конкретной проблемы эта подзадача будет иметь ЭТО единственое "самое лучшее" решение, а для другой проблемы другое и тоже "самое лучшее". (Для сомневающихся) Эффективность проста: не делать лишних движений. (сказано в попыхах, не оговорены все понятия в предложении <- это для отката, но не отказа) Правда, может, для кого-то это звучит красиво-лаконично, но для некоторых пративно-очевидно, как "Вода мокрая". А в "описание" входит НЕ только то, что вы думаете ... ... и кому-то это не нравится. |
| Автор: neutrino 19.12.2004, 22:12 |
| Вопрос все еще открыт! Пока был только офтоп... |
| Автор: ovr2000 20.12.2004, 13:05 |
| Безо всякого направления, это лишнее. Если я правильно понял - обход в правильном направлении делается очень редко, в основном изменение направления. Тогда: существует двунаправленный список, ссылки которого могут быть Null или другой элемент (как и было). Добавим дополнительное условие: следующий элемент не может быть предыдущим, т.е. если А имеет ссылку на В, В имеет ссылку на А и С, направление обхода выбирается в сторону С, т.к. предыдущий элемент был А. Значит для изменения направления достаточно перепривязать концы цепочки. |
| Автор: 3,14 20.12.2004, 15:56 | ||
Не совсем понял, а что структуру списка можно изменять, или нужно составить алгоритм непосредственно для типа
|
| Автор: neutrino 5.1.2005, 22:30 | ||||
менять можно. Добавлено @ 22:32
А чем этот вариант лучше? Надо бы обдумать этот вариает ... |
| Автор: Б а Т о Н 30.1.2005, 03:19 |
| Господи, ребята. Это же стандартная и довольно простая задача. Один переворот делается за O(1), и многие мои знакомые это много раз писали. Я сам это писал на чемпионате ACM. Честно говоря, мне в лом читать весь топик во всех подробностях, но одно могу сказать - алгоритм давно известный и реально работающий. Если надо - могу прокомментировать и ответить на конкретные вопросы. Вкратце - можно ставить флажки и использовать структуру данных DEC. |
| Автор: neutrino 4.2.2005, 11:38 | ||
| 1) o(1) = o(0) 2)
С этого места можно поподробнее. |
| Автор: De Gray 18.2.2005, 15:15 | ||||
Если тебе нужно проходить какие-то куски списка в прямом направлении, какие-то в обратном тогда может помочь следующее
Если изменить стурктуру, то.
Ну или что-то по мотивам. У меня давно работало вполне, для меня по крайней мере сносно -- задача была по многим признакам рассклассифицировать доволно сложный временной ряд. |