Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности

Новые сообщения [новые:0]
Дайджест
Горячие темы
Избранное [новые:0]
Форумы
Пользователи
Статистика
Статистика нагрузки
Мод. лог
Поиск
|
|
21.10.2016, 19:49
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
Всем привет, а может кто прояснить такую ситуацию. По свободе выполняю задачки на hackerrank.com на питоне. Была простая задача - на вход дается строка, нам надо определить является ли она панграммой (т.е. содержит каждую из 26 латинских букв хотя бы раз). Код: python 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. После того, как ты решаешь задание, ты можешь посмотреть альтернативные решения. Нашел там такой пример Код: python 1. 2. 3. Решил ради интереса проверить скорость работы. И вот тут меня ждало удивление. На строке в 10кк символов, причем последний 26й символ я специально добавлял в конец строки Код: python 1. 2. второй вариант отрабатывал намного быстрее - time: 0.2s, мой вариант time: 4.65s. Собственно вопрос. Как set так быстро удаляет дубликаты? Ведь ему необходимо пройти весь массив. И даже если я в начало массива добавляю саму панграмму, т.е. "thequickbrownfoxjumpsoverthelazydog" все равно ничего не меняется. Я конечно понимаю, что мой вариант далек от оптимального, но почему такая большая разница? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
21.10.2016, 20:00
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
ALex_hha, set не удаляет дубликаты, он их просто не добавляет. И set - это не массив, это хэш-таблица, поиск в ней имеет не линейную, а логарифмическую сложность ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
21.10.2016, 20:07
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
Да я знаю, но ведь set() в любом случае надо обойти всю строку, чтобы быть уверенным, а это 10кк символов? Или там какой то хитрый алгоритм используется ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
21.10.2016, 20:35
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
FishHookALex_hha, set не удаляет дубликаты, он их просто не добавляет. И set - это не массив, это хэш-таблица, поиск в ней имеет не линейную, а логарифмическую сложность Небольшая поправка... сложность алгоритма поиска значения по хэштаблице составляет O(1) Это у бинарного поиска O(log(n)) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
21.10.2016, 20:52
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
ALex_hhaВсем привет, а может кто прояснить такую ситуацию. По свободе выполняю задачки на hackerrank.com на питоне. Была простая задача - на вход дается строка, нам надо определить является ли она панграммой (т.е. содержит каждую из 26 латинских букв хотя бы раз). Код: python 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. После того, как ты решаешь задание, ты можешь посмотреть альтернативные решения. Нашел там такой пример Код: python 1. 2. 3. Решил ради интереса проверить скорость работы. И вот тут меня ждало удивление. На строке в 10кк символов, причем последний 26й символ я специально добавлял в конец строки Код: python 1. 2. второй вариант отрабатывал намного быстрее - time: 0.2s, мой вариант time: 4.65s. Собственно вопрос. Как set так быстро удаляет дубликаты? Ведь ему необходимо пройти весь массив. И даже если я в начало массива добавляю саму панграмму, т.е. "thequickbrownfoxjumpsoverthelazydog" все равно ничего не меняется. Я конечно понимаю, что мой вариант далек от оптимального, но почему такая большая разница? как-то чрезмерно сложно. Почему не так: Код: python 1. 2. Зачем столько танцев с бубном-то...какие-то сортировки лишние... P.S. за hackerrank.com спасибо. Я тоже решил как-раз питон изучать понемногу. Будет где в основах попрактиковаться. А то как известно мы запоминаем всего 10% из прочитанного... Вроде бы вчера треть книги Dive in Python прочитал, а для решения простой задачки пришлось названия методов гуглить всё же :) Без практики "в одно ухо влетело, из другого вылетело" так сказать :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
21.10.2016, 21:03
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
ALex_hhaДа я знаю, но ведь set() в любом случае надо обойти всю строку, чтобы быть уверенным, а это 10кк символов? Или там какой то хитрый алгоритм используется Всё дело может быть в кодировке. Если эта кодировка не однобайтовая с переменным количеством байт на символ (как utf-8), то для получения символа с позицией 1000 программе надо прочитать все 1000 символов, что бы узнать где он есть и прочитать его. Не знаю как обстоят дела с кодировками с фиксированным размером символов, вероятно они оптимизированы, но не факт. Множества, списки, словари и т.д. лишены этого недостатка, потому как с ними чётко понятно где искать "следующий" элемент. И тогда получение нужного элемента сводится к сложности O(1),а не O(n) как в строке. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
24.10.2016, 10:57
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
Ну конкретно я проверял на обычном ascii тексте, это собственно видно из кода генерации строки Код: python 1. авторМножества, списки, словари и т.д. лишены этого недостатка, потому как с ними чётко понятно где искать "следующий" элемент. И тогда получение нужного элемента сводится к сложности O(1),а не O(n) как в строке. да, я читал, что списки и словари в питоне оптимизированы по самое немогу :) Как написали на SO - "Dictionaries are one of the more heavily tuned parts of Python, since they underlie so much of the language." Кстати, а сама операция преобразования строки во множество или список/словарь она не является затратной? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
24.10.2016, 23:07
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
ALex_hhaНу конкретно я проверял на обычном ascii тексте, это собственно видно из кода генерации строки Код: python 1. авторМножества, списки, словари и т.д. лишены этого недостатка, потому как с ними чётко понятно где искать "следующий" элемент. И тогда получение нужного элемента сводится к сложности O(1),а не O(n) как в строке. да, я читал, что списки и словари в питоне оптимизированы по самое немогу :) Как написали на SO - "Dictionaries are one of the more heavily tuned parts of Python, since they underlie so much of the language." Кстати, а сама операция преобразования строки во множество или список/словарь она не является затратной? проверил :) нет... не является Итак, берём 3 файла (str.py, str2.py, empty.py... исходники в той же последовательности): Код: python 1. 2. 3. 4. 5. 6. 7. Код: python 1. 2. 3. 4. 5. 6. Код: python 1. 2. 3. 4. моя консоль$ time ./str.py real 0m6.835s user 0m6.812s sys 0m0.032s $ time ./str2.py real 0m0.975s user 0m0.940s sys 0m0.036s $ time ./empty.py real 0m0.113s user 0m0.024s sys 0m0.088s Итак, имеем следующие данные: 1. Строка в 100 000 000 символов создаётся в течении 0.024s . Что логично, учитывая что запись в ячейку памяти это 1 или 2 такта (не вспомню уже), то процессор может за секунду заполнить 6 гигабайт памяти (мы же заполняем значительно меньший объём, всего 100 Мб) 2. Строка в 100 000 000 символов переводится во множество в течении где-то 0.940s. По сути то же чтение и заполнение ячеек памяти, только + механизм вычисления хэш функции для каждой операции записи 3. А вот перебор строки в цикле ведёт к увеличению затрат ещё на 6 секунд. Теперь надо определить, эта задержка обусловлена циклом как таковым (расходами на его работу в питоне) или же она вызвана именно работой со строкой. Берём ещё файл (rng.py): Код: python 1. 2. 3. 4. 5. 6. 7. и меряем скорость перебора не строки, а списка: консоль$ time ./rng.py real 0m7.397s user 0m7.204s sys 0m0.200s Ничего не поменялось... всё те же 7 секунд (плюс-минус). Тут я сам был удивлён, потому что изначальное предположение по поводу строк оказалось ошибочным. Дело не в строке, а в цикле! Для перебора множества ситуация тоже не меняется. После этого я уже начал экспериментировать и нарвался на ожидаемый, но совсем неочевидный эффект. Смотрим файл rng2.py: Код: python 1. 2. 3. 4. 5. 6. Результат: консоль$ time ./rng2.py real 0m2.744s user 0m2.572s sys 0m0.172s ТАДАААААМ!!! Тернарный оператор for...in даёт нам оптимизацию вдвое (разумеется это так сильно заметно только из-за простоты выполняемых операций). Исходя из последнего опыта можно предположить, что очень много времени терялось на создании отдельного объекта строки "с" для каждой конкретной итерации. И при использовании тернарной операции похоже этого не происходит и каждое следующее значение передаётся напрямую в функцию добавления значения во множество. То есть вместо создания объекта строки для переменной "c", а потом ещё его дублирования для записи во множество, теперь происходит создание только одного экземпляра объекта, что изрядно экономит время :) P.S. Как помним, в питоне всё является объектами. Потому для ускорения работы очень больших циклов, похоже, следует использовать тернарные операции for...in по возможности. Проверял на практике, с числами та же хрень например. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 11:09
|
|||
|---|---|---|---|
|
|||
python: скорость выполнения set() для массива |
|||
|
#18+
и что никого не смутило, что автор сам реализует механизм получения уникального списка, а в примере в одну строчку - это делает скомпилированый код??? ну как бы компилированый код всегда работает быстрее интерпретируемого - и никакие байткоды и оптимизации тут не помогут. ЗЫ может создатель библиотеки где клас Set описан...так вообще на ассемблере вставки пили, там быстродействие достигает теоретического максимума. ЗЫЗЫ автор однострочного я так понял не расматривает наличие тире и прочего... так что наверно надо брать пересечение = множество уникальных значений /\ множество букв алфавита и его длина должна быть равно длинне множества букв алфавита. поди и быстрее будет.(в сравнение с доработаным примером однострочным - отнимать не только пробел а и остальные символы которые могут встретится.) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 11:14
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
Спасибо интересно. авторstr1 = 'a'*100000000 но вот тут мне кажется немного не честно генерить строку с одинаковых символов, а потом на нее натравливать set() ;) А пробовали генерить таки рандомные символы, результат будет таким же? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 11:18
|
|||
|---|---|---|---|
|
|||
python: скорость выполнения set() для массива |
|||
|
#18+
раз уж тут туса питонщиков(я не знаю питон вообще) погуглил документацию... вот в упор не нашол где бы чётко было написано, если конструктору класса Set передать строку, то он создаст множество символов этой строки. там написано только создание с помощьью перечисления через запятую, или по типуset(x for x in 'i love you' if x not in ' -' ) может я не правильную документацию читаю? или чего из вида упустил. но вот по пхп томуже, джаваскрипту, или библиотеке гуглится легко документация где будет чётко фнукция(-список-параметров): тип результата описание что делает описание параметров описание результата примеры использования если класс то перечисление полей методов, дальше по кажомупу полю методу как про функцию /глобальную коснтанту - чётко что и для чего. по классу чётко описан конструктор(ы)... а тут както трудно сбухты барахты чтото найти в документации по питону. ?? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 11:21
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
авторЗЫЗЫ автор однострочного я так понял не расматривает наличие тире и прочего... по условию на входе могут быть строчные, заглавные и пробелы. авторConstraints Length of can be at most and it may contain spaces, lower case and upper case letters . Lower-case and upper-case instances of a letter are considered the same. автори что никого не смутило, что автор сам реализует механизм получения уникального списка Ну не списка, а строки ;) А вы предлагаете вводить 10кк символов с клавиатуры? :D авторв сравнение с доработаным примером однострочным - отнимать не только пробел а и остальные символы которые могут встретится.) в идеале пожалуй да, если в условии не было бы оговорено, что именно может быть на входе. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 11:27
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
авторвот в упор не нашол где бы чётко было написано, если конструктору класса Set передать строку, то он создаст множество символов этой строки ну в самом описании конструктора есть комментарий - "Build an unordered collection of unique elements." ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 12:29
|
|||
|---|---|---|---|
|
|||
python: скорость выполнения set() для массива |
|||
|
#18+
ALex_hhaавторвот в упор не нашол где бы чётко было написано, если конструктору класса Set передать строку, то он создаст множество символов этой строки ну в самом описании конструктора есть комментарий - "Build an unordered collection of unique elements." так что делает конструктор и так понятно, но из чего он это делает? вот масив на вход он примет? а обьект по которому можно как по масиву пройтись??? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 12:47
|
|||
|---|---|---|---|
|
|||
python: скорость выполнения set() для массива |
|||
|
#18+
ПрограмёрALex_hhaНу конкретно я проверял на обычном ascii тексте, это собственно видно из кода генерации строки Код: python 1. пропущено... да, я читал, что списки и словари в питоне оптимизированы по самое немогу :) Как написали на SO - "Dictionaries are one of the more heavily tuned parts of Python, since they underlie so much of the language." Кстати, а сама операция преобразования строки во множество или список/словарь она не является затратной? проверил :) нет... не является Итак, берём 3 файла (str.py, str2.py, empty.py... исходники в той же последовательности): Код: python 1. 2. 3. 4. 5. 6. 7. Код: python 1. 2. 3. 4. 5. 6. Код: python 1. 2. 3. 4. моя консоль$ time ./str.py real 0m6.835s user 0m6.812s sys 0m0.032s $ time ./str2.py real 0m0.975s user 0m0.940s sys 0m0.036s $ time ./empty.py real 0m0.113s user 0m0.024s sys 0m0.088s Итак, имеем следующие данные: 1. Строка в 100 000 000 символов создаётся в течении 0.024s . Что логично, учитывая что запись в ячейку памяти это 1 или 2 такта (не вспомню уже), то процессор может за секунду заполнить 6 гигабайт памяти (мы же заполняем значительно меньший объём, всего 100 Мб) 2. Строка в 100 000 000 символов переводится во множество в течении где-то 0.940s. По сути то же чтение и заполнение ячеек памяти, только + механизм вычисления хэш функции для каждой операции записи 3. А вот перебор строки в цикле ведёт к увеличению затрат ещё на 6 секунд. Теперь надо определить, эта задержка обусловлена циклом как таковым (расходами на его работу в питоне) или же она вызвана именно работой со строкой. Берём ещё файл (rng.py): Код: python 1. 2. 3. 4. 5. 6. 7. и меряем скорость перебора не строки, а списка: консоль$ time ./rng.py real 0m7.397s user 0m7.204s sys 0m0.200s Ничего не поменялось... всё те же 7 секунд (плюс-минус). Тут я сам был удивлён, потому что изначальное предположение по поводу строк оказалось ошибочным. Дело не в строке, а в цикле! Для перебора множества ситуация тоже не меняется. После этого я уже начал экспериментировать и нарвался на ожидаемый, но совсем неочевидный эффект. Смотрим файл rng2.py: Код: python 1. 2. 3. 4. 5. 6. Результат: консоль$ time ./rng2.py real 0m2.744s user 0m2.572s sys 0m0.172s ТАДАААААМ!!! Тернарный оператор for...in даёт нам оптимизацию вдвое (разумеется это так сильно заметно только из-за простоты выполняемых операций). Исходя из последнего опыта можно предположить, что очень много времени терялось на создании отдельного объекта строки "с" для каждой конкретной итерации. И при использовании тернарной операции похоже этого не происходит и каждое следующее значение передаётся напрямую в функцию добавления значения во множество. То есть вместо создания объекта строки для переменной "c", а потом ещё его дублирования для записи во множество, теперь происходит создание только одного экземпляра объекта, что изрядно экономит время :) P.S. Как помним, в питоне всё является объектами. Потому для ускорения работы очень больших циклов, похоже, следует использовать тернарные операции for...in по возможности. Проверял на практике, с числами та же хрень например. ======== больше похоже на подгон под ответ. 1)не важно сколько тактов занимает действие на микропроцессоре, шина обмена с памятью в раз 20 медленее, поэтому там что 2 что 5 тактов - побую, шина влюбом случае будет скорость лимитирующим фактором 2)один такт идёт на декодирование команды, адрес в память строится сегмент смещение, это два регистра выставить на микропроцессоре недо, плюс сама отправка - это если отослать байт в адресс. 3)но 2 сдесь бесмсленно - вы пишете на питоне, ядро которого написано на си наврено, влюбом случае не на ассемблере, так что рассуждать в такие дебри безперспективно 4)подозреваю что команда продублировать 100 000 000 раз один символ будет оптимизирована...ведь соверменная память умеет копировать участки без участия микропроцессора. ну тоесть микропроцессору не обязательно 100 млн раз отправлять байт в память по адресу. можно выделить память(скорей всего это не один сплошной кусок будет - но тут не важно) и оставить сразу строку сразу на 8 байт(для 64 битнйо системы) и начиная с этого момента, копировать куски оперативки в оперативку.. 5)ваши замеры на питоне как что работает безперспективны. условие -любое, это очень заратная операция для микропроцессора. очень затратная, легче 100 сложений сделать чем одно условие (я про код на ассемблере) - по сути, весь ваш код на питоне, наш на пхп и подобных языках медленее кода написаного толково и скомпиленого толковым компилятором на Си обуславливается именно этими условиями - что в том коде вы на прямую работаете с памятью, и любое действие с чтением присвоением - это работа с адресами в память, вто время как на интерпретируемых языках, это всё проходит через таблицу переменных, а там как ни крути условия будут - ну как минимум проверить наличие переменной, подходящесть типа... поэтому пытаться чтото сравнивать так дотошно на таком языке как питон - безперспективно. сравнивать можно различные языковые конструкции, делающие одно и тоже, но никак не то что насравнивали вы. 6) а рассуждения чесно говоря иногда удивляют...фраза - по сути тоже чтение только есчо вычисление хеш функции... тут какбы ситуация такая, что уже болт забить можно на чтение или не чтение...выщитывать хешфункцию - это основная задача -- и вот тут можете написать сравнительный тест, в милион ячеек в цикле записать числа от 1 до 1000000, тоже только рандомное число(сохранять всегда ввиде текста число) - и тоже, но хеш значение ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 13:32
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
автортак что делает конструктор и так понятно, но из чего он это делает? вот масив на вход он примет? а обьект по которому можно как по масиву пройтись??? насколько я понимаю, любой итерируемый объект. Код: python 1. 2. 3. 4. 5. 6. 7. 8. 9. автор-- и вот тут можете написать сравнительный тест, в милион ячеек в цикле записать числа от 1 до 1000000, тоже только рандомное число(сохранять всегда ввиде текста число) - и тоже, но хеш значение надо только не забывать, что простые числа в питоне кешируются ;) Код: python 1. 2. 3. 4. 5. 6. 7. 8. 9. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
25.10.2016, 20:54
|
|||
|---|---|---|---|
python: скорость выполнения set() для массива |
|||
|
#18+
alex564657498765453, некоторые замечания уместны, разумеется.... Некоторые не очень :) Но я это делал не для того, что бы ответ под результат подогнать. Я это делал что бы понять что именно столько времени пожирает в программе. И выяснил. По сути поставленную перед собой задачу выполнил и поделился с другими. Я тоже вроде как с питоном только начинаю знакомиться, потому такие моменты меня очень интересуют... и помогают глубже вникнуть в происходящее. Насчёт копирования памяти. Возможно Вы правы, но я сомневаюсь (погуглю как-нить на досуге). Дело в том, что такое поведение оперативной памяти сделало бы её асинхронной, а это явное усложнение процесса программирования, потому как изменение некоторой ячейки или ячеек памяти и использование установленного значения в следующей команде могло бы завершиться ошибкой логики или вообще выбросом исключения. Это бы привело к необходимости проверять готовность памяти перед каждой операцией её чтения или записи. :) Мне при ознакомлении с ассемблером хватило этих глупостей с проверкой готовности результатов от сопроцессора (числа с плавающей точкой) и готовности данных при работе со считывающими или запоминающими устройствами (жёсткие диски, CD привод и т.д.) Знаете... это не лучшая практика... и актуальна только в случаях экономии большого количества времени. Заполнение больших структур данных в оперативной памяти является нечастым, а скорость этой операции итак достаточно высокая, потому очень сомневаюсь что кто-то будет вводить такие сложности ради иллюзорной экономии процессорного времени. "тут какбы ситуация такая, что уже болт забить можно на чтение или не чтение...выщитывать хешфункцию - это основная задача" - так и есть... потому то она и занимает в 40 раз больше времени чем обычная запись. Не понимаю суть замечания... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|

start [/forum/topic.php?fid=23&mobile=1&tid=1460875]: |
0ms |
get settings: |
5ms |
get forum list: |
9ms |
check forum access: |
2ms |
check topic access: |
2ms |
track hit: |
15ms |
get topic data: |
6ms |
get forum data: |
1ms |
get page messages: |
29ms |
get tp. blocked users: |
1ms |
| others: | 214ms |
| total: | 284ms |

| 0 / 0 |
