Try   HackMD

Словники (dictionaries)

Задача

Потрібно зашифрувати текст підстановкою символів. Для кожної букви алфавіту є відповідна буква шифрованого алфавіту. Наприклад, букви

replace_from = "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя' "

потрібно перетворити у відповідні символи:

replace_to   = "he*vd%qa ptkmj+gfx'rylzi@!ubwsc$=no"

Великі букви перетворити у маленькі, потім зробити заміну букв, а якщо якихось символів немає в першому списку, просто проігнорувати їх.

Приклад перетворення:

Щурячий бугай із їжаком-харцизом в'ючись підписали ґешефт у єнах.
=>
siy=btjoeivhjokpom h+'f!hyutp'fo*n$btlcork%rtlhgtodqwq@zoioaxh!

Оскільки дефісу і крапки немає в коді шифрування, результат став на 2 символи коротшим.

Варіант 1

За допомогою великої кількості elif letter == '...': перебрати всі варіанти букв:

replace_from = "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя' " replace_to = "he*vd%qa ptkmj+gfx'rylzi@!ubwsc$=no" def encode_1(message): message = message.lower() # по завданню треба перетворити у маленькі букви result = "" for letter in message: if letter == 'а': result += 'h' elif letter == 'б': result += 'e' elif letter == 'в': result += '*' elif ... return result

Цей спосіб буде працювати, якщо повністю записати всю таблицю. Але це дуже і дуже довго.

Варіант 2

Можна автоматизувати пошук відповідників за допомогою .index:

replace_from = "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя' " replace_to = "he*vd%qa ptkmj+gfx'rylzi@!ubwsc$=no" def encode_2(message): message = message.lower() # по завданню треба перетворити у маленькі букви result = "" for letter in message: # якщо буква недоступна для шифру, то ігноруємо її if letter not in replace_from: #print(f'Ignoring letter {letter}') continue # дістати індекс букви з списку шифрування index = replace_from.index(letter) # дістати відповідний шифрований символ symbol = replace_to[index] result += symbol return result

Це уже кращий варіант, бо не треба перебирати всі варіанти. Але можна зробити ще краще.

Варіант 3

В Пайтоні є тип даних "словник", який дозволяє записувати пари "ключ"-"значення", і робити пошук значення по ключу. Словник записується в фігурних дужках ({ }), схоже як список записується в квадратних дужках ([ ]).

replace_dict = {
        'а': 'h',  'б': 'e',  'в': '*',  'г': 'v',  'ґ': 'd', 
        'д': '%',  'е': 'q',  'є': 'a',  'ж': ' ',  'з': 'p', 
        'и': 't',  'і': 'k',  'ї': 'm',  'й': 'j',  'к': '+', 
        'л': 'g',  'м': 'f',  'н': 'x',  'о': "'",  'п': 'r', 
        'р': 'y',  'с': 'l',  'т': 'z',  'у': 'i',  'ф': '@', 
        'х': '!',  'ц': 'u',  'ч': 'b',  'ш': 'w',  'щ': 's', 
        'ь': 'c',  'ю': '$',  'я': '=',  "'": 'n',  ' ': 'o'
    }

Пари ключ-значення треба записувати через двокрапку і розділяти комами.

Після цього можна взнати значення по ключу через replace_dict.get():

symb = replace_dict.get('й')
print(symb)  # виведе j

Якщо символа немає серед ключів, повернеться "нічого"

symb = replace_dict.get('A')  # такого ключа немає
print(symb)  # виведе None

Другий аргумент у replace_dict.get дозволяє задати дефолтне значення:

symb = replace_dict.get('A', "NO")  # такого ключа немає
print(symb)  # виведе "NO"

Все це разом дозволяє зробити ще одну версію шифрувальної машини:

def encode_3(message): message = message.lower() result = "" for letter in message: symbol = replace_dict.get(letter, "") result += symbol return result

Ще задачі

Дешифрування

Розкодуйте шифрограму xqo hyzijzqoekgcwqory'ohe'ei, використавши словник дешифрування:

decode_dict = { 'h': 'а', 'e': 'б', '*': 'в', 'v': 'г', 'd': 'ґ', '%': 'д', 'q': 'е', 'a': 'є', ' ': 'ж', 'p': 'з', 't': 'и', 'k': 'і', 'm': 'ї', 'j': 'й', '+': 'к', 'g': 'л', 'f': 'м', 'x': 'н', "'": 'о', 'r': 'п', 'y': 'р', 'l': 'с', 'z': 'т', 'i': 'у', '@': 'ф', '!': 'х', 'u': 'ц', 'b': 'ч', 'w': 'ш', 's': 'щ', 'c': 'ь', '$': 'ю', '=': 'я', 'n': "'", 'o': ' ' }

Коли він народився

plato = { "name": "Plato", "country": "Ancient Greece", "born": -427, "teacher": "Socrates", "student": "Aristotle" }

Коли народився Платон?

Заміна ключів

Значення словника можна змінювати, і можна додавати/видаляти ключі.

plato["importance"] = 2 # додати новий ключ. Значення по 5-бальній шкалі plato["born"] = -428 # змінити значення ключа del plato["student"] # видалити ключ-значення

Це додасть нову пару ключ-значення в слоник plato.

Замініть ім'я у Платона на "Kanye", додайте прізвище "West", замініть країну на USA, рік народження на 1977, видаліть вчителя і студентів. Додайте ключ "importance" і придумайте йому значення.

Вивести словник гарно

Вивести словник гарно на екран. Наприклад, словник { 'a': 10, 'b': 20, 'c': 30 } при виводі повинен виглядати як:

a -- 10
b -- 20
c -- 30

Для цього доведеться "пройтись" по всім парам ключ-значення в словнику. Це можна зробити так:

my_dict = { ... }

for key, value in my_dict.items():
    # key та value -- це тепер відповідні ключ та значення
    print(key)
    ...

Юзвери

Зробити консоль адміна. В консолі можна додавати нових юзверів в систему.

Спочатку програма питає: "хочеш додати ще одного юзвера?". Якщо адмін відповідає "так", то програма питає окремо логін юзвера, пароль юзвера і записує його у словник.

При запису у словник потрібно в якості ключа вказати логін, а в якості значення - пароль.

Якщо адмін відповідає "ні", то програма виводить словник юзверів на екран, красиво.

Banlist в Minecraft

Гравці на сервері Майнкрафту зберігаються в одному великому словнику. Ключем є ID гравця, а значенням об'єкт гравця. Ось приклад об'єктів у цьому словнику:

class Player: pass # id 1 player1 = Player() player1.name = "vasia" player1.is_banned = False # id 2 player2 = Player() player2.name = "petia" player2.is_banned = True player2.ban_reason = "мати, гріф" player2.ban_till = 1619540031 # id 3 player3 = Player() player3.name = "ksiusha" player3.is_banned = True player3.ban_reason = "чітер, волхацкер" player3.ban_till = 1619531049 users = { 1: player1, 2: player2, 3: player3, }

Потрібно написати функцію def send_msg(user_id, msg), яка робить прінт повідомлення на екран, але тільки якщо юзер не забанений. Якщо юзер забанений, написати скільки лишилось часу до розбану.

Як взнати скільки лишилось часу? Потрібно взнати поточний час (time.time() з бібліотеки time), і відняти його від параметру player.ban_till (доколи забанено). Якщо результат додатній, значить юзер ще в бані, якщо від'ємний, то бан треба знімати і повідомлення можна прінтити.

Створення словника заміни

Маючи два рядки replace_from та replace_to зробити словник заміни replace_dict. Алгоритм такий:

  1. Створити порожній словник replace_dict
  2. Пройтись по всім індексам першого списку
  3. Отримати відповідні букви з обох списків
  4. Додати ці букви як пару ключ-значення у словник
  5. Після циклу повернути результат

Словник частот

Нехай є текст:

big_text = """
Class for timing execution speed of small code snippets.

The constructor takes a statement to be timed, an additional statement used for setup, and a timer function. Both statements default to 'pass'; the timer function is platform-dependent (see the module doc string). stmt and setup may also contain multiple statements separated by ; or newlines, as long as they don’t contain multi-line string literals. The statement will by default be executed within timeit’s namespace; this behavior can be controlled by passing a namespace to globals.

To measure the execution time of the first statement, use the timeit() method. The repeat() and autorange() methods are convenience methods to call timeit() multiple times.

The execution time of setup is excluded from the overall timed execution run.

The stmt and setup parameters can also take objects that are callable without arguments. This will embed calls to them in a timer function that will then be executed by timeit(). Note that the timing overhead is a little larger in this case because of the extra function calls.
"""

Потрібно порахувати скільки разів яка буква зустрічається. Алгоритм:

  1. Створити порожній словник. В ньому ключами будуть букви, а значеннями їх кількість
  2. Проходитись по всім буквам тексту
  3. Отримати значення по ключу. Врахувати, що ключа може не бути
  4. Замінити значення ключа на збільшене на одиницю.
  5. Після циклу вивести гарно словник не екран

Сума значень

Маючи словник частот (наприклад, з попереднього завдання) порахувати суму всіх значень. (Для перевірки: вона повинна дорівнювати довжині ориганільного тексту)

Словник дешифрування

Якщо у нас є словник заміни, як розкодувати шифроване повідомлення? Потрібно зробити аналогічний словник і зробити "шифрування", але тепер іншим словником.

Потрібно словник:

replace_dict = {
    'а': 'h', 
    'б': 'e', 
    'в': '*', 
    'г': 'v', 
   ... 
}

перетворити у

reverse_dict = {
    'h': 'а', 
    'e': 'б', 
    '*': 'в', 
    'v': 'г',
   ... 
}

Зробити функцію decode і дешифрувати за її допомогою повідомлення.

Мода

Мода - це ключ, у якого найбільше значення у словнику частот. Окрім моди, зручно також знати топ-5, або топ-10 з цього словника.

Нехай є словник частот (наприклад, з попередніх завдань). Потрібно:

  1. Знайти моду - букву, яка зустрічається найчастіше
  2. Топ-10 - найчастіші букви в тексті
  3. Суму всіх значень
  4. Сума значень 10 найчастіших елементів та який відсоток від всіх вони складають.

Медіана

Маючи словник частот, потрібно знайти топ-50% - мінімальний набір ключів, чиї значення в сумі дають 50% всіх значень таблиці.