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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    В столбик. Точно так же как обычные десятичные числа, только помните, что цифры 8 нет. Если получилось больше 7, все остальное, поделенное на 8 переносится дальше.
    Ответ написан
    Комментировать
  • Теоретически, что будет если дать процессору инструкцию поделить на ноль без механизмов обработки?

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

    Можно было бы предположить, что оно повиснет, как при делении на 0 на механическом калькуляторе. Хоть это и прикольно выглядит.

    Но процессор не вычитает кучу раз подряд, ведь там двоичная система счисления. Разряд вычисляется проверкой на переполнение при одном вычитании со сдвигом, а результат идет дальше. Вот лекция о том, как устроена схема делителя.

    При вычитании нуля со сдвигом там никогда переполнения быть не будет, поэтому все биты ответа получатся равными 1.

    В итоге оно скорее всего выдаст неправильный результат. Что-то вроде 2^31-1 для любого делимого.

    Правда, если Intel/Amd/etc. нагородили каких-то оптимизаций или как-то усложнили схему, то результат может быть другим.
    Ответ написан
    Комментировать
  • По какому алгоритму находить наибольшую общую подпоследовательность двух дублирующихся слов?

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

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

    Какие у вас там ограничения: длины строк и количество повторений?
    Ответ написан
  • Почему ближайшие точки определяются неправильно?

    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;
    Ответ написан
    Комментировать
  • Что делать, когда 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 комментариев
  • Как сделать преобразование фурье для изображения по xy?

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

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Есть простой трюк сильно упростить задачу: Измените переходы из "конечной" вершины - она теперь с вероятностью 100% будет вести только в саму себя. Таким образом, процесс хотя бы раз достигший вершины, навсегда в ней останется.

    А вопрос из задачи (если его понимать как: минимальное количество шагов, чтобы хотя бы раз посетить конечную вершину с вероятностью >99%) становится эквивалентен: минимальное количество шагов, чтобы быть в конечной вершине с вероятностью не меньше 99%.

    А тут уже надо найти минимальное число k, такое что соответствующее значение в матрице A^k было бы > 0.99. A тут - это матрица переходов.

    Можно или в тупую умножать матрицу саму на себя, пока значение в строке начальной вершины и столбце конечной вершины не станет достаточно большим. Это будет O(N^3*ответ). Или можно делать хитро: бинарным поиском перебирайте степень, а внутри логарифмическим возведением в степень считайте A^k. Это будет O(N^3*log(ответ)^2).
    Ответ написан
    Комментировать
  • Как написать функцию sin из библиотеки math.h в Си?

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

    Ваша функция, как и стандартная, работает с радианами и выдает такой же результат от того же аргумента Pi*30/180=Pi/6.

    Посмотрите, ведь в result и во второй строке вывода вы передаете один и тот же агумент и получаете один итот же 0.500000. Только во второй строке у вас вместо Pi написано 3.1415265.
    Ответ написан
  • Посчитать многоугольник почему не работает програма?

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

    Код проверки будет примерно такой:
    def can_construct_polygon(lengths):
      return len(lengths)>0 and max(lengths) < sum(lengths)/2


    В списке lengths надо передать длины всех отрезков. Если у вас там вектора на плоскости заданы в vectors, то через map подсчитайте их длины сначала.

    Edit:

    Раз поворачивать вектора-отрезки нельзя, то действительно, составить многоугольник можно, если по-координатная сумма векторов дает (0, 0).

    Все еще остается вопрос, а допускаются ли вырожденные мноугольники? Например, можно ли составить такой из веторов (1,0), (-1,0). Если допускаются, то проверять длины не надо. Если нет, то все еще надо, как я выше указал, сравнить максимальную длину с суммой всех длин.

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

    Ваш код вроде бы правильно проверяет на сумму векторов покомпонентно. Попробуйте добавить еще и проверку длин.
    Ответ написан
  • Как проверить многоугольник на закольцованность?

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

    Доказательство элементарно: В противном случае, очевидно, всех остальных сторон не хватит составить путь от двух точек длиннейшего отрезка.
    А дальше возьмем окружность у которой этот длиннейший отрезко будет хордой. Приложим все остальные отрезки как хорды подряд. Если их не хватает замкнуть многоугольник, то уменьшим радиус. Если они закрывают суммарно дугу окружности больше, чем между хордой на самом длинном отрезке, то увеличиваем радиус. По какой-нибудь теореме о нуле непрерывной функции где-то существует радиус, для которого все эти отрезки оформятся в выпуклый мноугольник, вписанные в окружность.
    Ответ написан
    Комментировать
  • Можно ли быстрее чем за 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: прилизал код немного.
    Ответ написан
    Комментировать
  • Как правильно задать интервал для формулы a³+b³=c³+1?

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

    Я думаю, вам стоит вообще внешний цикл убрать и всегда работать только с одним интервалом.

    Можно чуть чуть ускорить решение, если не увеличивать c в цикле, а вычислить его по формуле (an+bn-1)^(1/n). Какая-нибудь функция pow вам поможет. Она даст неуелое число, его надо округлить и проверить, что равенство ввполняется.

    Еще, ваша проблема, что c у вас может быть не из заданного интервала. Надо или жто проверять, или перебирать b и c, считать a, вместо перебора a, b и вычисления c. Потому что a < c. И раз c перебирается в интервале - то и a будет в нем.
    Ответ написан
    44 комментария
  • Как определить есть ли противоречия в цепочке логических выражений?

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

    Все неравенства "==" замените на пару "<=" и ">=".
    Добавьте неравенства 1 < 2, 3 < 4 и т.д. для каждой пары соседних на числовой прямой чисел во входных данных

    Постройте граф: Каждой переменной и уникальному числу во входных данных сопоставьте одну вершину. Проведите для каждого неравнества ребро от меньшей вершины к большей, раскрашенное в 2 цвета: черный, если неравнество нестрогое (<=), белый - иначе.

    Теперь, если в этом графе нет циклов, содержащих белые ребра (строгие неравенства) - то противоречий нет: Все циклы целиком из черных ребер означают, что все вершины имеют одинаковое значение. Можно эти вершины все объединить в одну новую. Раз белые ребра (<) циклов не образуют, то получившийся граф будет ациклическим и можно назначить всем вершинам какие-то числовые значения, удовлетворяющие условиям. Проблема может еще быть, что нет целых решений вроде 1== a < b < c == 2, но это можно потом проверить в топологической сортировке жадно назначая всем вершинам числа. Или противоречия вида 2==3. Тоже решается после получения компонент связности.

    Итак, алгоритм: найдите в этом графе компоненты сильной связности. Потом проверьте все ребра. Если белое ребро (строгое неравнество) имеет оба конца в одной и той же компоненте - вы нашли противоречие.

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

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

    В конце вы получите для каждой компоненты ее численное значение без каких-либо противоречий.
    Ответ написан
    4 комментария
  • Какое решение уравнения x^3 + y^3 = z^3 в дробных числах?

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

    А в иррациональных - берете вообще любые x, y и по ним считаете z. Все.
    Например, x=10, y=1, z= 1001^(1/3)
    Или x = 20^(1/3), y= 7^(1/3), z=3
    Их континуум этих решений (бесконечно много).
    Ответ написан
    24 комментария
  • Есть ли у этого название?

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

    Подозреваю, что вам нужно целое число, помещающееся в делимом (семь двоек в 15). Тогда такая операция называется "деление нацело". Она вместе с делением по модулю идет парочкой: вот это деление нацело дает количество целых кусков, а деление по модулю (оно же "взятие остатка") дает сколько там остается.
    Ответ написан
    Комментировать
  • Как классифицировать числовой ряд?

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

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

    Во-первых, данные надо отсортировать, если уже не. А дальше у вас тут 2 переменные - (i,j) - первый и последний элемент в средней группе.i>0,j<n-1.

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

    Но вообще можно, наверно, просто взять 2 максимальных промежутка между соседними числами и по ним разделить. В примере выше этот метод отлично разбивает на 3 группы: 1-103, 999-1001, 9000-9500

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

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

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Ну, скалярное произведение вектора самого на себя - даст квадрат его длины по определению (|a|*|a|*cos(0)). Если менять точки - меняется длина вектора - меняется скалярное произведение.
    Ответ написан
    Комментировать
  • Доказать рекуррентную формулу. Кто может решить? Что с этим вообще можно сделать?

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

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

    Осталось поэксперементировать перебирая разные части x^n и корня из квадртатного выражения: что из них интегрировать, а что дифференцировать.

    Интегрировать корень в знаменателе будет очень тяжко, там логарифмы какие-нибудь вылезут и вообще форма выражения будет совсем другой. Попробуйте продифференцировать 1/sqrt(), а оставшеюся часть интегрировать. Или можно домножить числитель и знаменатель на корень, тогда корень можно уже интегрировать, а оставшееся рациональное полиномиальное выражение дифференцировать.

    Еще можно с конца идти - перенесите все интегралы в одну сторону, сгрупируйте. Получите интеграл равен функции. Можно проверить равенство просто продифференцировав функцию.

    Похоже в задании опечатка - там должны быть J(n-1) и J(n-2), потому что дифференцируя вот эту свободную штуку получается (C1x^n+C2x^(n-1) + C3x^(n-2)) / sqrt(ax^2+bx+c). Значит, с другой стороны должны быть линейная комбинация J(n), J(n-1) и J(n-2).
    Ответ написан
    Комментировать
  • Как позиция объекта соответствует четырёхмерной матрице?

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

    Четвертая координата позволяет записывать одной матрицей повороты/растяжения и сдвиги. Если у вас матрица 3x3 (не "трехмерная" - у нее все так же есть только ширина и высота), то точка (0, 0, 0) всегда останется (0, 0, 0). Ведь на что 0 не домножай - останется 0. Поэтому матрицами 3x3 можно записать только повороты и сжатия, но не сдвиги.

    Поэтому вводят фиктивное четвертое измерение w. При этом удобно считать, что координаты точки (x, y, z, w) - Это (x/w, y/w, z/w). Это еще иногда называют однородными координатами. Еще один плюс этого объекта в том, что им можно описать точки на бесконечности (когда w = 0).

    Вот тут уже можно составить линейное преобразование (т.е. взять матрицу), которое оставляет w нетронутым, но использует его для сдвига:
    x' = a11*x + a12*y + a13*z + a14*w
    ...
    w' = w

    Вот в a14 - как раз и находится сдвиг по оси X.
    Ответ написан
    3 комментария