Задать вопрос
  • Как делать спиральные массивы?

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

    Выписываете прямые куски - какой они длины, в какую сторону вращаются. Ищите паттерн (например в спиральном массиве из центра длины кусков: 1, 1, 2, 2, 3, 3, ...).

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

    Для реализации поворота храните текущее направление в виде двух переменных dx, dy. Одна из них +-1, а другая 0. Для поворота против часовой стрелки делаете (dx, dy) = (-dy, dx)
    Ответ написан
    Комментировать
  • Как найти внутри текстового файла слово?

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

    Быстрые алгоритмы есть: Кнута-Морриса-Пратта, Бойера-Мура. Там есть примеры реализации алгоритма в википедии прямо кодом на Си. Эти алгоритмы сканируют строку текст посимвольно. Эту часть нужно тупо заменить вводом очередного символа из файла (не забудьте сделать буферизацию, если ваш ЯП этого не делает сам).

    Ну, или, если файл не большой, то можно тупо прочитать весь файл в строку и запустить какой-нибудь встроенный метод поиска подстроки в строке. Большинство ЯП уже реализуют какой-либо быстрый метод поиска.
    Ответ написан
    Комментировать
  • Где в решении задачи ошибка?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Инициализируйте не нулями, а двумя первыми числами и min1|2 и max1|2. Не забудьте их в нужном порядке сделать (num1max>=num2max). И цикл с 2 гоните, а не от нуля при этом.

    Например на тесте {1, 0} у вас программа может вывести 2 нуля.

    Потому что num1min и num2min останутся нулями а оба произведения по нулям.
    Ответ написан
  • Как быстро проверить, является ли некоторое огромное число (от 100 знаков) квадратом целого числа?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Можно попробовать вычислить корень быстрым алгоритмом. Но там сложно. Гуглите Karatsuba square root. Есть открытые реализации. Есть еще какой-то адский метод через быстрое преобразование Фурье, попробуйте погуглить и его.

    Более простой в реализации, но менее быстрый метод вычисления корня - бинарный поиск. Храните l, r, l^2, r^2 и lr. По этим числам можно вычислить m=(l+r)/2, m^2, m*l, m*r без длинных умножений, а только складывая длинные числа и деля на 2. Вам надо поддерживать, чтобы l^2 <= n <= r^2. Изначально можно сделать l=1, r=10^51 (или больше - половина длины входного числа + немного, чтобы точно квадрат был больше n), потом делить отрезок пополам и отбрасывать ненужную половину.

    Еще есть вероятностный метод через символ Лежандра/Якоби. Это будет самым быстрым методом.

    Это как смотреть на последнюю цифру. Квадраты могут давать там 0, 1, 4, 9, 6, 5. Но нет ни одного квадрата, который оканчивался бы на 2. Т.е. если число заканчивается на 2, то можно сразу говорить, что это не квадрат. Это мы взяли остаток от деления на 10 (последняя цифра) и посмотрели, какие из них хорошие. Вот символ Лежандра - это такая проверка для модуля по любому простому числу (а не 10).

    Если брать некоторое простое число p, то n может быть квадратом, только если символ Лежандра (n/p) - равен 1 или 0 (По научному: n - должно быть квадратичным вычетом).

    Если брать небольшие (<64-битные) простые числа, то можно за линию считать n%p и потом вычислять символ Лежандра (n%p/p) по алгоритму через символ Якоби за O(log(p)^2). Когда подсчитали символ Лежандра и если он -1, то n - точно не корень.

    Тут проблема в том, что это необходимый, но недостаточный критерий - если для какого-то p вы получили -1 - то это точно не квадрат. Но возможно можно подобрать такое число, что все ваши тесты дадут 1, а оно не квадрат. Поэтому надо брать много простых чисел. Скажем, 20. Желательно еще числа брать достаточно большими. Но их не надо искать каждый раз, можно захардкодить. Грубая прикидка говорит, что вероятность ошибки такого алгоритма 2^(-количество простых чисел).

    Т.е. берете много простых чисел. Считаете для каждого n%p выполняя деление большого числа на короткое (один проход по массиву цифр). Потом считаете символ Лежандра. Если получили где-то -1 - то точно не квадрат. Иначе - скорее всего квадрат.

    Можно совместить вероятностный тест и вычисление корня. Сначала проверьте парочку простых чисел на символ Лежандра для отсечения точно не квадратов. Еще проверку последней цифры можно сделать, это очень дешево. Если не отсеклись, то считайте корень. Так будет всегда работать правильно но будет быстрее работать в некоторых случаях.
    Ответ написан
    Комментировать
  • Перевод фото таблицы в матрицу из 0 и 1?

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

    На изображении квадратики размера 2x2, между ними один пиксель пустой. И по границам еще пустые пиксели. Если обрезать 1 пиксель справа и снизу, то получится, что каждой ячейке соответствует квадрат 3x3: или целиком белый, или с 4-мя черными пикселями в правом нижнем углу.

    Соответственно, достаточно проверять пиксель с координатами 3*i+1, 3*j+1 для получения значения ячейки [i][j] (все индексы с 0).

    Т.е. в вашем языке программирования получаете изображение в виде матрицы пикселей, потом двумя циклами до 100 заполняете все ячейки матрицы - смотрите на нужный пиксель и, если он не белый, ставите 1.

    Если изображение с артефактами сжатия, то можно брать среднее между 4-мя пикселями 3*i+x, 3*j+y (x,y=1..2) по всем трем компонентам (RGB) и смотреть, что оно сильно отличается от черного. Для этого делаете еще 2 вложенных цикла по x и y, там прибавляете значение цветов пикселя в переменную, потом делите на 12 и сравниваете, допустим, с 200. Если меньше - это черный квадрат, если больше - то белый.
    Ответ написан
    2 комментария
  • Fopen segmentation error?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Еще ошибка:
    struct BMP_HEADER *header;
    ...
     fread(&header,sizeof(BITMAPFILEHEADER),1,fp);


    header - это указатель. 8 байт (на 64-битных системах). Вы в указатель (переменную-адрес) читаете структуру BITMAPFILEHEADER, сколько бы байт оно не было. Вряд ли это то, что вы хотели сделать.

    Надо выделять BMP_HEADER через malloc или на стеке (как локальную переменную, а не указатель). И передавать в fread указатель на структуру, а не адрес указателя. То же самое со второй структурой.
    Ответ написан
    Комментировать
  • Как вращать кривую Безье в функций на WinAPI?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Для поворота достаточно перед SetPixel повернуть xu и yu на угол, пропорциональный прошедшему времени. Ну или количеству тиков, но лучше по таймеру ориентироваться.

    Чтобы окно перерисовывалось периодически само надо делать как тут: запустить таймер на несколько миллисекунд и по его срабатыванию перерисовывать окно. Можно или как в этом примере стирать и рисовать заново, а можно вызывать WM_PAINT через какой-нибудь RedrawWindow:
    RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
    Ответ написан
  • Как Исправить код?

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

    Можно делать так вместо ZeroMemory:
    STARTUPINFO info={sizeof(info)};
    Ответ написан
  • Как удалить запись из структуры?

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

    Для удаления надо пройтись по всему списку - это уже делается в функции main при выводе библиотеки (кстати, там не нужен pLibrary. Можно совместить цикл while и цикл for после него. Вы проходитесь циклом по элементам списка, кладете их в массив и потом проходитесь по массиву. Достаточно просто делать с ними, что вам надо прямо в первом цикле).

    Потом, вместо вывода сравнивайте название текущей книги с введенным с клавиатуры (функция strcmp). Если совпало, то надо предыдущему элементу в next присвоить next текущей записи и потом вызвать free() от текущей записи и вывалиться из цикла через break.

    Да, единственная сложность - надо поддерживать указатель на предыдущую запись, а лучше даже на next у предыдущей записи (это будет LIBRARY**). Тогда для удаления надо просто head->next записать туда и текущий элемент выпадет из списка. Перед переходом к следующему элементу в цикле while просто перезапишите этот указатель на &head->next. Изначально он должен быть &head. Таким образом можно удалить даже первый элемент списка без разбора случаев.
    Ответ написан
  • Как вычислить количество слов, которые начинаются с большой буквы, подобное этому коду?

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

    Вам могут понадобится функции isalpha() isspace() isupper() из файла ctype.h
    Ответ написан
    Комментировать
  • Какой алгоритм сортировки односвязного списка?

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Диагональ, которая идет слева сверзу вниз вправо - это j == i. Вторая диагональ - это j == n-1-i.

    Соответственно, штриховка это когда j лежит между n-1-i и i включительно. Надо оба знака развернуть. Только сначала замените < N... на <= N-1...
    Ответ написан
    Комментировать
  • Как сделать реверс масива на С++?

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

    void Reverse (int *a, int x, int y) { 
      int i = y+1;
      int j = x-1;
      while (i < j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
        i++;
        j--;
      }
    }


    Или, если хочется, можно покороче:
    for (int i=x+1, j = y-1; i < j; i++, j--) {
      std::swap(a[i], a[j]);
    }


    Или совсем просто, если это не задание а для дела нужно:
    std::reverse( &a[x+1], &a[y]);

    Только надо обязательно проверить, что x < y - что есть что разворачивать.
    Ответ написан
    1 комментарий
  • Как можно найти все пути между вершинами графа networkx?

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

    Готового решения нет, потому что это довольно редкая задача - получить все пути. Кстати, даже без самопересечений путей может быть экспоненциально много. Уже для для 15 вершин есть тривиальный граф, в котором почти 100 миллиардов простых путей между любыми двумя вeршинами.

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

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

    Что-то типа такого. Я не питонист, так что возможно с ошибками, но идея должна быть понятна.

    def dfs(v, end, graph, path, visited):
      if v == end:
        print(path)
        return
      for u in graph.neighbours(v):
        if u not in visited:
          path.append(u);
          visited.add(u);
          dfs(u, end, graph, path, visited)
          path.pop();
          visited.remove(u);
    
    // вызывать dfs(start, end, graph, [], set())
    Ответ написан
    Комментировать
  • Как из циклического сдвига вправо сделать циклический сдвиг влево?

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

    Вариант чуть получше - вместо разворота, представить, что массив развернут. Тогда при любом обращении к элементу номер x надо обращаться к элементу n-1-x. Т.е. тупо в программе, где сдвиг делается(после ввода), каждый arr[x] заменить на arr[n-1-x] (примеры x у вас там - n-1, i+1, i, 0).

    Вариант еще лучше - включить голову и просто подумать. Сдвиг влево - это в сторону уменьшения индекса. arr[0] становится arr[1], arr[1] - arr[2], и так далее. arr[n-1] становится arr[0]. Т.е. надо в буфер запомнить arr[0], пройтись циклом по возрастанию i и присвоить каждому элементу следующее за ним (+1 к индексу) значение. Потом arr[n-1] заполнить из буфера. На самом деле получится то же, что и в предыдущем варианте, но без лишних вычислений индексов.

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

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

    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 Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Звучит, как бинарный поиск. Изначальный шаг - половина длины массива (128).
    Следующий, половина от этого и так далее. Так будет максимум 8 проверок всего.

    Если же надо делать обязательно +1 вторым шагом, то можно вычислить максимальное количество проверок, если первый шаг - X, а длина массива - N.

    Первых проверок будет максимум - floor(N/X), вторых (с шагом +1) - X-1.

    Можно примерно подсчитать в вещественных числах и попытаться минимизировать f(x) = N/X+X.

    Можно найти производную f'(x) = 1-n/x^2. Ноль у этой производной при x=sqrt(n).

    Отсюда получается, что примерно sqrt(256)=16 - искомая длина. Максимальное число проверок - 32.
    Ответ написан
    3 комментария
  • Кто и когда использует бинарные деревья и другие структуры данных?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Иногда вам может понадобится что-то не совсем стандартное. Например, возможность быстро вставлять элемент на k-ое место в структуре и находить, что лежит на k-ой позиции. Это делается деревом по неявному ключу, но, по моему, ни один язык не предоставляет стандартной структуры для этого.
    Ответ написан
    2 комментария
  • Где собрать решение?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Есть такая штука: https://chromium.googlesource.com/infra/goma/server/

    Позволяет собирать большие проекты параллельно на куче машин. Да, эти 30 минут сборки все равно придется потратить. И никто вам бесплатно вычислительные мощности под это не даст. Придется свои сервера настраивать.

    Еще есть вариант переструктурировать ваш проект, что бы при небольших изменениях понадобилось бы собирать лишь малую часть объектников. И 2 гигабайта в одном файле - это какой-то перебор. Если вы тесты разобъете на много логически обособленных частей, то есть шанс, что сборка сильно ускорится. Да, придется запускать больше файлов, но это сделать просто.
    Ответ написан
    Комментировать