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

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

    А теоремы эти используют как раз, чтобы доказать, что граф не планарный. Тогда вы берете как-то стягиваете его вершины и получаете K33 или K5. Значит не планарный.

    Доказать отсутствие таких стягиваний очень муторно. Надо перебирать все варианты. Типа, что будет если мы вот эти 2 вершины стягиваем в одну? А если нет? В первом случае, а стягиваем ли мы туда вот эту третью? А если нет? И так у вас 100500 случаев. Если очень граматно выбрать вершины, то есть шанс, что количество вариантов будет не слишком большое. Если видите, что стянутый граф можно нарисовать без самопересечений, дальше стягивать не надо.
    Ответ написан
    2 комментария
  • Как выглядит нефундаментальный разрез?

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

    У вас в таблице же даже примеры нарисованы: последние 3 строки - это комбинации пары фундаментальных разрезов. Этот символ "плюс в кружочке" - это и есть исключающее или.
    Ответ написан
  • Как мне продолжить сокращать формулу?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Два наблюдения:
    1) изначальная формула имеет вид (a=>b)*(b=>a).
    2) b = !a

    Отсюда в одно действие видно, что она вырождается в ложь: (a=>!a)*(!a=>a)

    Для второго наблюдения надо применить какой-то там закон, что !x+xy=!x+y
    Ответ написан
    4 комментария
  • Правильно ли я дал описание для диаграммы?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Нет. В вашу формулу подойдет и верхний треугольник между A и B.
    Ответ написан
  • Почему x ограничен от -1 до 1?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Если брать более большой промежуток, отображение будет не биективным.
    Например, для x=2 отображение дает -2. Это же -2 можно получить для x=-2/3.
    Потом, в точках x=+-1 это отображение вообще не определено.
    Ответ написан
    2 комментария
  • Объясните как посчитать график отношений RoR?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Судя по всему, у вам надо поставить звездочки в клетках i,j, если R[i][j] или если существует k, т.ч. R[i][k] и R[k][j].

    Видимо, "RoR" означает "связаны R напрямую или через одного". Я уж и не помню, как эта операция называется математически.

    Решение в лоб: завдеите массив для графика ответа и вычисляйте каждое значение поотдельности. Во-первых, если есть отношение во входном R в этой же ячейке. Во-вторых, напишите третий вложенный цикл, который переберет все промежуточные элементы, и установите ответ в true (или звездочку там поставьте), если оно есть хотя бы в одном R[i][k] и R[k][j] одновременно.
    Ответ написан
    5 комментариев
  • Как графически показать эквивалентность двух множеств?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Наверно, на графике XY нарисовать обе функйции зависимости. Отметить отрезки допустимых x на OX, вертикальными линиями соединить концы отрезков с функцией зависимости, от точек пересечения горизонтальными линиями получить отрезок значений на оси OY. Если множества на оси OY совпали, то они эквивалентны (Спойлер: тут они не совпали. Например, инфинумом первого множества является 1, а второго - 27).
    Ответ написан
  • Почему любую булеву функцию можно представить в виде СДНФ или СКНФ?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Потому что эти формы - это тупо перечисление всех наборов входных значений, которые дают истину, или ложь (в другой форме). В СДНФ вы получаете слагаемые, объедененные через ИЛИ. Каждое слагаемое через И задает все переменные так, что только вот в конкретном наборе входных данных это слагаемое будет истинно. Раз они все через ИЛИ соединены, то вся функция истинна только если входные значение из нужного набора. Аналогично СКНФ - но там каждая скобочка истина, если входные переменные не равны набору, на котором функция должна давать ложь.
    Ответ написан
    Комментировать
  • По какому алгоритму находить наибольшую общую подпоследовательность двух дублирующихся слов?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Вообще, вот алгоритм для произвольных слов: https://e-maxx.ru/algo/longest_increasing_subseq_log

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

    Какие у вас там ограничения: длины строк и количество повторений?
    Ответ написан
  • Можно ли быстрее чем за O(N) подсчитать сумму S(N,K,M) = sum i=0..N K*i%M?

    wataru
    @wataru Автор вопроса, куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Совершенно случайно наткнулся вот на это вот равнество: hermite's identity (в твитте с прикольным доказательством. Зацените).

    C помощью него наконец-то вывел способ подсчитать эти суммы за O(log n)! Я знал, что это можно сделать!

    Итак, во-первых, можно переключатся между суммами floor(i*k/m) и (i*k)%m через вот это равенство: floor(i*k/m) = (i*k - (i*k)%m) / m
    Это нам позже поможет. В сумме через floor можно сократить GCD(m, k) в них. В сумме через модуль можно сделать k %= m, если оно больше m, да и сократить полные "обороты" в n.

    Итак, можно допустить, что m > k, m >n, GCD(m, k) = 1, иначе преобразуем сумму к нужной форме и все упростим.

    Дальше, применим равенство hermite's, взяв x = i/m, n = k.

    Потом поменяем местами суммы. Под знаками суммы будет floor(i/m + t/k) (где t - новая переменная суммирования от 0 до k-1). Присмотритесь к этому выражению - там 2 числа меньших 1. Т.е. весь этот floor даст 1, только если t/k >= 1-i/m. Отсюда можно решить это неравнество, сдвинуть нижнюю границу суммирования и получить сумму единиц во внутренней сумме. Заменив ее всю на количество слагаемых там вылезает floor (t*m/k). Т.е. мы выразили сумму i*k/m через сумму t*m/k. Но мы же помним, что можно перейти к сумме модулей и там сократить множитель k в числителе. Таким образом мы вычисляем сумму для (m, k) через сумму для (k, m%k). Это точно такой же переход, как и в GCD, поэтому суммарно будет всего O(log n) итераций. Вообще, выкладки довольно нудные, ибо сумма для t*m/k будет не от 0 до какого-то n', а от n' до k-1. Но можно взять известное значение для суммы полного оборота (от 0 до k-1) и из нее вычесть сумму от 0 до n'-1. Эта сумма известна, потому что при GCD=1, она пройдется по всем остаткам в каком-то порядке.

    Формула примерно такая получается:
    FloorSum(n, k, m) = (m-1)*(k-1)/2 - (n1+1)*n1*(m/k)/2 + (n - m + 1)*(k-n1-1) - FloorSum(n1, m%k, k)
    n' = floor(((m-n)*k-1)/m)


    Вот код. Значения совпадают с тупым решением для всех чисел до 1000 и для миллиардов случайных чисел до миллиона.
    // sum i = 0.. n floor(i * k / m)
    // GCD(k, m) must be 1.
    // n < m
    // k < m
    long long FloorSumInternal(long long n, long long k, long long m) {
    	if (k == 0 || n <= 0) return 0;
    	if (m == 1) return k*n*(n+1)/2;
    	const long long n1 = ((m-n)*k - 1)/m;
    	long long ans = (m-1)*(k-1)/2 - (n1+1)*n1*(m/k)/2 + (n - m + 1)*(k-n1-1);
    	ans -=  FloorSumInternal(n1, m%k, k);
    	return ans;
    }
    
    
    // sum i = 0.. n floor(i * k / m)
    long long FloorSum(long long n, long long k, long long m) {
    	if (k == 0 || n <= 0) return 0;
    	if (m == 1) return k*n*(n+1)/2;
    
    	const long long d = Gcd(m, k);
    	m /= d;
    	k /= d;
    	if (k >= m || n >= m) {
    		// (n*k*(n+1)/2 - ModSum(n, k, m, d))/m;
    
    		const long long nn = (n+1)%m-1;
    		const long long num_full = (n+1) / m;
    		const long long kk = k % m;
    
    		long long ans = 0;
    		ans = (k*n*(n+1) - kk*(nn+1)*nn)/m - num_full * (m-1);
    		ans /= 2;
    		ans +=  FloorSumInternal(nn, kk, m);
    		return ans;
    	}
    	return FloorSumInternal(n, k, m);
    }
    
    
    
    // sum i = 0.. n (i * k) % m;
    long long ModSum(long long n, long long k, long long m)
    {
    	long long d = Gcd(k, m);
    	if (k == 0 || m == 1) return 0;
    	// (i*k) % m = i*k-floor(i*k/m)*m
    	k %= m;
    	long long num_full = (n+1) / m;
    	long long ans = num_full * (m-d) * m/2;
    	n = (n+1)%m-1;
    	if (n > 0) {
    		ans += ((long long) k)*(n+1)*n/2 - m*FloorSum(n, k/d, m/d);
    	}
    	return ans;
    }


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

    Edit: прилизал код немного.
    Ответ написан
    Комментировать
  • Почему некорректно выводит перестановки методом поиска с возвратом?

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

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

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

    Вам надо добиться того, что оставшиеся в arr числа после index всегда шли по порядку на момент рекурсивного вызова.

    Пусть ставшиеся числа 1,2,3,4. Сначала вы ставите на первую позицию 1 (оставляете все как есть).
    Потом вы должны получить в массиве 2,1,3,4 и запуститься рекурсивно. Это один swap.
    Потом надо получить в массиве 3,1,2,4. Это можно сделать одним swap из предыдущего состояния.
    Важно тут, что вы не возвращаете массив назад после каждого рекурсивного вызова. Иначе вам пришлось бы делать циклический сдвиг части массива на каждой итерации (1,2,3,4 -> 3,1,2,4).

    В конце, после последнего рекурсивного вызова у вас будет 4,1,2,3. Чтобы вернуть все как было вам придется после цикла с рекурсивными вызовами сделать циклический сдвиг массива влево на 1 позицию.

    Т.е. рекурсивные вызовы будут как-то так:
    perm( arr, size, index + 1 );
    for( i = index + 1; i < size; i++ )
    {
        swap( arr[i], arr[index] );
        perm( arr, size, index + 1 );
    }
    int tmp = arr[index];
    for (i = index; i < size-1; i++)
        arr[i] = arr[i+1];
    arr[size-1] = tmp;
    Ответ написан
  • Обход Dom дерева как то относится к дискретной математике?

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    У вас 8 исходов. Выпишите все, которые это выражение делают 1 (преобразуйте в нормальную форму). Просуммируйте вероятности каждого из этих исходов. Для нахождения вероятности исхода (A=1,B=1,C=1) надо перемножить соответствующие вероятности.

    У вас скорее всего в вопросе опечатка, потому что правильного ответа среди перечисленных вами - нет (хотя можно поменять одно q на p и получить правильный ответ).
    Ответ написан
    1 комментарий
  • Верно ли я поставил знаки в формуле?

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

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Ваша реализация неправильная. Введите тест
    4
    0 1 2 0
    1 0 0 2
    2 0 0 1
    0 2 1 0


    Ваша программа выведет всего 2 ребра, хотя их должно быть 3. Потому что надо использовать систему непересекающихся множеств, а не массив пометок Q.

    Потом, можно не искать минимальное ребро из оставшихся в цикле, а изначально отсортировать все ребра.

    Посмотрите псевдокод в википедии. Или вот есть реализация.
    Ответ написан
  • Известен ли эффективный алгоритм поиска всех элементарных циклов в неориентированном графе?

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

    Проблема в том, что циклов в графе из n вершин может быть O(n!) (n-факториал): представьте клику - граф, где есть ребра между всеми парами вершин. Любое подмножество вершин, да еще и в любом порядке, задает элементарный цикл.

    Обычно под словом "эффективный" подразумевают полиномиальный алгоритм. Такой алгоритм тут, очевидно, не существует (потому что надо перебрать экспоненциальное количество объектов).

    Используйте обход в глубину без запоминания обойденных вершин. Такой полный перебор гарантированно найдет все циклы. Можно его ускорить, если предварительно разбить граф на двусвязные компоненты мостами и искать циклы внутри каждой компоненты отдельно.
    Ответ написан
    9 комментариев
  • Сколько существует инъективных отображений Z7-Z4?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Подсчитайте, сколько элементов в каждом множестве. Вспомните определение "инъективного отображения". Посчитайте, сколько возможных значений отображаения для первого элемента, для второго и т.д. Перемножте.
    Ответ написан
    Комментировать
  • Как считать максиминный способ в нечетких множествах?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Ну вот у вас в формуле же написано, что U объединения, это максимум из двух U. Ваши два U - это линии на графике. Надо брать верхнюю из них для каждого x.
    Ответ написан
  • Правильно ли я делаю?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Не совсем. Форма правильная, но пропорции - нет. График должен начинаться в той же точке, что и u_b. Число 0.5 там не верно. Эта точка значительно ниже 0.5. И точка пересечения двух графиков вовсе не x=4.
    Ответ написан