Задать вопрос
  • Как получилась запись в квадратных скобках?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    2 комментария
  • Как решить эту задачу?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Проверьте для мелких чисел сначала.
    2,5 - 2
    4,25 - 3
    8,125- 4
    16,625 - 5.

    Вроде как напрашивается паттерн, что ответ в вашей задаче 2021.

    Почему так? Его можно доказать. Как Rsa97 уже написал, количество цифр - log_10, округленный вниз+1. Пусть k=2020, тогда ваш ответ:

    2+floor(log(2^k))+floor(log(5^k)).

    Если бы floor не было, учитывая log(a)+log(b) = log(ab), то мы бы получили 2+log(10^k), что было бы 2+k. Но у нас есть floor, каждый из которых уменьшает занчение на число от 0 до 1, больше 0, но меньше 1 (ведь ни 2 ни 5 ни в какой спени не дадут степень 10 - log дает нецелое число). Итого - ответ меньше чем k+2 на некое число, от 0 до 2 (невключительно) и целое. Такое число только одно - k+1.
    Ответ написан
  • Поможет ли функциональный ЯП (например, Haskell) лучше понять ООП (С++)? Если да, то чем конкретно он поможет?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Нет. Скорее помешает.

    Это принципиально разные парадигмы. В функциональной парадигме основа - чистые функции, никакого стейта. ООП основано на внутреннем состоянии классов, которое постоянно меняется.
    Ответ написан
    2 комментария
  • Почему у меня не выводится текст в файле?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    У вас путь к файлу аж в трех местах написан!

    Вы попробуйте только в одном месте путь передавать (или в конструкторе ofstream без вызоыва open() или используйте конструктор без параметров, но тогда оставьте open). И используйте string, раз уж его завели. И сделайте его const string заодно.
    Ответ написан
    Комментировать
  • Что получится в результате выполнения блок-схемы?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Первый цикл читает входные данные в массив x. Ваши "1, 1, 1, 0, 0, 1, 0, 1, 1" будут в этом массиве (только нужно сначала 9 ввести в качестве n).

    Поэтому проверка на 1 имеет смысл и даст истину для первых трех итераций, но не для двух следующих, и т.д.
    Ответ написан
  • Как выполнить проверку if в функции только 1 раз при первом вызове?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Только написать 2 функции: одну с проверкой, другую без. И вызывать первую только один первый раз, а потом вторую. Если вызов в цикле, то надо развернуть цикл - первую итерацию вытащить перед циклом и гнать цикл со второй итерации.

    Но эта оптимизация будет практически бесполезна во всех случаях. Тупо увеличение кода может изменить попадания в кэш и замедлить вашу программу гораздо сильнее.
    Ответ написан
    Комментировать
  • Как реализовать приоритетную очередь?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Пока вижу проблему, которая 100% приведет к тайм-лимиту. Вы при изменении приоритета ищите по всей очереди. Если вам дадут 500000 добавлений в очередь, а потом 500000 уменьшений случаного элемента, то ждать вы завершения программы будете очень долго.

    Надо поддерживать массив позиций по по всем айдишникам. При любом перемещении пары элементов в массиве в очереди надо обновлять этот массив позиций.

    При изменении приоретета уже не надо никакого find, потому что позиция уже будет доступна в массиве.

    При добавлении нового элемента не надо вызывать build_heap. достаточно только shift_up от нового элемента. У вас в программе отдельно этой функции нет, но она фактически реализована в decrease_key. Только нужно, опять же, проходится не по всей очереди, а только по отцам. В цикле надо делать не i--, а i = (i+1)/2-1.

    И ошибка как раз в decrease_key, У вас цикл по i, а внутри все время используется k.
    Ответ написан
    5 комментариев
  • Корректен ли данный код, возможна ли оптимизация?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    В целом неплохо, оптимизировать тут нечего, ибо программа тривиальная и работает так быстро как это только возможно.

    По коду есть комментарии:
    1)
    if ((valVAT == 10) || (valVAT == 18) || (valVAT == 20)) {
    ...


    Тут у вас один большой мега-if в котором что-то делается. Гораздо проще для понимания и визуально читабельнее, если делать "ранний выход". Вместо if(a) { много кода } стоит писать:
    if (!a) {
      continue; // или return; если это в функции.
    }
    // много кода.


    У вас стоит сначала проверить, что valVAT == 0 и выйти из цикла через break в этом случае. Потом проверить, что valVat != 10 && valVAT != 18 && valVAT != 20 и вывести сообщение об ошибке и сделать continue. Дальше уже идет тело цикла с вычислениями.

    2) Вместо if(chng == 1) {} else if (chng == 2) {}... стоит использовать конструкцию
    switch (chng) {
    case 0:
      // код
      break;
    case 1:
      // код
      break;
    case 2:
      // код
      break;
    default:
      // сообщение об ошибке
      continue;
    }


    3)
    sleep(1) после вывода сообщения об ошибке, на мой взгляд не нужен. Зачем это? Заставить пользователя прочитать сообщение об ошибке?
    Ответ написан
    4 комментария
  • Почему добавляется лишний символ в массив?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Вы не копируете '\0' на конце. Поэтому после приписывания идет мусор. И так получается, что у вас в памяти там мусор "v\0".

    Вторая проблема, вы пишите в строку s, не меняя ее размер.
    char s[] = "abc"; выделяет ровно столько памяти, сколько нужно для "abc", да еще и на стеке, раз у вас переменные локальные.

    Вы при дописывании чего-то к строке затираете другие переменные. Так у вас совпало, что после name идет surname на стеке. Поэтому вы переписываете память второй строки (но без первого символа, который занял место '\0' в name. Именно поэтому мусор в конце - это неперезаписанный конец surname "v\0". Если поменять местами переменные, то вы может стек перепишите и у вас программа вообще упадет.

    Вам надо строки выделять с запасом: char name[1000] = "Ivan";
    Ответ написан
    Комментировать
  • Как создать радиус в любое расстояние м/км расположенного вокруг координаты и получить список этих координат?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Пусть координаты x0,y0. Расстояние можно считать по этой формуле (вики).

    Вам придется или скопировать код по ссылке и переписать на питоне, или использовать этот пакет.

    Дальше, вам надо начиться отступать от заданной точки на север или восток на 1 метр. Это можно или медетировать над формулой выше а можно просто запустить бинарный поиск, который будет искать изменение широты или долготы. Пусть d - сколько вам надо отложить в метрах, R - радиус земли в метрах. Тогда ищите изменение на отрезке (0, 4*r*180/(2*Pi*R)). Пробуйте откладывать текущее значение по широте или долготе, считайте расстояние по формуле и, если оно больше d, заменяйте верхнюю границу отрезка на середину, иначе заменяйте нижнюю границу. Остановите бин.поиск, когда отрезок станет достаточно мелким (например <1e-10).

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

    Сначала сгенерируйте точки на одной вертикале с начальной. Для этого повторно откладывайте 1м на север и юг от нее, пока расстояние до (x0,y0) не превысит ваш радиус. Бинпоиск достаточно запустить ровно один раз в самом начале и дальше именно это приращение откладывайте вверх и вниз.

    Потом аналогичным образом откладывайте от каждой сгенеренной точки новые точки на запад и восток. Сначала для каждой точки бинпоиском найдите нужное приращение по долготе. Потом откладывайте его влево и вправо, пока не выйдите за радиус от (x0,y0).
    Ответ написан
  • Вычитание в прямом коде?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Лучше вычитать в обратном коде, через сложение. Или вычитать меньшее из большего.

    Но если вам так хочется, то можно вычитать, пока не кончатся разряды.

    00000100
    -
    00000111
    =
    *1111101

    Но вот проблема, в последнем разряде (где у меня стоит *) на самом деле должна быть -1. просто больше не откуда заимствовать.

    Если же мы сделаем это заимствование, то получим X=11111101 и -1 в следующем, не существующем разряде.

    Это фактически и есть уже число в обратном коде! Если взять битовое НЕ и прибавить 1, то мы получим 0011 = 3, как и должны были. Ведь само число - -3.

    Но почему так получается? Ваше число на самом деле X - 2^8 (потому что 1 стоит в следующем, не существующем разряде).
    Или - (2^8-X). Побитовое НЕ. это просто вычитание из 111...111. Т.е. ~X=(2^8-1) -X.

    Остюда 2^8-X = ~X+1.

    Т.е. ваше число на самом деле ~X+1 по модулю.
    Ответ написан
    Комментировать
  • Как рассчитать "похожесть" двух словарей?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Если отсутствие слова в словаре равносильно слову с весом в 0, то можно считать какую-угодно меру от векторов чисел. Хоть корень из суммы квадратов разностей по каждому слову.

    В вашем примере это будет (1-2)^2+(2-2)^2+(3-0)^2+(1-0)^2 = 11.
    Чем меньше это число, тем похожее словари. Можно ее еще как-то нормировать, поделив на, допустим количество уникальных ключей в обоих словарях. Или на количество всевозможных слов.

    Если ваш язык/структура позволяет пройтись по словарю в лексикографическом порядке, то можно подсчитать такую меру за линейное время выполняя что-то вроде слияния сортированных списков. Изначально 2 указателя на минимальные элементы (по словарю) в каждом словаре. Если два элемента с одинаковым ключем, то считайте разность двух весов и двигайте оба указателья. Иначе считайте разность веса с минимальным ключем и 0 и двигайте только этот указатель. Случай, когда один из словарей уже пуст совпадает со вторым случаем.

    В питоне позволяет обходить ключи по порядку OrderedDict.
    Ответ написан
    Комментировать
  • БлэкДжек, вероятность комбинации, как посчитать?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Проблема у вас, похоже, что разные комбинации можно получить с разными вероятностями.

    Допустим, 19 = 2+2+5+5+5. Но можно же переставлять карты местами. Ведь диллеру могли прийти 5, потом вторая 5, потом 2, потом 2, потом 5. Да и сами пятерки могли прийти 3! способами. Однако, вы не можете поставить 2 последней, потому что сумма четырех первых карт уже 17. Диллер бы не тянул эту последнюю двойку.

    Но 19=3+3+4+4+5 уже можно выбрать всеми 5! разными способами. Поэтому эти 2 комбинации, хоть и дают одну и ту же сумму и содержат одинаковое количество карт, можно получить с разной вероятностью.

    Правильное решение с применением динамического программирования:

    Во-первых, переберите, какая карта будет вытянута последней. Найдите вероятности всех сумм с учетом этой карты (так, что она переваливает сумму за 17, но не пердыдущая). Потом просуммируйте по всем таким картам.

    Исключите эту последнюю карту из колоды. Теперь надо найти сколько наборов карт заданной длины дают каждую сумму < 17. При этом при подсчете вероятности можно переставлять все эти карты как угодно. Поэтому имеет значение, сколько карт мы использовали для суммы.

    Теперь считайте динамическое программирование - сколькими способами вы можете выбрать n карт из первых k, получив сумму s (порядок не важен).

    При подсчете у вас есть 2 варианта - последняя из k карт или входит, или нет в сумму (допустим массив a[] хранит веса карт).
    Т.е. F(n,k,s) = F(n,k-1,s)+F(n-1,k-1,s-a[k]).

    База:
    F(0, k, s) = 1 если s==0; 0 иначе
    F(n,0,s) = 1 если n==0 и s==0; 0 иначе.
    F(n, k, s<0) = 0

    Можно сэкономить на памяти и считать в двумерном массиве с конца в начало, выкинув k (второй параметр).

    Потом искомая вероятность набрать сумму x c последней картой l, если осталось в колоде max_k карт (если x<17+l, x>=17, иначе - вероятность 0):

    P(x) = sum_{n=1..max_k} f(n, max_k, x-l) * n! * (max_k-n)! / (max_k+1)!

    Тут перебор по всем возможным количествам карт у диллера. Дальше берем количество способов набрать нужную сумму из ДП. Потом домножаем на n!, потому что эти карты в руке у диллера могли прийти в любом порядке. Дальше домножаем на вероятность вытянуть конкретные n+1 карт (считая последнюю) из колоды с max_k+1 картами. Это 1/(max_k+1)/max_k/,,,/(max_k-n+1) = (max_k-n)!/(max_k+1)!

    Этот множитель после f() можно пересчитывать за одно домножение и деление
    при увеличении n.

    Напоминаю, это надо просуммировать по всем возможным картам взятым за l, заново прогоняя ДП.
    Ответ написан
    5 комментариев
  • Дайте определение обратного элемента a по модулю n. Когда он существует?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Обратный, это такой, что при умножении на него получается 1.
    Существует тогда и только тогда, когда gcd(a,n)=1.
    Ответ написан
    Комментировать
  • Swift. При формировании массива добавляется __lldb_expr что это значит?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Я не специалист по свифту, но пока похоже, что язык просто не умеет писать класс Dog. Поэтому выводит какое-то свое внутреннее обозначение класса.
    Ответ написан
    Комментировать
  • Как побороть переполнение?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В этой задаче все должно в long long помещаться. Никаких трюков не надо.

    У вас ошибка вот тут:
    long long div_up(int x, int y)

    Типа параметров - int. Вы когда в эту функцию передаете long long сумму - происходит переполнение.

    Просто измените типы на long long и должно пройти.
    Ответ написан
    1 комментарий
  • Какова сложность алгоритмов (Big(O)) встроенных JS методов?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Похоже документация не указывает сложность методов. Видимо, никто не ожидает от js кода производительности =)

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

    Возможно, можно нагуглить что-то по конкретным методам.

    По описанию метода concat, он возвращает новую строку не меняя аргументы. Поэтому, скорее всего, он работает за линейную сложность от общей длины операндов. Вряд ли там используется rope.
    Ответ написан
    2 комментария
  • Как упорядочить очередь из разных групп?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Делайте жадно. Ставьте в очередь сотрудника той группы, которая меньше всего загружена пока.

    Изначально у всех загрузка на 0/k[i] - поэтому выбираем любую случайно или первую по порядку.
    Допустим выбрали группу a. Поставили одного сотрудника. У этой группы загрузка стала 1/k[0]. Это больше 0/k[i] для всех остальных групп, поэтому следующим сотрудником вы поставите кого-то другого.

    Можно делать тупо - двумя циклами (внешний по общему количеству сотрудников, внутренний по всем группам).
    Можно ускорить процесс, если использовать приоритетную очередь на минимум. Изначально кладете в очередь все группы с приоритетами 0. Потом достаете оттуда минимум, сотрудника этой группы ставите на заказ и добавляете в очередь эту группу назад с приоритетом +1/k[i]. Можно не класть в очередь группы, в которых не осталось незадействованных сотрудников. И тогда остановка - когда очередь пуста. Можно просто останавливаться когда у минимальной в очереди группы приоритет (k[i]/k[i]).
    Ответ написан
    Комментировать
  • Как работает присвоение tail связанного списка?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    head, tail и next тут - это ссылки не элементы в списке, указатели или как это в typescript называется. Эти переменные не хранят LinkedListNode, а лишь указывают на такой элемент где-то в памяти. Первая операция this.tail.next = newNode; делает последний элемент в списке ссылающимся на новый элемент, который мы добавляем в конец. Просто обновляя ссылку у последнего элемента.
    Следующая операция передвигает ссылку tail указывать на новый, только что добавленный, элемент.

    В самом начале, когда вы будете добавлять второй элемент head и tail будут указывать на одну и ту же вершину. Поэтому изменение tail.next одновременно меняет и head.next, потому что head и tail - указывают в одно и то же место.
    Ответ написан
    Комментировать
  • Параметры функции?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Потому что иначе невозможна рекурсия. Что если у вас f(1,3) захочет вызвать f(0,0). Еще придется эти глобальные переменные переписать и потом как-то восстанавливать, если после вызова f(1,3) еще что-то хочет делать с этими параметрами.

    Потом, это тупо неудобно. Для каждой функции нужен свой набор глобальных переменных.

    Это про второй ваш вариант.
    В третьем же a и b локальные переменные. Получается вы их никак снаружи функции не сможете задать.

    Кажется, у вас непонимае. Функции нужны не просто для того, чтобы распилить программу на меньшие логически обособленные блоки, но и для переиспользования этих блоков в разных ситуациях. Вот пример - функция вывода строки на экран. Она в качестве параметра принимает строку для вывода. Ее можно вызывать для разных строк. Иначе смысла в ней вообще нет. Вот зачем нужны параметры.
    Ответ написан
    Комментировать