• Как сделать ограничение максимума и минимума в генерации случайных чисел с помощью строк?

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

    Потом генерируйте посимвольно, считая, какие значения текущая цифра может принимать. Смотрите, если где-то вы сгенерируете цифру меньше maximum, то дальше может быть вообще что угодно. Ваше сгенеренное число уже меньше максимума. Так же с минимумом. Поэтому, поддерживайте 2 флага: "сгенрированная строка уже меньше максимума" и "сгенерированная строка уже больше минимума". Каждый следующий символ не должен превосходить цифру в максимуме, если не первый флаг. Также он не должен быть меньше минимума, если не второй флаг.

    Что-то вроде этого:
    bool smaller_than_max = false;
    bool bigger_than_min = false;
    for (int i = 0; i < n; ++i) {
       int left = bigger_than_min ? '0' : minimum[i];
       int right = smaller_than_max ? '9' : maximum[i];
       answer[i] = left + rand() % (right - left + 1);
       bigger_than_min |= answer[i] > minimum[i];
       smaller_than_max |= answer[i] < maximum[i];
    }


    Edit: тут все возможные числа не будут равновероятны. Чтобы они были равновероятны придется хорошенько поизвращаться.
    Ответ написан
    Комментировать
  • Знает ли кто-то системную DLL, которая не фиксирует себя в памяти при загрузке?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вряд ли тут какие-то хитрые методы остаться в памяти тут используются. Просто библиотека приложением итак используется. LoadLibrary лишь увеличивает счетчик использований, FreeLibrary просто уменьшает его, и ничего не загружается/выгружается. Попробуйте найти какую-то dll-ку, которая не выводится в списке в начале.
    Ответ написан
    1 комментарий
  • Как хранятся многомерные массивы в памяти?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Зависит от типа массива.
    int **a;
    // или vector<vector<int>> a;
    a[10][7];


    Тут происходит 2 разименовывания указателя. Массив в памяти хранится строчками. Каждая строка может быть где угодно. При этом дополнительно хранится массив указателей на строки (длиной с длину столбца). Поэтому такой массив занимает в памяти M*(sizeof(int*))+M*N*sizeof(int). Чуть сложнее для vector, но идея такая же.

    int a[10][3];
    a[4][5];


    Тут массив, хоть и многомерный, но фиксированного размера. Поэтому он хранится одним блоком. Компилятор знает длины всех строк и сразу вычисляет адрес конкретного элемента - сдвигаясь на (длину строки)*(номер строки)+(номер столбца). Он занимает N*M*sizeof(int).

    Сравните ассемблерный код.

    Кстати, именно поэтому вы не можете преобразовать int[4][5] к int**. И такой массив при передаче в функцию надо передавать по типу int[][5] (можно опустить количество строк. Ибо для адресации нужна лишь длина строк, но нестолбцов, но размер строки указать предется обязательно).

    arr[1][2] => *(*(arr + 1) + 2) Это действительно работает, потому что arr имеет тип int[][3] или int*[3]. Коспилятор видя arr+1, знает, что над сместится на 1 размер int[3]. * разыменовывает это, но при этом указывает на то же место. И получает просто указатель на int начало строки. Фактически тут просто меняется тип указателя с int*[3] на int*. +2 сдвигается в строке на 2 размера int.
    Ответ написан
    Комментировать
  • Как исправить ошибку Е0028 Выражение должно иметь константное значение на С?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    С вообще говоря, не умеет выделять массивы произвольного размера на стеке (вот так как у вас x локальная переменная). Ему надо знать размер массива во время компиляции. Вот про это он и ругается, n - не константа.

    Есть расширение VLA, которое есть в стандарте C99. С ним вот такие вот массивы можно заводить. Оно включено не во всех компиляторах из коробки. В некоторых его вообще, наверное, нет.
    Попробуйте передать компилятору в ключах флаг -std=c99.

    Или, выделяйте массив через malloc (не забудьте free сделать потом).

    Еще, можно завести достаточно большой глобальный массив фиксированного размера.
    Ответ написан
    Комментировать
  • Верно ли я понял процесс преобразование целого типа данных в символьный на C?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Не совсем. Действительно, вы получаете в x код символа '4'. Но вы не преобразуете его в символьный. Это все еще int (скорее всего, 4-х байтный). В данном конкретном случае, это работает. Но тут вам повезло, что printf может вместо char принять int и правильно его синтерпретировать. Правильнее было бы преобразовать к char перед передачей в printf:
    printf("%c", (char)x);

    Вот тут уже действительно происходит преобразование к символьному типу char.

    Edit:
    Немного не прав был. printf надо передавать int, даже если формат "%с". Но какой-нибудь другой функции может понадобится именно char.
    Ответ написан
    5 комментариев
  • Как решить задачу, на оптимальную нагрузку курьера доставки?

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

    Надо будет построить граф. Взять все точки доставки и склад как вершины и каким-нибудь google maps найти расстояние и пути между каждой парой точек. Эти длины пути надо записать в ребра.

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

    Если у вас точек и куръеров действительно мало (не более 25 точек, не более 3 куръеров), то можно попробовать решать через динамическое программирование F(M, c1, c2, c3) - минимальная стоимость развезти товары из множества M так, что 3 куръера остаются в вершинах c1, c2 и c3. Переход - перебрать куръера и откуда он пришел из множества M. Посмотрите в википедии алгоритм для одного куръера, на трех это легко обобщается. Будет это работать за O(n^(c+1)2^n), где c - количество куръеров. n - количество вершин в графе. Сильно много куръеров или точек на карте этот алгоритм не переварит.
    Ответ написан
    Комментировать
  • Не могу понять, правильно ли я ввел формулу?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Какое значение эта формула принимает при x=1, например?

    Ну и у вас условие в бинарном поиске неправильное. Если f(a) и f(c) принимают разные значения, то корень находится между a и c. Вы же переходите к отрезку [c,b].
    Ответ написан
    2 комментария
  • Не удается открыть семафор, в чем ошибка?

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

    Может, там, вполне NULL возвращается. И да, лучше HANDLE не сравнивать с nullptr. Это, все-таки, не указатель.
    Ответ написан
    1 комментарий
  • Почему вектор перемещения не поддается правилу треугольника при вычитании векторов?

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

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

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

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

    Вот как вы себе пердставляете перемещение int*?
    int* - это адрес в памяти. Число. Когда вы "перемещаете" img этого типа, вы перемещаете одно число. Из переменной img, в вектор.

    При этом что там лежит в памяти по адресу, равному этому числу (или на 20 сдвинутому), вообще не поменялось.

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

    Так, у вас в imgs вы не пихаете копию данных, а пихаете указатель.
    Ответ написан
    2 комментария
  • Как найти 7 неизвестных?

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

    Сходу очевидно, что x=y=a+b=c+d+k дает по нулям все левые части, что удовлетворяем всем неравенствам.

    Так что, берем x=y=a=c=1, b=d=k=0

    Плюс, можно менять 1 на что угодно. Можно распределять эту 1 между a и b, а так же между c, d и k. Бесконечно много решений.

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    В чем проблема? Если бы у вас в структуре было поле типа int, как бы вы получали значение этого поля? вот точно так же, только у вас тип не int, а char*

    struct Foo {
    char* data;
    };
    
    Foo* bar;
    char* pointer = bar->data;
    Ответ написан
    Комментировать
  • Где могла закрасться ошибка?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    В вашем рассуждении ошибка в том, что вы не домножаете условную вероятность 2/6 на вероятность условия. Вы же полную вероятность считатете а P(A) = P(A|B)*P(B)+P(A|!B)*P(!B). У вас P(A|B) = 0, как вы заметили - если они встретились в первом туре, то во втором уже не встретятся. Но все-равно надо 2/6 домножать на P(!B) - вероятность того, что они не встретились в первом туре. А это 1-1/7 = 6/7. В итоге получается все то же 2/6*6/7=2/7.

    Но рассуждения автора проще. Можно рассмотреть все независимые элементарные исходы "где в сетке оказался второй игрок". Их 7, они равновероятны по симметрии. Дальше надо домножить на вероятность, что они начиная так встретятся (выиграют свои матчи).
    Ответ написан
    4 комментария
  • Почему 3 секунд не хватает для выполнения кода?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    У вас решениe за O(n^3), ибо у вас там 2 вложенных цикла до n, а внутри еще и постоянно вызываются min/max, которые проходятся по всему массиву.

    Ограничения же в задаче n<10^5. С такими ограничениями максимум O(n log n) решение уложится в 3 секунды.

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

    Что можно сделать с входным массивом, чтобы можно было получать несколько самых маленьких элементов быстро? Помните, что вам надо уложиться в O(n log n).
    Ответ написан
    Комментировать
  • Как обрабатывать переполнение счетчиков и индексов?

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

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

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

    - Wraparound. Часто вам не важно абсолютное значение счетчика. Нужно лишь знать, какое из двух значений больше. При этом вы знаете, что сравниваемые значения не могут быть слишком большими. Тогда очень маленькое значение будет считаться больше очень большого. Два примерно одинаковых будут сравниваться как обычные числа. Так можно делать, например, с sequence number пакетов. Если вы получите пакет с номером 0x02 после покета с номером OxFFF1, Можно с спокойно считать, что это номер после переполнения. Ну не будет сеть задерживать перетасовывать 65 тысяч пакетов.
    Ответ написан
    1 комментарий
  • Как найти компоненты связности в графе в распределенной памяти?

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

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

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

    Более сложный вариант - когда у вас сразу запускаются все волны. Опять же, без лидера совсем сложно будет.
    Тут, наверно стоит сначала в каждом отдельном параграфе найти все компоненты связности отдельно. Это будут промежуточные варианты.

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

    Когда сервер получает сообщение о номере по призначному ребру, если этот номер меньше текущего у компоненты, она перекрашивается. И сообщение рассылается по всем остальным призрачным ребрам, торчащим из этой компоненты. Рано или поздно, все вершины одной компоненты по всем серверам будут помечены минимальным номером.

    Чтобы определить, что все закончилось, можно разбить процесс на фазы. В первой фазе все рассылают свои номера. Во второй фазе только те, у кого номера обновились. И т.д. Фаза на сервере начинается после сообщения от лидера. Каждый сервер отправляет на лидер сообщение, когда он закончил рассылать свои номера и были ли там вообще обновления. Сообщения надо делать по tcp с подтверждением. Когда лидер заметит, что никто никаких сообщений больше не рассылал в предыдущей фазе - все сошлось.
    Ответ написан
    1 комментарий
  • Как правильно подключить #include?

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

    Надо forward declaration расставить кое где. Чтобы не было зависимости от порядка include в файлах, стоит, наверно, расставить их в обоих файлах:
    class c_image;
    class c_window {
    // ...
    }


    И для красоты стоит image.hpp включать не в window.hpp, а window.cc. Ну и для второго класса аналогично сделать.

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

    Edit: Да, ошибка возникает потому, что в каком-то cc файле сначала вставился image.hpp, из которого вставился window.hpp (внутренний image.hpp в нем не вставляется из-за pragma once. Иначе бы была бесконечная рекурсия). В итоге у вас в .cc файле сначала идет определение класса window, и только потом класса image.
    Ответ написан
    Комментировать
  • Какая обёртка позволяет разыменовывать без неопределённого поведения?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ну просто проверяйте в operator*: если nullptr, то бросайте исключение.
    Ответ написан
    2 комментария
  • Издержки полиморфизма или неправильный дизайн?

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

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

    Если же вы действительно хотите брать вот такие совершенно разные по смыслу значения у разных наследников, то, да, в интерфейсе должны быть все функции. Можно в интерфейсе их определить с ассертами и переопределить только в нужных наследниках.
    Ответ написан
    1 комментарий