Loading...

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

supersane avatar supersane 234 Точки

[Data Structures] Exam 13 Sept 2015

Здравейте, тъй като наближава изпита по структури от данни, започнах да се подготвям решавайки изпита, който беше даван на миналата инстанция на курса. Тъй като в този курс най-вече се учим да използваме подходящи структури от данни, за конкретната ситуация, за да имаме максимално добра оптимизация, е изключително важно времето, за което минават тестовете, и затова е изключително важно да няма random лагвания на Judge конкретно за този изпит, но ето какво се случва. Тъй като не съм сигурен дали проблема се проявява от самия Judge или от конкретно моето решение, нека започна малко от по-назад. Първо, задачата не е трудна за реализация, тънкостите се проявяват в 1-3 теста, където има тест на това колко бързо ни работят структурите(ако видите начина на оценяване, ще ви е ясно, че дори и един тест е критично важен за крайния резултат). Та общо взето важния момент, който забелязвам е, че трябва да се използват такива структури, че хем бързо да добавяме игри, хем после, когато ги търсим по prefix да са ни сортирани, за да можем максимално бързо да ги извлечем. Затова в Хеш Речник добавям играта заедно с нейната парола, и в SortedSet пазя имената на регистрираните игри, за да мога после максимално бързо да извлека по даден префикс. Добавяйки обаче в този SortedSet, който се имплементира върху подредено дърво, вече при командата RegisterGame аз имам сложност, която не е константа ами е log(n).  И ETO какво се получава в джъдж, тестовете, които съм задраскал са с променен код, но останалите са с един и същ код, и е факт, че 2 пъти получвам 100 точки, но повече пъти не ги получавам. Та общо взето пиша тук, за да попитам дали използваните стуктури и алгоритъм от мен не е "стабилен", и затова да се получава това разминаване и евентуално ако е нещо свързано със самия джъдж, да бъде нещо като "бъг репорт". Тук оставям и целия си код ако се интересувате от друга част свързана с него.

Тагове:
RoYaL avatar RoYaL Trainer 6849 Точки

Търсенето по prefix най-трики частта. Не може така да ги сложиш в наредено множество и да търсиш по префикс. Много удобно си пропуснал факта, че Substring е O(N) операция. Претърсването на нареденото множество с форийч също е O(N).

Когато решавах задачата това, което ми дойде на ум беше като добавям нов запис, да го разбия на всичките му възможни префикси (считайки, че ще имам по-малко добавяния от търсения) и да ги набия в MultiDictionary<prefix, games>. Това, което ме прецака е че като триех игра, тряваше да я изтрия само нея от префикса, а изтривах всички с този префикс. Иначе се оказа работещ вариант за бързодействие.

Това, което знам обаче по принцип е, че си има специални дървовидни структури, които се използват за търсене по префикси, учудващо се казват Prefix Trees или Tries (аналогично има и Suffix Trees).

2
supersane avatar supersane 234 Точки

Честно казано, дори не знаех за сложността на Substring метода :D. Иначе за тези Prefix Trees и Tries, не съм ги ползвал до сега и  затова може би не съм разгледал варианти с тях, благодаря за предложението. Щом Substring е с линейна сложност, мислиш ли, че това може да създава проблем от гледна точка на бързодействие на програта при сравняване на префикса с играта(по конкретно тук: if (game.Substring(0, prefix.Length) == prefix)). Също забелязах, че има метод .StartsWith(), но не съм сигурен дали той е по-оптимален от Substring. Ще се радвам да чуя мнението ти.

0
RoYaL avatar RoYaL Trainer 6849 Точки

Абе, човек, няма по-оптимален начин да вземеш парче от низ. Щом е префикс задължително трябва да изциклиш символ по символ от първия до N без 1 дължина на префикса. И префиксното дърво ще се държи така при запазване на префиксите сигурно. Обаче едно е при всяко търсене да циклиш 0..N-1, друго е веднъж и после само да луукъпваш.

0
supersane avatar supersane 234 Точки

Ясно, значи сега ще се поразровя да видя как стоят нещата и ще си изгледам недоизгледаните лекции от курса, в които предстои да се говори за prefix tree. :)

0
a_rusenov avatar a_rusenov 1103 Точки

Друг вариант - sortedGames го променяш на OrderedSet и ползваш .Range() с граници с [prefix, prefix + char.MaxValue). Долната граница е ясна - всички по-големи или равни на префикса (т.е. които започват с него). Горната граница гарантира, че няма да вземе геймовете с различен префикс, защото ако един стринг е > prefix + char.MaxValue то някоя буква преди последната е станала по-голяма (следователно не започва с префикса).

Важно е на сета да му се подаде ординал компаратор - new OrderedSet<string>((s1, s2) => String.CompareOrdinal). Така сравняването на стринговете ще става по ASCII кода на символите.

И тук вече имаш сложност O(log n + k), където k е броят елементи в интервала. В средния случай това е по-добре от линейния вариант.

Относно лагванията в Judge, запознати сме с този проблем - затова и този път изпитът ще бъде само с компоненти тестове, без Judge.

4
supersane avatar supersane 234 Точки

А може ли да обясниш, каква е разликата между OrderedSet и SortedSet. Това, което аз знам, е че в OrderedSet, можеш да достъпваш елемнтите с индексатор, и те отново са сортирани. Не ми е ясно обаче това достъпване с чупените скоби каква сложност има, отново ли е log n? И какво е точно предимството на OrderedSet пред SortedSet? Също така, не знаех какво точно прави този ординал компаратор, сега вече знам, но ми се появява въпроса ако не се подаде това в конструктора, по какъв начин ги сравнява? Много ще се радвам ако ми обясниш тези неща, защото наистина ми са доста мъгляви! Също така метода .Range(), каква сложност има? Също в моето решение, в кейса където търся игра по префикс, мисля че само в най-лошия случай сложноста се пада O(n), защото аз така или иначе брейквам foreach-a, когато взема 10 елемента, освен ако те не са в края разбира се. Доста въпроси зададох, но така пък ми се изясняват доста повече неща! Благодаря за оказаната помощ.

0
a_rusenov avatar a_rusenov 1103 Точки

Да, има индексация при OrderedSet<T>, сложността е дълбочината на дървото (т.е. О(log n) при едно балансирано дърво). Другото предимство са Range, RangeFrom и RangeTo методите за изваждане на всички елементи в даден интервал (виж демата от презентацията за Collections and Libraries). Има още няколко бонус метода като IsSupersetOf, Difference, GetFirst (връща най-малкия), GetLast (връща най-големия).

Range() има сложност O(log n + k), k е броят елементи. log n, защото има значение колко е дълбоко дървото. Той работи като пуска един In-Order DFS обаче без да обхожда ненужни поддървета (такива каквито не може да са в интервала).

Твоят вариант прави линейно търсене. В конкретния случай имаш ограничение до 10 елемента, затова и не е толкова бавно. Но пък представи си, ако често правиш префиксно търсене по Z на 1 000 000 елемента (измежду които няма такава дума) по твоя начин. :) В същия случай Range() ще направи едно log n спускане и ще установи, че няма такива елементи.

2
11/03/2016 16:45:42
Filkolev avatar Filkolev 4482 Точки

SortedSet няма метод Range(). Относно сложността не мога да цитирам конкретно, но е много бързо, то е просто отрязване на поддърво, което предполагам е логаритмична сложност или близка (търсене на границите в балансираното дърво).

Едит: според документацията, която намерих, дори е константно време взимането на рейндж. Потърси го, в момента не мога да копирам линк.

0
11/03/2016 16:50:04
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.