• Сколько всего паролей будет?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если пароли считаются уникальные, как строки, то ответ - 2^n: любую строку из "a" и "b" можно как-то составить.

    Если пароли считаются уникальные, как последовательность "aa", "a", "b" и "bb" (так, для трех букв отдельно подсчитаются a+aa, aa+a и a+a+a), то ответ можно найти по рекуррентной формуле F(N) = 2(F(N-1)+F(N-2)), F(1) = 2, F(2) = 6.

    Формла выводится так: В конце может быть один символ. Тут 2 варианта - это "a" или "b", тогда получается F(N-1) последовательностей длины N-1. Если же в конце идет "aa" или "bb", то таких последовательностей ровно F(N-2). Просуммировав все получится 2(F(N-1)+F(N-2))

    Это что-то вроде чисел фиббоначи. Можно еще вывести формулу:
    F(N) = (1+sqrt(3))^(N+1)-(1-sqrt(3))^(N+1) / (2sqrt(3))


    Если пишите программу, то лучше считать F() итеративно. Только учтите, там будут очень большие числа - в 64-битный тип не поместится.
    Ответ написан
    Комментировать
  • Какова сложность алгоритма поиска всех элементов AVL дерева, принадлежащих интервалу?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Оценка O(log n + m) - правильная. Ее нельзя (или очень сложно) вывести рекуррентным соотношением, потому что она сильно завязана на условия в функции. Там 2 рекурсивных вызова, но они происходят не всегда.

    Оценку можно вывести посчитав различные вызовы функции. Сначала, когда node->key не в интервале - будет ровно один рекурсивный вызов. Ну, потому что не в интервале, это или <min, или >max. Т.е. одно из двух условий (node->key > min, node->key < max) точно нарушается.

    Поэтому сколько-то уровней дерева будет ровно один вызов. Потом будут вызовы в вершины внутри интервала, и могут быть вызовы за границы интервала. Первые все посчитаются в слагаемом M в оценке. Сколько будет вторых? Не более 2*количество уровней - максимум один вызов в числа меньшие min и один в числа, большие max.

    Вот мы оказались в какой-то вершине в отрезке (A). И пошли от нее влево в вершину вне отрзка (B). А так же вправо в вершину (C). Так вот, все ключи в поддереве С не меньше A, а значит, ни одна из них не будет "торчать" из отрезка влево. Поэтому, "плохие" вершины вне отрезка слева, которые мы посещаем, там не встретятся никогда. Значит, на следующем уровне может быть максимум одна "плохая" вершина слева - правый ребенок B. И так далее, пока мы не спустимся до вершины опять в отрезке. Аналогичное рассуждение показывает, что может быть только по одной плохой вершины справа на каждом уровне.
    Ответ написан
    Комментировать
  • Почему создается массив с мусором?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Закрывающего нуля в конце строки нет. Поэтому вывод лезет за границу выделенной памяти, а там мусор.

    И еще, очень глаз режет: правильно писать "symbol", а не "simbol".
    Ответ написан
  • Почему ближайшие точки определяются неправильно?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Есть еще вот такое решение. Выводится так: задаем обе прямые параметрически (точка + t или u, помноженная на вектор вдоль прямой). Получаем выражение для квадрата расстояния между точками как функцию от t и u. Ищем ее минимум, приравняв к 0 частичные производные. Там получаются 2 линейных уравнения.

    Vector3 a = axis2.first - axis1.first;
    Vector3 v1 = axis1.second - axis1.first;
    Vector3 v2 = axis2.second - axis2.first;
    float v11 = Vector3::DotProduct(v1, v1);
    float v12 = Vector3::DotProduct(v1, v2);
    float v22 = Vector3::DotProduct(v2, v2);
    float av1 = Vector3::DotProduct(a, v1);
    float av2 = Vector3::DotProduct(a, v2);
    // Решаем систему методом Крамера:
    // t*v11-u*v12=av1
    // t*v12-u*v22=av2
    float d1 = -av1*v22+v12*av2;
    float d2 = v11*av2-v22*av1;
    float d = -v11*v22+v12*v12;
    float t = d1/d;
    float u = d2/d;
    point1 = axis1.first + v1 * t;
    point2 = axis2.first + v2 * u;
    Ответ написан
    Комментировать
  • Как получить все возможные комбинации?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    itertools.combinations. Скармливаете туда range индексов вашей строки. Получаете список из 1/2/сколько надо сделать замен индексов, на эти позиции ставьте ваши Z, Y.

    Вот набросок кода.
    Я не питонист, возможно есть более лаконичная реализация. Особенно мерзкий хак с перобразованием строки в list, потому что строки в питоне immutable.

    import itertools
    
    def GenerateAll(source, rep):
      for comb in itertools.combinations(range(len(source)), len(rep)):
        s = list(source)
        for (i, j) in enumerate(comb):
          s[j] = rep[i];
        yield "".join(s)
        
    print(list(GenerateAll("abcd", "XY")))
    # ['XYcd', 'XbYd', 'XbcY', 'aXYd', 'aXcY', 'abXY']
    Ответ написан
  • Как сделать побитовое умножение и сложение большого числа decimal?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вот эти побитовые сдвиги работли бы, если бы вы хранили число в двоичной системе счисления. Допустим, по 16 бит в каждом int. В таком виде вычисления побыстрее будут немного, но при выводе надо переводить число в 10-ую систему счисления. Судя по примеру ответа, вы храните по 3 десятичных знака в каждой ячейке. Т.е. у вас основание системы счисления 1000. И тут нужны сдвиги не битовые, а тысячные (которых не существует).

    Вообще, основной цикл умножения должен быть примерно такой:

    for (i = 0; i < len1; ++i)
      for (j = 0; j < len2; ++j) 
        c[i+j] += a[i]*b[j];


    И потом надо сделать все переносы. Вот тут и происходят сдвиг на i позиций (в системе счисления 1000) - это то самое +i в индексе у массива результата. И вместо выполнения прибавления, когда бит равен 1, тут происходит умножение на a[i]. В битовом случае оно как раз вырождается в умножение на 1 или 0 - прибавление или пропуск прибавления.

    Это работает даже если вы в массивах a,b храните по несколько бит в каждой ячейке, или даже десятичных знаков. Это просто умножение в столбик в произвольной системе счисления.

    Правда, тут проблема, что вот такое вот умножение, если вы храните по 32 бита, оно переполнится, и ответ надо в 64-битном типе получать. И даже если вы храните по 16 бит, то сумма может переполниться из-за большого количества слагаемых. Ну это решается выполнением переноса сразу же:
    const int BASE = 1000; // База системы счисления. Оно же максимальное число в ячейке +1.
    for (i = 0; i < len1; ++i) {
      int carry = 0;
      for (j = 0; j < len2; ++j) {
        carry += c[i+j] + a[i]*b[j];
        c[i+j] = carry % BASE; 
        carry /= BASE;
      }
      int i = len1+len2-1;
      while (carry > 0) {
        carry += c[i];
        c[i] = carry % BASE;
        carry /= BASE;
        ++i;
      }
    }
    Ответ написан
  • Нужно ли делать защиту при делении на ноль?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Для облегчения отладки в будущем, стоит вставить assert какой-нибудь. Чтобы программа падала с понятной ошибкой, при попытке поделить на 0.
    Ответ написан
    Комментировать
  • Не могу решать задачи по программированию?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Единственный способ научиться решать задачи - это практика. Решайте задачи на сайтах. leetcode, codeforces например. Начинайте с самых легких задачек и пытайтесь решить. Если за час не можете решить задачу, смотрите решения в интернете, на форумах прямо на самих же сайтах. Обязательно реализуйте найденное решение.
    Ответ написан
  • Почему поиск в ширину работает?

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

    Вообще тут не нужен обход в ширину, тут, кажется, достаточно цикла по всем вершинам.

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

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Линейная. O(N+M). Ибо там цикл по i от 0 до startMedianIndex и вся остальная мишура на i вообще никак не влияет. Больше циклов в программе нет. Не понимаю, что тут может быть непонятного.
    Ответ написан
    Комментировать
  • Что делать, когда Wolfram говорит, что будет корень, а считать не хочет - a³+b³=z³?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Этот корень - иррациональное число. Бесконечное. Его точно на компьютере не подсчитать никак. Это будет какое-то большое число с бесконечным не повторяющимся набором цифр после точки.

    Точнее всех будет считать python если использовать библиотеку Decimal:
    from decimal import *
    getcontext().prec = 400
    a = Decimal('112312312312312311231231231231231123123123123123112312312312312311231231231231231123123123123123');
    print(pow(a,Decimal(1/3.0)))


    Можете хоть тысячи знаков считать, ограничения - в основном скорость работы и требования по памяти.
    Ответ написан
    5 комментариев
  • Как хешировать в хеш таблице узлы дерева?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Итак, у вас ключи - какая-то древовидная структура и вам надо быстро определять, а есть ли такая структура в таблице. Я правильно понял?

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

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

    Или можно тупо записать вашу структуру данных в строку (Например, расставив скобки вокруг каждого поддерева вроде "(a(b)(ccc(dd))" - это узел a, у которого есть дети b и ccc, у последнего есть ребенок dd) и потом как угодно хешировать уже строку, тогда ничего самостоятельно реализовавать ничего вообще не надо (to_json и hash от строки возможно уже есть).
    Ответ написан
    2 комментария
  • Как найти точки для обьекта и нарисовать его?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Точно так же, как вы в проедыдущем вопросе находили координаты тик-марков, вы можете тут найти 2 координаты - пятая точка на острие стрелки и центр короткой стороны. Теперь, можно ввести систему координат из центра окружности так, чтобы ось ox шла вдоль стрелки. Для этого вам надо найти 2 перпендикулярных вектора. Один у вас уже есть - это вектор между двумя известными уже точками. Он же будет cos(alpha), sin(alpha). Перпендикулярный вектор будет -sin(alpha), cos(alpha). Теперь надо только взять координаты 5 точек в этой системе координат (как будто бы стрелка лежит горизонтально) и сложить x, помноженный на первый вектор с у помноженным на 2 вектор.

    В итоге будет что-то вроде:
    x' = x*cosa + y*sina
    y' = -x*sina + y*cosa


    Это же выражение является заодно матрцей поворота на угол alpha.

    А координаты там что-то вроде: {r0, w/2}, {r0, -w/2}, {r1, -w/2}, {r1 + w/2, 0}, {r1, w/2}, гже r0, r1 - радиусы окружностей между которыми стрелка натянута. w - ширина стрелки. Эти пары подставляйте ввиде x,y в формулу выше и получите x', y' - координаты точек на экране.
    Ответ написан
  • Невидимый оверлей для записи видео/демке экрана/скриншотах?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Смотрите, чтобы окно было не видно записью экрана, оно должно быть "выше" этой самой записи. Например, рисоваться прямо в экранную память перед демонстрацией экрана. Такие штуки, действительно, зовутся overlay - hardware overlay. Что бы вы средствами winapi с вашим окном не делали, оно останется "ниже" захватчика экрана.

    То, что вам нужно нельзя сделать на чистом winapi. Тут нужен именно DirectX. Придется ручками что-то там рисовать в поверхность. Гуглите "directx overlay".
    Ответ написан
    Комментировать
  • В чем причина данной ошибки?

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


    У вас 2 раза определен class парсер::ВыражениеParser.

    Компилятор даже указал на оба определения: они оба в файле ВыражениеParser.h, но включенного 2 раза из разных исходников:
    In file included from ВыражениеVisitor.h:8,
                     from ВыражениеParser.cpp:6:
    ВыражениеParser.h:15:8: ошибка: повторное определение «class парсер::ВыражениеParser»
    ...
    In file included from ВыражениеListener.h:8:
    ВыражениеParser.h:13:8: замечание: предыдущее определение «class парсер::ВыражениеParser»


    Пока похоже, что вы там намудрили с include-guard'ами из-за чего один и тот же хедер включается несколько раз. Выкладывайте начало файла ВыражениеParser.h.
    Ответ написан
  • Как сделать преобразование фурье для изображения по xy?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Двумерное преобразование фурье обычно делается так: отдельно каждую строку одномерным перобразованием фурье, потом на вот этой новой матрице - отдельно каждый столбец. Обратное преобразование в обратном порядке: cначала каждый столбец, потом каждую строку (хотя можно и в другом порядке, просто так концептуально понятнее, почему оно работает).

    Если у вас всего 4 пикселя, то возможно x и y - это будут 2 строки входной матрицы.
    Ответ написан
    Комментировать
  • Как присвоить динамическому массиву типа void* значение в Си?

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

    А так, скорее всего вам подойдет функция memset. Какими-нибудь нулями все заполнить - отлично можно. Хоть там int, хоть char, хоть float.
    Ответ написан
  • Как вывести буквы, которые используется наиболее кол-во раз?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Задача разбивается на 2: 1) подсчитать для каждой буквы количество ее вхождений, 2) Найти максимум и вывести его индекс.

    Первая задача решается просто - заведите массив счетчиков. Например, на 256 элементов. Для каждой буквы строки увеличивайте счетчик с индексом равным символу (помните, же, что char в C++ - это целочисленный тип?)

    Ну, вторая задача - это элементарное упраждение. Например, заведите переменную, в которой будете хранить индекс максимума. Пройдитесь по массиву счетчиков (от 0 до 255), и изменяйте этот индекс, если текущее значение больше максимального.
    Ответ написан
  • Как лучше развернуть двумерный массив?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Если не обязательно делать поворт на месте, то вся суть алгоритма вот в этой одной строке:
    result[i][j] = arr[n-1-j][i];
    Надо только циклы прогнать по нужным границам, да массив нужного размера создать.

    Если матрица квадратная, то элементы сдвигаются по кругу в четверках - и это можно сделать без дополнительного массива . Можно делать сдвиг по кругу со временной переменной. Что-то вроде этого:
    tmp = arr[i][j];
    arr[i][j] = arr[n-1-j][i];
    arr[n-1-j][i] = arr[n-1-i][n-1-j];
    arr[n-1-i][n-1-j] = arr[j][n-1-i];
    arr[j][n-1-i] = tmp;


    И надо аккуратно границы цикла подобрать, чтобы там только левый верхний угол обработался. Иначе вы 4 раза в каждом круге сдвините, и ничего не поменяется.
    Ответ написан
    Комментировать
  • Может ли веб-страница записывать действия в браузере без разрешения на это?

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

    Вы можете узнать, на какую ссылку юзер ткнул, но дальше - все.
    Ответ написан
    Комментировать