Не так давно компания MixBytes проводила конкурс, пройдя который можно было попасть на их курс аудитора смарт-контрактов
Здесь публикую свой разбор этого теста

1-е задание
Ответ

Я не профи в питоне. Поэтому изначально полез гуглить что вообще тут за функции. Первое, что бросается в глаза: random.seed() при одинаковом параметре будет возвращать одинаковый результат.

Тут есть 2 подхода как найти решение: понять код самостоятельно или прогнать в компиляторе\интерпретаторе.

Начну с самостоятельного понимания.
Почитав про range и random в питоне, становится ясно: последняя строчка генерирует результат в диапазоне от 0 до (1e12)-1. Т.е всего 1e12 вариантов

Кто не понял что такое 1e12 - это тоже вполне гуглится

Честно говоря, давно с формулами теории вероятности дело не имел. Могу предположить, что дальнейшее повествование приведёт к правильному ответу основываясь на неверных вычислениях: как в примере со сломанными часами, которые 2-жды в сутки показывают правильное время. Если где-то ошибся - надеюсь, читатели поправят.
Для полноты картины полезно было бы почитать про расчёт вероятности зависимых и независимых событий... Но, можно рассуждать по-другому.
Вероятность p выпадения любого числа в выше названном промежутке = 1/1e12
С учётом фиксированного random.seed(), вероятность 10 раз получить одинаковое число (не важно какое именно) = 100% (т.е. 1)

Итого: p = 1/1e12 * 1 = 1/1e12

Если хочется считать через зависимые\независимые события, то можно понять, что с зафиксированным random.seed() события нельзя считать зависимыми: на любом шаге будет выпадать то же число (с вероятностью 1/1e12), что и на любом другом шаге. Т.е. одна и та же вероятность на каждом шаге 1/1e12 складывается 10 раз и делится на количество вызовов (10)

Т.е. формула: p = ((1/1e12) * 10)/10
Т.е. итог: 1/1e12

С использованием онлайн интерпретатора (гуглятся и находятся несколько разных без проблем) ещё интересней.
Чтоб поиграться с кодом, чутка переделаем его в:

import random

def generate():
    random.seed(12345)
    print("res: ", random.choice(list(range(int(1e12)))))
    
generate()

При попытке его запуска получим ошибку:
** Process exited due to resource limitations **

Но, играя со степенями 1e можно заметить закономерность:

1e1 = 6
1e2 = 53
1e3 = 426
1e4 = 6825

И т.д. Значения всегда только увеличиваются. Из этого вывод: если бы код работал при 1e12 вероятность была бы = 0.

Итого: у задачи фактически 2 ответа, смотря с какой стороны подходить к решению: 1/1e12 и 0

Как выяснилось, любой из этих 2-х ответов засчитывался.

2-е задание
Ответ

На мой взгляд, самое простое задание. Настолько простое, что даже не знаю: что тут объяснять... sqrt() - функция квадратного корня. ^ - возведение в степень.
Самостоятельно написав код по формуле я увидел, что ответ будет - первый вариант. Строго соблюдая ту же последовательность вычислений, что в формуле.

3-е задание
Ответ

unsigned - без знаковое. Значит, при вычислениях отбрасываем значения после запятой.

a1 = 1234 / 123 = 10a2 = 1234 / 12 = 102a1 = 10 *  123 = 1230a2 = 102 * 12 = 1224

Итого: 4 вариант

4-е задание
Ответ

На эту задачу я изначально неверно ответил. Но, ход мыслей был верный.

26 букв одного регистра. Значит, верхнего и нижнего регистра 26 *2 = 52. Добавим цифры (0...9). Всего 62
Теперь посмотрим на эти 2 строчки:

12345678
yo

Сколько различных комбинаций, где yo будет на разных местах? Двигая yo можно увидеть, что их 7.

Если из 8 символов нужно перебрать оставшиеся 6 (т.к. yo - это 2 символа) - получим:

62^6

Учитывая, вышесказанное про 7 различных позиций yo в строке из 8 символов получим:

62^6*7

Т.е. это 5 вариант - как раз мной выбранный. Но, тут ошибка. Верным считался ответ - 6 вариант:

62^6*7-1

Дело в том, что если перебрали 62^6*7-1 и хеш не совпал, то последний должен совпасть автоматически и его не нужно для этого считать.

Были ещё высказаны верные замечания различными участниками, что задачка всё равно не однозначная. Допустим, если на 1-м шаге перебраны все значения yo******, то на 3-м (**yo****) не нужно считать вариант yoyo****, т.к. он уже был на 1-м шаге. Авторы теста согласились, что это в логике задачи не учитывалось.

5-е задание
Ответ

Начнём считать все варианты функции начиная с 0

x(0) = 0x(1) = 1x(2) = x(1) + x(0) + 3 = 4x(3) = x(2) + x(1) + 3 = 8x(4) = x(3) + x(2) + 3 = 8 + 4 + 3 = 15x(5) = x(4) + x(3) + 3 = 15 + 8 + 3 = 26x(6) = x(5) + x(4) + 3 = 26 + 15 + 3 = 44x(7) = x(6) + x(5) + 3 = 44 + 26 + 3 = 73x(8) = x(7) + x(6) + 3 = 73 + 44 + 3 = 120x(9) = x(8) + x(7) + 3 = 120 + 73 + 3 = 196

Итого: ответ - 2 вариант

6-е задание
Ответ

Сама задача не вызвала вопросов. А вот понять ответы сходу было проблемно. Ясно было одно: ответы смотреть нужно из вариантов: 5-7. Т.к. по условиям даже одна строка не факт, что поместится в память. Значит, в память загружать блоками, которые точно влезут в память. И важно прочесть условие задачи: найти наибольшее число строк, идущих подряд. А не все одинаковые строки без разбора на какой позиции

Итеративный хеш на примере простой строки: 12345\n
Пусть блок N = 1
Считаем хеш от 1: h1 = h(1)
Следующий хеш: h2 = (h(1)+'2')
Последний хеш: h6 = (h(5)+'\n')
h6 будет уникальным хешем для строки, который получили итеративно. Если строка повторилась Х раз - значит, Х раз будет повторяться хеш h6. Считаем Х - максимальным числом (т.е. счётчиком повторений), пока не найдётся ситуация, где итеративный хеш для строки повторится Y раз, где Y > Х . В этом случае запишем Y. И т.д. По этому описанию подходит 7 вариант


8-е задание
Ответ

Сначала стоит разобраться с APR и APY. Сейчас я не могу найти хорошей статьи, которую нашёл в процессе решения задачи. Но, помню, что APY - это сложные проценты. Т.е. как раз тот случай, когда можно накопленное за период (период для Коли - месяц, для Пети - день), перезакладывать на следующий период

С калькулятором считать замучаешься. Набросав код на Solidity и проверив его в Remix для относительно больших чисел (например, 10000000000). Получим 4-й вариант.

contract dop {

    function monthly(uint256 num) public pure returns (uint256) {
        for (uint i = 0; i < 12; i++)
        {
            num = num + (num*2)/120; // 2/120 это 20% и 12 месяцев
        }
        return num;
    }

    function daily(uint256 num) public pure returns (uint256) {
        for (uint i = 0; i < 360; i++)
        {
            num = num + (num*20)/36000; // 20% и 360 дней
        }
        return num;
    }
}

9-е задание
Ответ

С этим я провозился больше всего. Пришёл 1 нормальный вариант: в стартовом вагоне включить свет, в любую сторону идти до вагона, где свет включен, выключить его и проверить выключился ли в стартовом (т.е. возвращаться, запоминая сколько вагонов прошёл). Повторять процедуру пока в стартовом свет не погаснет.

А где же 7-е задание?

Авторы задания признали его неверным

Комментарии (1)


  1. Shaman_RSHU
    25.08.2022 11:04

    А где здесь "Информационная безопасность"? Уж "Криптография" с натяжкой можно.