• Можно ли быстрее чем за 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: прилизал код немного.
    Ответ написан
    Комментировать
  • Как получается формула N*(N-1) / 2?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Ну вы подставьте в формулу число. Допустим, N=3: 1+2+3 = 6. N(N-1)/2= 3*2/2 = 3. Не сходится!
    Правильная формула, все-таки N(N+1)/2.

    А разница в том, что в учебнике получается сумма от 1 до N-1. Т.е. в формулу прогрессии от 1 до N надо подставить N-1. Вот и получается (N-1)(N-1+1)/2 = (N-1)N/2
    Ответ написан
    1 комментарий
  • Как быстро умножают края матриц?

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

    Или можно наивно последние 1-7 слагаемых в каждой сумме подсчитать. Это займет O(n^2), что по сравнению с остальным алгоритмом - копейки.
    Ответ написан
    2 комментария
  • Как убрать time.sleep() или чем его заменить а автотестах?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Все индивидуально. Чего этот sleep в коде ждет?
    Может, тут он ждет отклика от сервера, и там действительно надо 200мс ждать. И просто заменой time sleep на что-то вы ничего не исправите. Или это анимация чего-то на экране, и тут можно в 0 все убирать.

    Но вообще гуглите simulated time python.
    Посмотрите, может simpy вам тут поможет.
    Ответ написан
    1 комментарий
  • На каком языке пишут программы, где есть счетчик сколько ты не курил и их достижения?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если надо с чатиком, то нужна какая-то серверная часть. Серверную часть можно написать и на python, например, используя фреймворк django, но php или java какие-нибудь могут быть популярнее. Плюс какие-то базы данных там наверняка понадобятся, так что надо знать SQL. Клиентскую часть надо писать на разных языках в зависимости от платформы.
    Если это веб-приложение, то нужны javascript, typescript или scala. Плюс надо использовать один из сотни фреймворков, иначе задолбаетесь все с нуля писать. Ну и html c CSS надо знать, для оформления страницы.
    Если это приложение для смартфонов, то java или C++ + немножко java на андроиде, objective-c или swift на айфонах.
    Если это для винды приложение, то можно С++, java или python.
    Ответ написан
    Комментировать
  • Как я могу конвертировать число в символ?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Проблема в кодировке ascii. Код символа '3' не равен 3.

    На c++ можно делать так:
    (char)(x+'0')

    Этот код преобразует цифру x в символ, ей соответствующий. Лучше, правда приводить тип через static_cast.

    Еще можно воспользоваться всякими to_string, будет даже читабельнее, но медленнее.
    Ответ написан
  • Почему free() выводит ошибку?

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

    Вижу сразу ошибку в delete_node: Если вершина в списке всего одна, произойдет фингя: this_list->last не обновится, да и next_node->prev несуществующее будет переписано.
    Ответ написан
    2 комментария
  • Как найти последовательность букв через графы?

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

    Тогда постройте граф на n*m*l вершин. Каждая вершина соответствует тройке (x, y, k) и означает, что мы как-то походили по полю и оказались в координате (x, y), при этом набрав первые k символов искомой строки. Переходы в графе тут такие: от каждой вершины можно пойти в 4 соседние по координатам, а количество символов увеличивается на 1, если в следующей вершине читается следующая буква строки. Т.е. переходы вида (x, y, k) -> (x+1, y, k + (s[k] == grid[x+1][y] ? 1 : 0)); (x, y, k) -> (x, y+1, k + (s[k] == grid[x][y+1] ? 1 : 0)) и еще 2 на минус (если x,y не у стенки поля, естественно - некоторые переходы могут отсутствовать).

    Ищите кратчайшее расстояние из вершины (x0, y0, s[0] == grid[x0][y0] ? 1 : 0) в любую вершину с третим индексом равным l (т.е. любое место, где вы прочтете всю искомую строку). Поскольку в графе все ребра длины 1, можно запускать bfs. Граф строить и охранить не надо, можно эти 4 перехода вычислять неявно. Кладите в очередь тройки чисел, вынимайте их оттуда и вычисляйте до 4 соседних троек, которые гужно тоже сложить в очередь обхода в ширину. Удобно завести 2 костантных массива на 4 элемента, которые будут хранить приращения по x и по y в каждую их четрех сторон. Тогда не будет много дублируемого кода.

    Еще понадобится, таки, массив на n*m*l для хранения расстояния, или хотя бы пометок о том, что вершина уже в очередь была положена.
    Ответ написан
    2 комментария
  • Как "умножить" строку в си?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Руками. Циклом на x_len-2 итераций. Раз у вас там, судя по коду, есть обрамление, то лучше гнать цикл с 1.
    for (int j = 1; j < x_len-1; ++j) {
      map[i][j] = ' ';
    }


    Еще, конечно, можно извратиться с memset, но лучше ненадо. Это тяжело читать и можно налажать запросто.
    Ответ написан
    Комментировать
  • Как в ировом движке на C++ распаралерить функции Update и Render?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Если есть несколько потоков, то можно данные защищать через какой-нибудь mutex. Каждый поток перед тем как менять или читать данные, блокирует мьютекс, что-то быстрое делает, освобождает мьютекс. Лучше не держать его все время длинных вычислений, а, допустим, считать новые данные в локальных переменных, а потом в критической секции записать их в место, которое другой поток сможет читать.
    Ответ написан
    1 комментарий
  • Как выполнить сортировку слиянием списков несравнимых элементов?

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

    Наверно, это задача на сортировку при частичном порядке.
    Решается через топологическую сортировку графа. Для каждой "буквы" заведите вершину. Проведите направленные ребра между каждой парой соседних элементов в каждом списке. Топологически отсортируйте граф. Работает за линейную сложность.
    Ответ написан
    1 комментарий
  • Как управлять значением пикселей на экране в виндовс?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Гугли "GDI+"
    Это библиотека под винду, которя позволяет рисовать в окнах/на экране.
    Надо получить DeviceContext для экрана, и там рисовать что хочешь.
    Ответ написан
  • Логика игры "Пятнашки" на Python?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Надо, чтобы "четность" перестановки совпадала с четностью финального поля (1).
    Занумеруйте все 16 позиций слева направо сверху вниз.
    чтобы подсчитать четность, рассматривайте каждую пару заполненных позиций (15\*14/2=105 пар) - если числа идут не в том порядке (большее число на позиции с меньшим номером) - то прибавьте 1 к ответу. В конце возьмите ответ по модулю 2. Это и будет четность перестановки.

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

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Надо системное апи использовать. Вы, похоже, под виндой, так что LoadLibraryEx, GetModuleHandleEx, GetProcessAddress вам помогут. Первой вы открываете библиотеку. Второй можно потом пользоваться чтобы получить доступ к уже открытой библиотеке, если вы HANDLE не сохранили куда-то. Третья позволит вам получить указатель на функцию из библиотеки.

    Можно гуглить "имя функции example" и тогда вы найдете в интеренете готовый код, работающий с этими функциями.
    Ответ написан
    Комментировать
  • Как правильно задать интервал для формулы 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
    Разработчик на С++, экс-олимпиадник.
    Нет, блок схемы нигде, кроме как на парах застывших в советских учебниках динозавров, не используются.
    Ответ написан
    Комментировать
  • Как определить есть ли противоречия в цепочке логических выражений?

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

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

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

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

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

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

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

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Суть проста: У вас есть два набора из N чисел. Две строки чисел одинаковой длины, если хотите. Надо в каждом столбце (из двух элементов) наверх поставить максимальный, а вниз - минимальный.

    Для решения этой задачи надо уметь в массивы, циклы, условные операторы и уметь поменять местами два значения. Код тривиален: пройдитесь циклом от 0 до N-1 (ведь нумерация с 0) и, если элементы в заданном столбце идут не в том порядке, в каком должны, поменяйте их местами.

    Код напишите сами. Хотя бы попробуйте.
    Ответ написан
    1 комментарий
  • Функция _kbhit в C++?

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

    Но тут ситуация другая. Майкрософт что-то намудрили cо стандартами и названиями. Чую тут какую-то долгую и запутанную историю полную костылей и заплаток. Я так понял, что _kbhit, это winapi функция, а kbhit - это сишная функция в стандартной библиотеке. Вторая просто вызывает первую. Но нельзя было сделать две функции с одинаковыми названиями, поэтому эти winapi-шные функции оставили с _ в названии.
    Ответ написан
    1 комментарий
  • Почему выражение (-1ll) в ассемблерном коде MSVC равно ff ff ff ff?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Уловка в системе процессорных комманд: 48 c7 45 08 - позволяет загрузить в 64-битную ячейку памяти 32 битное число, автоматически расширяя его до 64 бит.

    Смотрите список кодов x64:
    0x48 - означает, что следующая комманда работает с 64-битами.
    0xC7 - mov immediate (данные в команде)
    Дальше идут флаги, указывающие как интерпретировать аргументы, что куда адресовывать и т.д.

    Но важно, что команда C7 работает с r/m16/32/64 данными, а аргумент у нее может быть только imm16/32 (третий столбец). Т.е. она принимает или 2 или 4 байта, в зависимости от обвеса, а записывать может до 64 бит. Сравните это с коммандой 0xB8 в той же таблице, она уже может принимать 64 бита.

    Если аргумент меньше ячейки памяти, то он расширяется (sign extended) до нужного размера (бит знака копируется влево до упора). Это позволяет записать числено равное значение в более битную ячейку. ведь 32-битное число 0xFFFFFFFF - это -1 в дополнительном бинарном коде, а 0xFFFFFFFFFFFFFFFF - это тоже -1 в дополнительном 64-битном коде.

    Компилятор использует вот эту команду, а не 0xB8, потому что сама команда короче, а исполняется так же быстро. Меньше кода, больше всего помещается в кеш и все работает быстрее, да и exe-шник меньше получается.
    Ответ написан
    2 комментария