Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 213 additions & 0 deletions Урок 3. Практическое задание/task_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,216 @@
то реализуйте ф-цию-декоратор и пусть она считает время
И примените ее к двум своим функциям.
"""
from functools import wraps
from time import perf_counter
import sys


def timer(fun):
@wraps(fun)
def wrapper_timer(*args):
t0 = perf_counter()
ret = fun(*args)
dt = perf_counter() - t0
a0 = args[0]
# Чтобы, если аргументом является массив,
# печаталась бы только его длина
try:
desc = f"(<{len(a0)}>,...)"
except TypeError:
desc = f"({a0},...)"
print(f"{fun.__name__}{desc}: {dt:.4f} s")
return ret
return wrapper_timer


# Проверим скорость заполнения списка и словаря
# с помощью for и list comprehension
@timer
def fill_list(n):
ret = []
for i in range(n):
ret.append(i)


@timer
def fill_list_comp(n):
[i for i in range(n)]


if sys.argv[1] == "1":
for k in (4, 5, 6, 7):
fill_list(10**k)
for k in (4, 5, 6, 7):
fill_list_comp(10**k)

# Результаты:
# fill_list(10000,...): 0.0008 s
# fill_list(100000,...): 0.0075 s
# fill_list(1000000,...): 0.0737 s
# fill_list(10000000,...): 0.7417 s
#
# fill_list_comp(10000,...): 0.0004 s
# fill_list_comp(100000,...): 0.0044 s
# fill_list_comp(1000000,...): 0.0478 s
# fill_list_comp(10000000,...): 0.4943 s
#
# Видно, что list comprehension дает почти двукратное ускорение
# для списков. Это можно объяснить возможностью заранее выделить
# пямять для итогового списка и избежать перераспределения памяти.


@timer
def fill_dict(n):
ret = {}
for i in range(n):
ret[i] = True


@timer
def fill_dict_comp(n):
{i: True for i in range(n)}


if sys.argv[1] == "2":
for k in (4, 5, 6, 7):
fill_dict(10**k)
for k in (4, 5, 6, 7):
fill_dict_comp(10**k)

# Результаты:
# fill_dict(10000,...): 0.0006 s
# fill_dict(100000,...): 0.0083 s
# fill_dict(1000000,...): 0.1145 s
# fill_dict(10000000,...): 1.0419 s
#
# fill_dict_comp(10000,...): 0.0006 s
# fill_dict_comp(100000,...): 0.0081 s
# fill_dict_comp(1000000,...): 0.0985 s
# fill_dict_comp(10000000,...): 0.9942 s

# То, что list comprehension не приводит к ускорению
# заполнения словаря говорит о том, что перераспределение
# памяти происходит в любом случае, т.к. размер
# хеш-таблицы словаря изменяется из-за коллизий,
# которые невозможно предугадать заранее.

# Время заполнения словаря с ключами в виде целых чисел
# отличается от времени заполнения равного по размеру списка
# всего на четверть. Поскольку для целого n справедливо
# условие hash(n)==n, при заполнении последовательностью
# range() dict и list становятся алгоритмически эквивалентны.
#
# Проверим, как изменится время заполнения для строчных ключей:


@timer
def fill_list_str(n):
ret = []
for i in range(n):
ret.append(str(i))


@timer
def fill_dict_str(n):
ret = {}
for i in range(n):
ret[str(i)] = True


if sys.argv[1] == "3":
for k in (4, 5, 6, 7):
fill_list_str(10**k)
for k in (4, 5, 6, 7):
fill_dict_str(10**k)

# fill_list(10000,...): 0.0023 s
# fill_list(100000,...): 0.0270 s
# fill_list(1000000,...): 0.3104 s
# fill_list(10000000,...): 2.6481 s
#
# fill_dict(10000,...): 0.0027 s
# fill_dict(100000,...): 0.0339 s
# fill_dict(1000000,...): 0.4624 s
# fill_dict(10000000,...): 5.8220 s

# Теперь разница составляет уже два раза.
# Предположительно это связано с большей вероятностью
# коллизий и, соответственно, большим необходимым
# объемом памяти:
#
# >>> n=10000000
# >>> len(set(hash(i) % n for i in range(n)))
# 10000000
# >>> len(set(hash(str(i)) % n for i in range(n)))
# 6319569


@timer
def remove_first(lst):
lst.pop(0)


@timer
def insert_first(lst):
lst.insert(0, 0)


if sys.argv[1] == "4":
for k in (4, 5, 6, 7):
lst = list(range(10**k))
remove_first(lst)
for k in (4, 5, 6, 7):
lst = list(range(10**k))
insert_first(lst)

# remove_first(<9999>,...): 0.0000 s
# remove_first(<99999>,...): 0.0001 s
# remove_first(<999999>,...): 0.0012 s
# remove_first(<9999999>,...): 0.0123 s
# insert_first(<10001>,...): 0.0000 s
# insert_first(<100001>,...): 0.0001 s
# insert_first(<1000001>,...): 0.0053 s
# insert_first(<10000001>,...): 0.0130 s
#
# Время удаления и добавления элемента в начало списка
# пропорционально его длине из-за того, что такое
# удаление вызывает перемещение блока памяти.


@timer
def list_append(lst):
lst.append(0)


@timer
def list_pop(lst):
lst.pop()


@timer
def dict_insert(d):
d[0] = 0


@timer
def dict_del(d):
del d[0]


if sys.argv[1] == "5":
n = 10000000
lst = list(range(n))
d = {i: True for i in range(n)}
list_append(lst)
list_pop(lst)
dict_insert(d)
dict_del(d)

# list.append(), list.pop(), dict[] и dict[]= по очевидной
# причине дают близкое к нулю время
#
# list_append(<10000001>,...): 0.0003 s
# list_pop(<10000000>,...): 0.0000 s
# dict_insert(<10000000>,...): 0.0000 s
# dict_del(<9999999>,...): 0.0000 s
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а как же операции поиска элементов и их получения?

104 changes: 104 additions & 0 deletions Урок 3. Практическое задание/task_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,107 @@
Введите пароль еще раз для проверки: 123
Вы ввели правильный пароль
"""

import sqlite3
import sys
from textwrap import dedent
from random import choice, randint
from hashlib import pbkdf2_hmac


# Класс для удобства работы с базой данных
class SQLite():
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQLite- не самое удачное имя для СВОЕГО класса

def __init__(self, file='passwd.db'):
self.file = file

def __enter__(self):
self.conn = sqlite3.connect(self.file)
self.conn.row_factory = sqlite3.Row
return self.conn.cursor()

def __exit__(self, type, value, traceback):
self.conn.commit()
self.conn.close()


# Генерирует имя пользователя из букв,
# следит за чередованием гласных и согласных
def gen_login(maxlen=10):
SONANTS = 'aeiou'
CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
i1 = randint(0, 1)
i2 = randint(i1 + 3, maxlen)
result = []
for i in range(i1, i2+1):
arr = SONANTS if i % 2 == 0 else CONSONANTS
result.append(choice(arr))
return "".join(result)


# Генерирует пароль из произвольных символов
def gen_passwd(n=5):
return "".join(chr(randint(33, 126)) for _ in range(n))


# Генерирует таблицу (логин, пароль)
def gen_db(size=10):
return [
(gen_login(), gen_passwd())
for i in range(size)
]


def hashit(login, passwd):
scrambled = "".join(
[x for i, c in enumerate(login) for x in (c, login[-i-1])]*3)
saltb = scrambled.encode()
passb = passwd.encode()
return pbkdf2_hmac(
hash_name='sha256',
password=passb,
salt=saltb,
iterations=100000
).hex()


def reset_db():
db = gen_db()
with open("passwd.txt", "w") as fo:
for login, passwd in db:
fo.write(f"{login}\t{passwd}\n")
data = [
(login, hashit(login, passwd))
for login, passwd in db
]

with SQLite() as cur:
try:
cur.execute(dedent("""\
DELETE FROM users"""))
except sqlite3.OperationalError:
cur.execute(dedent("""\
CREATE TABLE users
(login text, hash text)"""))

cur.executemany(dedent("""\
INSERT INTO users
VALUES (?, ?)"""), data)


def check_passwd(login, passwd):
with SQLite() as cur:
cur.execute('SELECT * FROM users WHERE login=?', (login,))
res = cur.fetchone()
if res is not None:
if hashit(login, passwd) == res[1]:
print("Успешный вход в систему")
return None
print("Неправильный логин или пароль")


if len(sys.argv) > 2:
check_passwd(sys.argv[1], sys.argv[2])
elif len(sys.argv) > 1 and sys.argv[1] == "--reset":
reset_db()
else:
print("usage: task_2.py <uname> <passwd> OR task_2.py --reset")
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

выполнено
плюс за БД

17 changes: 17 additions & 0 deletions Урок 3. Практическое задание/task_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,20 @@
р
а
"""


def count_substrings(s):
bag = set()
for i in range(len(s)):
for j in range(i+1, len(s)+1):
bag.add(hash(s[i:j]))
return len(bag)-1


for s in ("bbbb", "baba", "bcde"):
print(f"{s} -> {count_substrings(s)}")

# ---output---
# bbbb -> 3
# baba -> 6
# bcde -> 9
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

выполнено

Loading