Уникальные запросы
От: SomeOne_TT  
Дата: 17.09.18 15:32
Оценка:
Не дает покоя нерешенная задачка.
Возможно, вы покажете, как она решается?
(найти ее можно при регистрации на сайте яндекс блица по ссылке https://contest.yandex.ru/contest/8470/problems/K/)

"Пользователи задают в Яндекс.Поиске десятки тысяч запросов в секунду. Часть запросов задают сотни раз в час, другая часть запросов повторяется несколько раз в день, третью часть запросов пользователи спрашивают у Яндекса впервые.
Необходимо оценить количество уникальных запросов, при условии наличия 500 KB оперативной памяти. Гарантируется, что правильный ответ не превосходит
100000 и не меньше, чем 50000.
Решение засчитывается, если ответ отличается от правильного не более, чем на 5%. "

Для плюсов

Ограничение времени
2 секунды

Ограничение памяти
500Kb

Формат ввода
В первой строке указано число
n ≤ 500000 — количество запросов, среди которых нужно найти число уникальных. В каждой из
n последующих строк содержится по одному запросу. Длина каждого запроса не превосходит
1000 символов.

Формат вывода
Необходимо вывести одно число — оценку количества уникальных запросов. Оценка не обязана быть целой.


Я решал ее "в лоб" хэшируя запросы, но, очевидно, это неверно — совершенно не используется информация про распределение запросов.
Re: Уникальные запросы
От: Qulac Россия  
Дата: 17.09.18 16:54
Оценка:
Здравствуйте, SomeOne_TT, Вы писали:

SO_>Не дает покоя нерешенная задачка.

SO_>Возможно, вы покажете, как она решается?
SO_>(найти ее можно при регистрации на сайте яндекс блица по ссылке https://contest.yandex.ru/contest/8470/problems/K/)

SO_>"Пользователи задают в Яндекс.Поиске десятки тысяч запросов в секунду. Часть запросов задают сотни раз в час, другая часть запросов повторяется несколько раз в день, третью часть запросов пользователи спрашивают у Яндекса впервые.

SO_>Необходимо оценить количество уникальных запросов, при условии наличия 500 KB оперативной памяти. Гарантируется, что правильный ответ не превосходит
SO_>100000 и не меньше, чем 50000.
SO_>Решение засчитывается, если ответ отличается от правильного не более, чем на 5%. "

SO_>Для плюсов


SO_>Ограничение времени

SO_>2 секунды

SO_>Ограничение памяти

SO_>500Kb

SO_>Формат ввода

SO_>В первой строке указано число
SO_>n ≤ 500000 — количество запросов, среди которых нужно найти число уникальных. В каждой из
SO_>n последующих строк содержится по одному запросу. Длина каждого запроса не превосходит
SO_>1000 символов.

SO_>Формат вывода

SO_>Необходимо вывести одно число — оценку количества уникальных запросов. Оценка не обязана быть целой.


SO_>Я решал ее "в лоб" хэшируя запросы, но, очевидно, это неверно — совершенно не используется информация про распределение запросов.


Точность 5%, т.е. все можно не анализировать, а взять например каждый 10 запрос или сравнивать только начало строк.
Программа – это мысли спрессованные в код
Отредактировано 17.09.2018 17:04 Qulac . Предыдущая версия .
Re[2]: Уникальные запросы
От: SomeOne_TT  
Дата: 17.09.18 17:09
Оценка:
Здравствуйте, Qulac, Вы писали:


Q>Точность 5%, т.е. все можно не анализировать, а взять например каждый 10 запрос или сравнивать только начало строк.


Точность 95%
Re[3]: Уникальные запросы
От: Qulac Россия  
Дата: 17.09.18 17:13
Оценка:
Здравствуйте, SomeOne_TT, Вы писали:

SO_>Здравствуйте, Qulac, Вы писали:



Q>>Точность 5%, т.е. все можно не анализировать, а взять например каждый 10 запрос или сравнивать только начало строк.


SO_>Точность 95%


ОК. Да, ещё длину запросов можно взять за основу.
Программа – это мысли спрессованные в код
Re: Уникальные запросы
От: SergeCpp Россия http://zoozahita.ru
Дата: 17.09.18 17:14
Оценка: 1 (1)
Здравствуйте, SomeOne_TT.

Считаем crc32 от строки, масштабируем результат, чтоб от 0 до 512 кб было и устанавливаем соотв. байт. В конце считаем число ненулевых. Можно по битам (чтоб точнее), битов будет 500 тыс * 8 = 4 миллиона примерно. Строку можно к нижнему регистру.
http://zoozahita.ruБездомные животные Екатеринбурга ищут хозяев
Re[2]: Уникальные запросы
От: SomeOne_TT  
Дата: 17.09.18 18:02
Оценка:
Здравствуйте, SergeCpp, Вы писали:

SC>Здравствуйте, SomeOne_TT.


SC>Считаем crc32 от строки, масштабируем результат, чтоб от 0 до 512 кб было и устанавливаем соотв. байт. В конце считаем число ненулевых. Можно по битам (чтоб точнее), битов будет 500 тыс * 8 = 4 миллиона примерно. Строку можно к нижнему регистру.


Не вижу разницы с моим подходом.
Максимум 100к уников, для 500к памяти это по 5 байт на хэш, а запросов-то 500к...

либо надо извращаться и как-то хитро моделировать хэш функцию с учетом энтропии алфавита и
максимальной длины значения (1к),либо правильный ответ все же учитывает распределение
частот уников/не уников в домене запросов.
Уникальные запросы
От: SergeCpp Россия http://zoozahita.ru
Дата: 17.09.18 18:19
Оценка:
Здравствуйте, SomeOne_TT.

SC>>Считаем crc32 от строки, масштабируем результат, чтоб от 0 до 512 кб было и устанавливаем соотв. байт. В конце считаем число ненулевых. Можно по битам (чтоб точнее), битов будет 500 тыс * 8 = 4 миллиона примерно. Строку можно к нижнему регистру.


SO_>Не вижу разницы с моим подходом.


Вы свой подход не описали, лишь туманно что-то сказали вскользь.

SO_>Максимум 100к уников, для 500к памяти это по 5 байт на хэш, а запросов-то 500к...


Не нужно никаких байт на хэш, я же ясно написал.

А 500 К запросов, или больше, написано же: "Гарантируется, что правильный ответ не превосходит
100000", то есть уникальных в 500 тыс байтов (или в 4 мил. битов) будет не более 100 тыс.

Для чего эти биты? А если все crc32 наши образуют компактные кластеры, тогда при 500 тыс. будет слишком много коллизий и наши 100 тыс. макс. образуют всего тыс. 50 разных. А при побитовом уже разрежённее будет. 4 млрд (crc32 max) делим на 1000 получаем 4 милл. для битов.

P. S. Если под уникальными имеются в виду те, что только один раз были, то 2 милл. двухбитовых полей (тогда crc32 делим на 2 тыс.). И сложение с насыщением (00-01-10-11-11-11-...) -- потом считаем единички.
http://zoozahita.ruБездомные животные Екатеринбурга ищут хозяев
Отредактировано 17.09.2018 19:22 SergeCpp . Предыдущая версия . Еще …
Отредактировано 17.09.2018 19:21 SergeCpp . Предыдущая версия .
Re: Уникальные запросы
От: Chorkov Россия  
Дата: 18.09.18 07:48
Оценка: 9 (2) +1
Здравствуйте, SomeOne_TT, Вы писали:

SO_>Не дает покоя нерешенная задачка.

SO_>Возможно, вы покажете, как она решается?
SO_>(найти ее можно при регистрации на сайте яндекс блица по ссылке https://contest.yandex.ru/contest/8470/problems/K/)

SO_>"Пользователи задают в Яндекс.Поиске десятки тысяч запросов в секунду. Часть запросов задают сотни раз в час, другая часть запросов повторяется несколько раз в день, третью часть запросов пользователи спрашивают у Яндекса впервые.

SO_>Необходимо оценить количество уникальных запросов, при условии наличия 500 KB оперативной памяти. Гарантируется, что правильный ответ не превосходит
SO_>100000 и не меньше, чем 50000.
SO_>Решение засчитывается, если ответ отличается от правильного не более, чем на 5%. "

SO_>Для плюсов


SO_>Ограничение времени

SO_>2 секунды

SO_>Ограничение памяти

SO_>500Kb

SO_>Формат ввода

SO_>В первой строке указано число
SO_>n ≤ 500000 — количество запросов, среди которых нужно найти число уникальных. В каждой из
SO_>n последующих строк содержится по одному запросу. Длина каждого запроса не превосходит
SO_>1000 символов.

SO_>Формат вывода

SO_>Необходимо вывести одно число — оценку количества уникальных запросов. Оценка не обязана быть целой.


SO_>Я решал ее "в лоб" хэшируя запросы, но, очевидно, это неверно — совершенно не используется информация про распределение запросов.


Что значит неверно? Не уложился в заданное время? память? точность?
Посчитать хеш 500 МБ строк (для получения точного ответа) за 2 секунды, может и не получаться... если диск тормозит.
Возможно, ты ее решил не оптимально, но тогда нужны критерии оптимальности: минимизируем время работы при заданной точности и доступной памяти?
Или оптимизируем точность при фиксированном времени? Или память?

Если не уложился в память:
Возможно, на их компиляторе char не 8-битный, используй std::uint8_t/std::uint64_t для организации битового массива.
Возможно, они считают память, захваченную процессом целиком, с учетом памяти стека, файловых буферов, etc... В этом случае нельзя самому аллоцировать 500 кб, уменьши размер массива до 256kB.
Возможно, стоит посмотреть в сторону более низкоуровневых функций чтения из файла, для уменьшения размеров буферов чтения....
(Последнее — врядли понадобится).

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

Если не уложился во время:
Дописывай в конец каждой прочитанной строки — нули. Это позволит при расчете хеша рассматривать строку ка массив uint64_t, а не байт. (Что в 8 раз быстрее).
Не обрабатывай весь файл данных, обработай сколько успеешь за 1.9 секунды. Далее — экстраполируй статистику.
Re[2]: Уникальные запросы
От: watchmaker  
Дата: 18.09.18 20:57
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>Если не уложился в память:

C>Если не уложился в точность:
C>Если не уложился во время:

Всё это, безусловно, замечательно и хорошо.

Но не стоит забывать, что даже тщательно оптимизированная пузырьковая сортировка на больших последовательностях всё равно будет работать медленнее какой-нибудь небрежно написанной реализации quicksort.

Так и тут: вместо того, чтобы выкраивать такты и байты, можно просто взять высокоуровневый алгоритм получше. Их много известно: Count-distinct problem.
Re: Уникальные запросы
От: Кодт Россия  
Дата: 26.09.18 12:52
Оценка: 6 (1)
Здравствуйте, SomeOne_TT, Вы писали:

SO_>n ≤ 500000 — количество запросов, среди которых нужно найти число уникальных. В каждой из

SO_>n последующих строк содержится по одному запросу. Длина каждого запроса не превосходит
SO_>1000 символов.

Всего у нас есть 500Кб, то есть, 500 000 байт (примерно). Или 4 000 000 бит (примерно).
И нам дано до 500 000 строк.
Совпадение? Не думаю.

Если у нас есть идеальная хеш-функция, отображающая строки на 0..500000, то задача выглядит очень просто:
— заполнить битовый массив нулями
— для каждой строки выставить соответствующий бит
— посчитать количество выставленных битов (причём это можно сделать сразу)

По условиям задачи допускаются коллизии не более чем в 5% случаев. (Или как-то надо пересчитать формулу — из количества FR+FA в вероятность коллизии... сейчас лень думать).

Предположим, что наше наивное решение (мы взяли хеш-функцию наобум) вылетело из этих ограничений. Функция оказалась слишком неидеальной.
Что можно сделать?

1) Отображать не на 500 000, а на 4 000 000.
2) Взять 8 разных хеш-функций, отображающих на 500 000 каждая. Считать коллизией факт одновременно 8 коллизий по каждой из них.

SO_>Я решал ее "в лоб" хэшируя запросы, но, очевидно, это неверно — совершенно не используется информация про распределение запросов.


Сжатие данных — это, конечно, здорово, но наш битовый массив покрывает байтовые строки длиной до 7. (8**7 = 2млн). Это значит, что 1000-байтовые строки придётся сжимать в 142 раза.
Мы точно это сможем?
Поэтому, на первом этапе, я бы не стал париться и считал бы, что обычные хеш-функции справляются со своей задачей по устранению неравномерности распределения.
Перекуём баги на фичи!
Re[2]: Уникальные запросы
От: Erop Россия  
Дата: 02.10.18 19:36
Оценка:
Здравствуйте, Кодт, Вы писали:

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


Ещё можно случайную подвыборку переданных запросов брать и по ней как-то судить о всём распределении.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.