Ответы пользователя по тегу Математика
  • Как найти угол для поворота вектора?

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

    Достаточно только иместь координаты обоих векторов (синего и фиолетового) в евклидовом пространстве.

    Делаете произведения, делите длину вектора или число на длины изначальных векотров и получаете синус и косинус. Этого уже достаточно для матрицы поворота. Но если вам нужен сам угол, то скормите эти значения функции atan. На знаю C#, но точно должна быть версия, которая получает значения синуса и косинуса.
    Ответ написан
  • Что не так с кодом для решения по математической игре Баше?

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

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

    Возьмите, например, k=4,5,6 и порисуйте на бумажке, попробуйте подсчитать, какие позиции выигрышные (из них можно сделать ход в проигрышную позицию), а какие - проигрышные (любой ход ведет в выигрышную позицию). Считайте увеличивая количество предметов. Позиция с 0 предментами - проигрышная - предыдущий игрок придя в нее выиграл. Позиция 1 - выигрышная, потому что можно пойти в проигрышную 0. Найдите закономерность, попытайтесь ее логически обосновать.

    Пример для k=3:

    0 - проигрышная

    1 - выигрышная ( можно взять 1)

    2 - выигрышная (можно взять 2)

    3 - выигрышная (можно взять 3)

    4 - проигрышная

    5 - выигрышная (можно попасть в 4, взяв 1)

    6 - выигрышная (можно попасть в 4, взяв 2)

    7 - выигрышная (можно взять 3 и попасть в проигрышную 4)

    8 - проигрышная (любой ход ведеть в выигрышные 5-7)

    ...
    Ответ написан
    3 комментария
  • Как решить задачу про графы?

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Если в условии "трех различных цифр" означает, что все цифры разные, то ответ - количество сочетаний из 10 по 3, умноженное на 3 факториал: C(10,3)*3! = 10*9*8 = 720.

    Логично: есть 10 вариантов выбрать первую цифру, 9 - вторую (исключив вариант повторения), и 8 - третью (минус 2 варианта повторения первой и второй цифры).
    Ответ написан
    Комментировать
  • Как получилась запись в квадратных скобках?

    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.
    Ответ написан
  • Как рассчитать "похожесть" двух словарей?

    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, заново прогоняя ДП.
    Ответ написан
    3 комментария
  • Дайте определение обратного элемента a по модулю n. Когда он существует?

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    В моей нотации С(n,k) - сочетания из n по k.

    Нужно знать вот это тождество для сочетаний:
    С(n,k) = C(n,n-k).

    Применив к тому, что у нас под знаком суммы:
    C(2n+1, 2k) = С(2n+1, 2n+1-2k)

    Когда k проходит от 0 до n, слева получаются сочентания по всем четным числам от 0 до 2n+1, а справа - по всем нечетным.

    Есть формула, что сумма всех сочетаний из n по всем возможным k будет 2^n, Получается тупо из раскрытия (1+1)^n по биному Ньютона.

    Но у нас тут не сумма по всем, а только по четным. Но выше мы уже видели, что суммы по всем четным и всем нечетным совпадают. Отсюда напрашивается варинат просто подсчитать удвоенную искомую сумму.

    Формально: пусть X - искомая сумма в задаче.
    тогда
    X+X = sum_{k=0..n} C(2n+1, 2k) + sum_{k=0..n}(2n+1, 2n+1-2k) =
    = sum_{k=0..n} C(2n+1, 2k) + sum_{k=0..n}(2n+1, 2*(n-k)+1) =
    = sum_{k=0..n} C(2n+1, 2k) + sum_{k=n..0}(2n+1, 2*(n-k)+1) =

    Во второй сумме заменим n-k=j. Т.к. k=n..0, то j = 0..n.

    = sum_{k=0..n} C(2n+1, 2k) + sum_{j=0..n}(2n+1, 2*j+1).

    А это тупо сумма по всем возможным k от 0 до 2n+1 (только четные и нечетные в отдельных суммах):
    = sum_{k=0..2n+1} C(2n+1, k) = 2^(2n+1)

    Остюда X = 2^(2n+1) / 2 = 2^(2n)
    Ответ написан
    1 комментарий
  • Как разделить "веса" на кластеры КОРРЕКТНО?

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

    Варианты метрик:

    - Для каждого кластера считается наибольшее расстояние между двумя элементами, и это суммируется по всем кластерам. Можно суммировать квадраты этих расстояний, тогда будут наказываться кластеризации с очень большими кластерами.
    - отношение максимального расстояния между соседними точками в любом кластере и минимального расстояния между кластерами.
    - Это может быть и качественная метрика. Любая кластеризация, где расстояние между соседними точками в кластере меньше расстояния между кластерами считается хорошей. Это частный случай предыдущей метрики, но вам достаточно искать не минимум, а любое значение <1.

    Некоторые метрики имеют смысл только при фиксированном количестве кластеров, как первая.

    Разные метрики дают разные кластеризации и все они в каком-то смысле хорошие. Что именно подходит вам в вашей задаче - можете судить только вы эмпирически.

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

    Многие метрики, если они аддитивны как первая, можно считать динамическим программированием: f(i,k) - значение метрики если мы разбили первые i точек на k кластеров.

    Для других, как для второй придется смешивать дихотомию по ответу и динамическое программирование (бинарный поиск по ответу, далее проверяем, а есть ли разбиение с такой или лучшей метрикой. Внутри динамика - минимально достижимое значение максимума между соседними точками в классе среди первых i при разбиении на k кластеров. При переборе последнего кластера нужно смотреть, чтобы расстояние между ним и соседними не превышало ответа динамики деленного на перебираемый коэффициент).

    Еще можно применять стандартные методы без оптимизаций опирающихся на то, что у нас одномерное пространство - тупо применяйте метод k ближайших соседей, например.

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

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

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

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

    Можно проверить в 2 этапа. Подсчитать A=a*x+b, B=c*x+d. Потом проверить что A*x*x = -B. Но тут нужно заранее отсечь случаи, когда abs(A) > abs(B)/(x*x). Тогда переполнений не будет.

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

    if (d != 0) {
      n = d;
    } else {
      s.push_back(0);
      if (c != 0) {
        n = c;
      } else if (b != 0) {
       n = b;
      } else {
       n = a;
      }
    }
    if (n == 0) {cout << "-1"; return}
    Ответ написан
    2 комментария
  • Как решить задачу с перебором?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Самое простое решение для понимания - полный перебор. Тупо 3 цикла - сколько монет с номиналом 2 взяли, сколько монет с номиналом 3 и сколько монет с номиналом 5. Каждый цикл от 0 до <Сколько осталось набрать> / номинал. Потом проверяем, что сумма сошлась. Более того, можно последний цикл пропустить и просто проверить, что оставшееся можно набрать пятаками.

    bool CanExchange(int n, int have2, int have3, int have5) {
      for(int take2 = 0; take2 <= min(have2, n/2); ++take2) {
        int left2 = n - 2*take2;
        for (int take3 = 0; take3 < min(have3, left2/3); ++take3) {
          int left23 = left2 - take3*3;
          if (left23 % 5 == 0 && left23 / 5 <= have5) {
            return true;
          }
        }
      }
      return false;
    }


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

    Самое быстрое решение - динамическое программирование.

    F(n,k) - можно ли набрать число n первыми k типами монет.

    F(0.0) = 1
    F(*, 0) = 0
    F(n,k) = Sum(i = 0..have[k]) F(n-i*value[k], k-1)

    Фактически, это тот же рекурсивный перебор, только с мемоизацией. Можно переписать это двумя циклами и соптимизировать по памяти до O(n).
    Ответ написан
    Комментировать
  • Что это такое в решении предела (lim)?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Это они просто вынесли за скобки максимальную степень x отдельно в числителе и знаменателе.
    Ответ написан
    Комментировать
  • Как выразить динамику набора данных одним числом?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Возьмите для каждого товара набор точек {день, показатель} за сколько-то последних дней. Постройте линейную регрессию. Наклон прямой (т.е. коэффициент перед номером дня) и будет этим показателем динамики.
    Ответ написан
    Комментировать
  • Как посчитать вероятность того, что конкретная подстрока встретится во всей строке только 1 раз?

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

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

    Вот построили вы автомат. Теперь задача состоит в том, чтобы найти вероятность, что случайный путь в этом автомате длины n пройдет через конечное состояние ровно один раз. Для этого подсчитайте 2 вероятности: что путь из начала длины k дойдет до конечного состояния один раз, и что путь из конечного состояния длины n-k не вернется в него.

    Обе эти вероятности можно подсчитать динамическим программированием:
    P1(i, k) - вероятность того, что путь начиная с состояния i (i < n) за k шагов дойдет до состояния n первый раз. Это просто сумма по всем возможным переходам:

    P1(i, k) = sum_{c - все символы} P1(next(i, c), k-1) * p(c)

    База:
    P1(m, 0) = 1
    P1(m, k>0) = 0
    P1(i < m, 0) = 0


    Вторая вероятность: сделать k шаговиз состояния i и ни разу не войти в конечное состояние:
    P2(i, k) = sum_{c - все символы, next(i,c) < m} P2(next(i, c), k-1) * p(c)


    База:
    P1(i, 0) = 1

    Ответ к задаче - сумма по всем возможным длинам первой части пути:
    sum_{k=m..n} P1(0, k) * P2(m, n-k)

    Это решение через динамическое программирование будет O(n*m) по вермени и по памяти.

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Вам просто надо пересечь 3 окружности на плоскости. По "формула пересечения окружностей" гуглится, например, это или это.

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Не очень понятно, но вам, похоже, нужны такие числа, чтобы у них был максимальное наименьшее общее кратное (это то самое 42 для 6 и 7).

    Из чисел до 60, очевидно, вам подходят числа 60 и 59 - у них нет общего делителя и НОК равен просто их произведению (3540). Если нужно строго меньше 60, то берите 58 и 59. Вообще, 2 максимальных числа в промежутке будут вашим ответом. У соседних чисел НОК всегда равен их произведению и произведение двух максимальных в отрезке будет самым большим возможным значением.
    Ответ написан
    1 комментарий
  • Как проверить, попадает ли географическая точка в правильный шестиугольник?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Вычтите координаты центра из координат точки. Теперь шестиугольник лежит центром в (0, 0).

    Составьте 6 уравнений сторон шестиугольника в виде Ax+By+C=0, так что C положительно (если нет - домножте на -1). Подставьте в эти 6 уравнений координаты точки, все они должны дать положительные значения (или нули, если точка на границе считается лежащей внутри в вашей задаче).

    Почему это работает? Мы просто проверяем, что точка лежит с той же стороны от всех 6 прямых, что и центр (0, 0).

    Как составить уравнения? Найдите координаты 6-ти углов. Если сторона = a, то координаты точек (a, 0), (a/2, sqrt(3)*a/2), (-a/2, sqrt(3)*a/2), (-a, 0) ...

    Для уравнения одной из сторон возьмите в виде A разность по у у соседних точек, а в виде B разность по x (но с другим знаком). Потом подставьте туда координаты одной из точек и возьмите C так, чтобы был 0.

    Можно все 6 уравнений составить на бумажке и закодировать в программе.

    Пример для первой стороны:
    A = sqrt(3)*a/2-0 = sqrt(3)*a/2
    B = a - a/2 = a/2
    C = -A*a - B*0 = -sqrt(3)*a*a/2.

    Поскольку C отрицательно, меняем знаки:
    A = -sqrt(3)*a/2
    B = -a/2
    C = sqrt(3)*a*a/2

    Для второй прямой будет 0*x-a*y+sqrt(3)*a*a/2 = 0

    И да, это работает, если предположить, что шестиугольник маленький или на плоскости. если у вас кривизна земли играет роль, то все сильно усложняется.
    Ответ написан