• Как решить задачу "Шестерки" с меньшими затратами памяти?

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

    Подсказка, вы получите вот такие вот числа:
    36
    4356
    443556
    44435556
    4444355556
    444443555556
    44444435555556
    4444444355555556
    444444443555555556
    44444444435555555556
    4444444444355555555556
    444444444443555555555556
    44444444444435555555555556
    4444444444444355555555555556
    444444444444443555555555555556
    44444444444444435555555555555556
    4444444444444444355555555555555556
    444444444444444443555555555555555556
    44444444444444444435555555555555555556
    4444444444444444444355555555555555555556


    Этот паттерн можно и доказать. Надо лишь представить возводимое в квадрат число как (10**n-1)2/3 и применить чуть-чуть элементарной алгебры, вроде формулы квадрата разности.
    Ответ написан
    Комментировать
  • Что быстрее индексы или указатели?

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

    Практический совет - лучше писать через индексы, ибо так понятнее и больше шансов что компилятор там все наоптимизирует (например, он сможет векторизовать работу через какие-нибудь SSE инструкции процессора).

    Совет по бенчмарку - если памяти не хватает, стоит по одному достаточно большому массиву пройтись 10000 раз. А лучше использовать готовые фреймворки для измерения скорости, вроде того де gbenchmark.

    Еще, иногда полезно посмотреть на ассемблерный выхлоп. Вот, например, что происходит при -O3 опции компилятора. Он генерирует вообще идентичный код для обеих функций (развернув циклы)! И даже при -O2 оно одинаковый код выдает.

    Без оптимизаций код разный, но там все не так как вы думаете. Вместо инструкции mov eax, dword ptr [rax + 4*rcx] в варианте с индексами используется инструкция mov eax, dword ptr [rax] для указателей. Это самое "складывание с указателем массива" вообще не отдельная операция - а вариант адрессации в инструкции mov. Они могут вообще одинаковое количество тактов занимать, это надо мануал по конкретной архитектуре процессоров читать.
    Ответ написан
    Комментировать
  • Как правильно рассчитать коэффициент полезного использования пространства?

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

    Можно ускорить алгоритм сжатием координат: вввпишите все x и y координаты всех углов блоков, отсортируйте и унифицируйте (отдельно по каждой оси). Потом замените все координаты в блоках на порядковый номер в массиве уникальных координат. Примените алгоритм выше, но в конце надо помнить, что каждая ячейка теперь не 1x1, а сколько-то больше по вертикали и горизонтали.
    Ответ написан
  • Как обеспечивается совместимость динамических библиотек при ликовке в рантайме?

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вообще, работу с указателем на base тоже надо переписать через memcpy. Потому что это UB. Вдруг выравнивание у массива не такое, как у структуры Base. Без memcpy тут никак. Другого способа разместить 2 структуры в памяти подряд компактно - нет.

    Edit: конечно, можно объявить вашу стоуктуру где поля base и S1 идут рядом, но там будет какой-то padding. Работать с этим как с сериализированным массивом байт - нельзя.
    Ответ написан
    1 комментарий
  • Задача о рюкзаке, используя 1-D массив?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вы не правы. Двумерный массив считает, можно ли набрать вес i используя первые j предметов. А не j-ый предмет.
    Далее, обычно можно сэкономить память храня и пересчитывая только одну строку этого массива, но ДП все еще остается двумерным. Также числа Фибоначчи можно считать храня лишь 2 последних числа, но на самом деле там массив на n элементов в дп.

    Этот трюк не всегда возможен. Например, если вам надо не только максимальный вес/цену найти, а еще и набор предметов. Восстановление ответа не всегда можно сделать лишь по последней строке.
    Ответ написан
  • Как найти расстояние от точки до вектора?

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Есть костыль:
    #define Class3 Class3_Unused
    #include "module1.h"
    #undef Class3


    Таким побразом при включении module1.h вместо Class3 будет объявлятся какая-то хрень, которую нигде вы использовать не будете.

    Правда, все ломается, если у вас этот module1.h включен по цепочке других инклудов. Надо аккуратно в каждом месте, где вы его включаете так же обарачивать в define.

    Но по уму, это большой косяк авторов module1 и module2, что они не используют namespace. Их надо бы переписать.
    Ответ написан
    1 комментарий
  • Как сделать расчёт пройденного расстояния лучом?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Формула: W/sin(a). Ну, или косинус, в зависимости от того, что вы за угол задаете. W - ширина прямоугольника.

    Вывести формулу просто со стандартным трюком: вместо отражения луча, отражайте зеркальную комнату, а луч пусть идет прямо. Тогда луч просто пройдет вдоль кучи вертикально сложенных одинаковых прямоугольников.

    Формула осмысленна: если нет отражений, она очевидна. Чем вертикальнее луч, тем больше ответ.

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    727/x. Для 720 получается 1.0097222222...
    Но вообще, если это аудио, то можно паузы попробовать повырезать в какомнибудь audacity руками.
    Ответ написан
    Комментировать
  • Как решить подобную задачу?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ошибка, если интересно, в том, что в коде написан & вместо %. Соответственно, пропускаются не числа, которые не делят n, а числа, дающие не 0 в побитовом И с n.

    Вообще, не понятно условие задачи. Какие там ограничения? Так-то можно вот прям этот код взять и тупо вставить в исходник и посмотреть, что он вернет.

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

    Совсем быстрое решение - это динамическое программирование по бинарным цифрам корня. Это весьма сложный алгоритм. Сработает, в общем-то, для n до 2^64. Считайте F(l,f) - количество способов как-то расставить первые l бит числа, т.ч. все биты, единичные в n, взяты по 0, и число не превосходит корня из n, а f - флаг, означающий, что число уже строго меньше корня.

    Легче считать это циклом снизу вврех. Смотрите, какую цифру можно дописать к (l,f): если в n стоит 1 в этом бите - то только 0. Иначе, можно дописать 1, только если f=1 или в sqrt(n) стоит 1 в этом бите. Новый флаг f' будет 1, только если f=1, или вы поставили 0, а в sqrt(n) стоит 1.

    Потом ответ надо домножить на 2 и вычесть 1, если (int)sqrt(n) & n == 0.
    Ответ написан
    1 комментарий
  • Как убрать дублирование чисел в бинарном файле на си?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    С чего вы взяли, что туда записывается два числа 7?
    Туда записывается одно число размером в 4 байта, которое, если его интерпретировать как строку, выглядит как "7 7 ".

    Вы числа читаете и пишите в бинарном формате. Соответственно, файл input.bin у вас должен в hex редакторе выглядеть "0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x07...", а не то, что у вас.
    Ответ написан
  • Как найти наикратчайшие пути взвешенного орграфа, представленного матрицей инцидентности, используя алгоритм Дейкстры?

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

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

    Кстати, у вас матрица инцидентности неправильная: должно быть по 2 числа в каждой строке. Обычно ставят -цену у начальной вершины и +цену у конечной.
    Ответ написан
    4 комментария
  • В чем разница записи массива через указатели?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Проблема в том, что * выполняется перед вычетанием. *(current-1) - это предыдущий элемент. (*current-1) в плохой версии - это текущее значение минус 1.
    Ответ написан
    Комментировать
  • Как преобразовать char* содержащий символы unicode?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вам надо \uXXXX преобразовать в char16_t? Можно это руками делать. Берете по 6 байт из строки. Последние 4 руками преобразуете из 16ричной системы. Если символ от a до f, то прибавляете к нему 10-'a'. Если от 0 до 9 - 0-'0'.

    Удобно это циклом делать, сдвигая ответ на 4 бита влево и прибавляя новый символ:
    std::wstring Parse(const std::string encoded) {
      std::wstring result;
      for (int start = 0; start < encoded.length(); start += 6) {
        if (encoded[start] != '\\' || encoded[start+1] != 'u') return result // строка неправильного формата.
        char16_t nxt = 0;
        for (int i = start +2; i < start+6; ++i) {
          int cur = 0;
          char &chr = encoded[i];
          if ('0' <= chr && chr <= '9') cur = chr - '0';
          if ('a' <= chr && chr <= 'f') cur = chr - 'a' + 10;
          if ('A' <= chr && chr <= 'F') cur = chr - 'A' + 10;
          nxt = (nxt << 4) + cur;
        }
        result += nxt;
      }
      return result;
    }
    Ответ написан
  • Как можно улучшить код?

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


    Разбейте на функции. Хотя бы выделите основной алгоритм в функцию, которая возвращает вектор из цифр. Отдельную функцию для вывода. символов. Минус можно вывести отдельно.
    Ответ написан
    Комментировать
  • Как правильно округлять числа с плавающей точкой с заданной точностью?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Очень многие числа нельзя записать в виде числа с плавающей запятой, потому что они не представимы в виде доичной дроби с заданным количеством знаков. Например вы 1/3 как десятичную дробь не запишите. Ибо там 0.3333... - бесконечная последовательность троек. Для хранения double выделенно сколько-то бит под мантиссу, но их сильно меньше бесконечности.
    Поэтому ни 1/10, ни 3/100, ни 1467/1000 и почти любая дробь будет непредставима в виде float. Поэтому, если вам нужна точность, то вы округляете до целого и храните количество, скажем, тысячных долей в целых числах. Как сумму денег вы храните не долларах, округленных до 2 знаков, а как целое число центов.
    Ответ написан
    Комментировать
  • В чем проблема в коде работы с графом?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ошибка в том, что у вас граф криво задан. У вас там ровно 6 списков. А размер графа - 7. Поэтому в MakeStep вы обращаетесь к graph[6], а это undefined behavior. А там непонятно, что происходит. Может вы таблицу портите каким-то значениями и никогда положительного значения не получаете. Скорее всего корраптите память через обращение к мусорным adj в std::array (который на стеке лежит) и получаете мусор в каких-то локальных переменных.
    Ответ написан
  • Компиляция C++ кода на Ubuntu и Windows даёт мне разный результат, почему?

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

    Советую переписать код. Возвращайте std::array<std::bitset<16>,4> например. Вместо push_back сразу обращайтесь к i-ому элементу ответа.

    Далее, у вас там 2 функции делают одно и то же - разбивают большой bitset на несколько маленьких. Напишите template функцию, которая выделяет из битсета длиной N битсет длиной M со сдвигом offset. Внутри просто циклом присваивайте res[i] = input[i+offset].

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

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

    Т.е. действительно, в вашем случае надо в AIJH добавить точки ABKJH.

    Образуются ребра длины 0 - значит повторяющиеся вершины надо просто выкинуть.
    Ответ написан
    Комментировать