Задать вопрос
  • Как решать задачу, пожалуйста?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Нужно хотя бы 5 интегралов. Возьмем какую-нибудь пятерку людей. Если исключить первого, какой-то интеграл, взятый первым человеком, будет не взят по условию всеми остальными. Если исключить второго, будет не взят какой-то другой интеграл (ведь прошлый интеграл отсутствовал в четверке 2,3,4,5). Аналогично, можно взять еще 3 недостающих интеграла, исключая оставшихся трех людей. Итак, мы насчитали 5 каких-то уникальных интегралов, а значит их хотя бы 5.

    Также можно составить пример с 5-ю интегралами: {{1},{2},{3},{4},{5}} - 5 студентов, 5 интегралов, каждый взял совой интеграл.

    Вот и получается, что 5 - минимальное количество.
    Ответ написан
  • Reinterpret_cast вектора типа double в T неопределенное или определенное поведение?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это Undefined behavior, конечно. Пока луна не в скорпионе, оно даже может работать, потому что расположение в памяти Time и double одинаковое, и вроде как даже vector для этих двух типов будут в памяти одинаковы.

    Тут проблема даже не reinterpret_cast, кастовать-то можно: пункт 6, а потом пункт 5.

    Обращаться к этому потом нельзя.
    Тут нарушение strict aliasing. Нельзя к данным vector<Time> обращаться через указатель на vector<double>. Компилятор в праве посчитать, что вы к вектору v вообще не обращаетесь нигде и оптимизировать его нафиг из программы и памяти.

    Раз расположение в памяти идентичное, вы можете выделить вектор Time нужного размера и скопировать туда данные через memcpy:
    memcpy(&t[0], &v[0], sizeof(double)*v.size());

    Или напишите свою функцию в виде шаблона.
    template <typename T> 
    std::vector<T>Interpolate(vector<T> data) {
      // ...
    }


    Тогда она может и double и Time принимать без проблем.

    Или напишите конструкторы для Time, И можно его инициализировать через список инициализации:
    struct Time {
            Time() : x(0) {}
            Time(double a) : x(a) {}
            double x;
        };
    
    std::vector<Time> v = {1,2,3};


    С такими конструкторами можно даже скопировать vector<double> в vector<Time>:
    std::copy(a.begin(), a.end(), std::back_inserter(v));


    Edit: ну, и наоборот, если вы хотите из вектора Time получить вектор double, вы должны определить operator double() у вашего Time. И оттуда уже можно будет копировать в дабловый вектор. И работать это будет даже если класс Time содержит еще чего-то кроме double.
    Ответ написан
    3 комментария
  • Заголовочные файлы в Си нужны только для интерфейса?

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

    Вам надо сказать компилятору, что вот такая функция есть и у нее вот такие вот параметры.

    Можно просто в main.c написать то, что вы бы написали в math_functions.h. Но это быстро становится сложно, если у вас проект большой и функции используются в разных файлах. Надо будет эти объявления копировать в кучу мест. А если вам еще и поменять что-то надо потом, вы офигеете. Для этого и придумали заголовочные файлы - вы пишите объявление один раз и потом его везде используете.

    Вообще, сейчас не обязательно math_functions.h включать и в math_functions.c. Раньше надо было определять функцию даже если ее объявления нигде нет. В современных стандартах это не так. Но все равно хедеры включают в соответствующий файл, чтобы ловить ошибки. Если вы поменяете функцию в .c но не поменяете в хедере, компилятор заметит несоответствие объявления и определения и сообщит об ошибке.
    Ответ написан
    6 комментариев
  • Какие данные берет функция для генерации случайного числа?

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

    В онлайн играх сулчайность должна быть одинакова для всех игроков - если вам генератор покажет 10 единиц урона при ударе, а соседу - 20 для этого же самого удара, то в у вас монстр останется жив, а у соседа умрет, что плохо. Плюс, манипуляции со случайностью - это пространство для читов. Поэтому в онлайн играх своя специфика и там имеет смысл случайность оставлять на сервере, да.
    Ответ написан
    1 комментарий
  • Как решить ошибку "Код инcтрументирования объектов cookie стека обнаружил переполнение буфера, связанное со стеком"?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Давайте код функции текстом вставляйте. Ошибка говорит о том, что вы где-то вылезли за границы массива на стеке (локального), и перезаписали то, что не нельзя было перезаписывать. Ошибка в логике программы и работе с памятью.
    Ответ написан
    Комментировать
  • Файл cpp не видит библиотеку из другого файла как решить проблему?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вы про ошибку "Use of undeclared identifier 'cin'"?

    Это потому что cin и cout находятся в пространстве имен std. Надо или писать std::cin, или использовать using namespace std;. Но вот эта вторая конструкция у вас написана в файле main.cpp, только в нем и действует. Она не распространяется на исходник функции fu1, которая находится вообще в другом файле.

    Компилятор отдельно компилирует каждый cpp файл, генерирует машинный код с пометками, где какая функция лежит. Что вы там в main.cpp понаписали в io.c не имеет никакого значения.
    Ответ написан
    2 комментария
  • Как правильно реализовать блок памяти для сильной и слабой ссылки?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Тут есть два варианта: 1) вы вызываете деструктор, но не освобождаете память, когда все сильные ссылки кончились. В этом случае надо воспользоваться type erasure, чтобы выключить деструктор. Деструктор же вы вызываете вручную, когда надо, через reinterpret_cast(storage_)->~T(); Посмотрите вот тут пример: https://chromium.googlesource.com/chromium/src/bas...
    2) Вы вызываете деструктор и освобождаете память. В этом случае Value обязано быть в другом блоке памяти, а в вашем умном указателе будет лишь указатель на него. Ибо нельзя никак вернуть часть блока.
    Ответ написан
    4 комментария
  • Почему не работает передача контекста между приложениями?

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

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

    Чтобы что-то делать с ресурсами другого приложения вам надо эти ресурсы передать средствами операционной системы, через хэндлы (на винде).
    Попробуйте получить хендл окно с контекстом, потом как-то из него создать GLFWwindow (тут я не нашел, возможно ли это), и его уже использовать в качестве параметра для создания нового контекста, который окажется расшарен с предыдущим. Хендл искомого окна получайте через EnumWindows.

    Но скорее всего ваша задумка невозможна.
    Ответ написан
    3 комментария
  • Необходимость сохранения инвариантов при мат. индукции?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Очевидно, да. Чтобы доказать утверждение по индукции, вам надо из индукционного предположения P(k) доказать P(k+1). Если P(k+1) состоит из кучи частей Pi(k+1), то надо их все доказать. Ведь иначе у вас P(k+1) окажется истинным лишь частично, а значит вы индукционный шаг не доказали.
    Если вам надо доказать, что "все овцы имеют 4 белые ноги", а вы выводите, только "у них 4 ноги", то вы не доказали исходное утверждение, ибо из "4 ноги" не следует "4 белых ноги". Может быть, у них 4 черных ноги.
    Ответ написан
    1 комментарий
  • Как узнать, хранятся числа в компьютере в прямом, дополнительном или обратном коде?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Можно переинтерпретировать память как unsigned int, а там уже никакого дополнительного кода нет и 11111111111111111111111111111110 будет 4294967294, а 11111111111111111111111111111101 - 4294967293.
    #include <iostream>
    #include <cstring>
    int main() {
      int x = -2;
      unsigned int y = 0;
      memcpy(&y, &x, sizeof(y));
      std::cout << y;
      return 0;
    }

    Еще можно делать извращение вроде std::cout << *(unsigned int*)(&x), но не стоит.
    В случае обращения к int через указатель на unsigned int это еще нормально, но вообще говоря, это исключение. Обычно нельзя обращаться к переменной одного типа через указатель другого типа. Это неопределенное поведение. Таким вот образом посмотреть на расположение битов в float через преобразование к int нельзя.

    Копирование же в переменную другого типа но такого же размера через memcpy - сработает всегда. При этом компилятор копирование убирает при оптимизации, так что правильное решение работат также быстро, как с Undefined Behavior.

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    arrOne и arrTwo - это переменные, типов int[5] и int[3][2]. Это на уровне C. Под капотом там указатели: компилятор знает адрес начала массива. Путаницу может вызвать то, что массивы в языке C тождественны указателям на начало с той лишь разницей, что компилятор знает его длину и всякие sizeof() сработают правильно.
    Соответственно, arrOne неявно приводится к int*, а arrTwo к int (*)[2] (указатель на массив из 2 элементов, что в свою очередь неявно приводиться к указателю на указатель на инт).

    Поэтому работает код:
    int a[3][2];
       int (*b)[2];
       b = a;


    Можно считать, что arrOne - указатель на первый элемент int, а arrTwo - указатель на первую строку int[2], по совместительству на первый элемент в первой строке:
    int x = *arrOne;
       int y = **arrTwo; // с одной * не сработает ибо int[2], оно же int* к int не приводиться.
       int *z = arrOne+2; // указатель на 3-ий элемент
       int *w = arrTwo + 1; // указатель на вторую строку (первый элемент в ней).


    Но лучше на закапываться в адрессную арифметику, а воспринимать массивы, как свой отдельный тип данных. Не пишите arrOne+1, а пишите arrOne[1] и не парьтесь.

    Даже для int переменных компилятор знает их адрес, так что там под капотом тоже есть указатель. Не очень большая разница.

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Да, ваше рассуждение корректно. Но можно сильно проще.
    F(2x+y)-f(x+y) = 2x

    Обозначим x+y = a.
    f(x+a)-f(a) = 2x

    Очевидно, что это уравнение выполняется для любых x,a

    Подставим a=0:
    f(x)-f(0)=2x
    или F(x) = 2x+f(0).
    Обозначим f(0) за c.
    F(x) = 2x+c.

    Тут все рассуждения в обе стороны, мы не выводили следстие из чего-либо, а лишь переписывали известное нам уравнение в эквивалентное. И получили, что F(x) - прямая с наклоном 2. Других быть не может, потому что именно этот вид эквивалентен изначлаьному условию.

    Edit: Был не прав в конце. Это не эквивалентные утверждения, но мы вывели логически из условия, что функция - прямая. Других быть не может, потому что противоречие: функция не может одновременно быть такой прямой и не быть. А мы прямую вывели.
    Ответ написан
    4 комментария
  • Влияет ли, передаёшь ты в функцию аргументы по ссылке или по значению, на производительность и память?

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

    В целом, в С++ хорошее правило - передавать большие переменные как const &.

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

    С другой стороны, компилятор может наоптимизировать и даже ссылка на int будет не дороже копии int.

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

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

    Потребляет ли много памяти какой-то другой процесс?

    Возможно там утечка каких-то ресурсов, вроде хендлов.

    Каким-нибудь process explorer можно посмотреть сколько хендлов ваша программа имеет, если это число растет, надо разбираться, какие из виндовых объектов вы правильно не уничтожаете. Вроде бы все битмапы вы корректно уничтожаете через DeleteObject, возможно ошибка в другом коде.

    Можно еще поробовать вашу программу подебажить. Вот когда окно вылезает, какой ваш код выполняется? Подключитесь к вашему процессу visual studio и смотрите. Или добавьте отладочный вывод, чтобы понять, какая функция начинает выполнятся но не заканчивает. Это подскажет вам, какие объекты вы не удалили правильно.
    Ответ написан
  • Какой подход выбрать для представления Chunk?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Зависит от того, как вы планируете эти chunk использовать. subrange - гораздо эффективнее, потому что ничего не копируется. Но при этом он не может использоваться после уничтожения оригинального items, или chunks (если внутри у chunk все-таки копия).

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Вообще, в этой задаче это не обязательно. Даже если A > B, вы найдете какие-то решения и потом подстановкой их проверите. Ну или, да, вам надо сразу найти x, что они равноудалены от C, и надо решать уравнения, из которых это следует.

    А так, A=B - это легко доказывается.
    Ответ написан
  • Как определить виртуальные методы в полной специализации шаблона?

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

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

    Видимо, этими углами и высотой задается плоскость в которой треугольник лежит.

    Пусть уравнение плоскости вида Ax+By+Cz+D=0. Тут A,B,C - это нормаль к плоскости. Вы эту плоскость уже знаете - это три ваши точки же. Она от вертикального вектора вверх отличается за счет второго поворота на угол beta. Т.е. вы косинус этого угла можете найти как скалаярное произведение нормали к плоскости и вектора (0,0,1). А угол альфа - тут вам надо спроцировать нормаль плоскости на горизонтальную плоскость и найти угол между этим вектором и (1,0). Не уверен, в какую сторону вы там вращаете и от какого вектора, возможно вам надо будет прибавить 180 или 90 к альфа или бета.

    Как считается высота - я не понимаю. Возможно это просто параметр D, Возможно, D*cos(beta).

    Естественно, уравнение плоскости - нормированное, чтобы A^2+B^2+C^2 = 1. Можно A,B,C найти взяв векторное произведение двух сторон труегольника, а для D потом подставить координаты одной из трех точек.

    Тут в формулах будут использоваться не только 3 координаты z, но вообще все точки. Несколько векторных,скалярных произведений и пара арккосинусов.
    Ответ написан
    7 комментариев
  • Какая функция y=f(x) может описывать подобный график с ассиметричным распределением?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Смотрите на производную. Она сначала около нуля положительная, потом растет до какого-то значения потом резко убывает до более низкого значения. Что-то вроде сигмоиды но сначала вверх, а потом вниз на более низкое значение, а потом снова к 0.
    Значит, можно 3 такие сигмоиды сдвинутые просто сложить: Сначала вверх, потом со сдвигом сильнее вниз, потом вверх назад к 0. Интеграл от сигмоиды - log(1+e^x).

    Вот и получается что-то вроде log(1+e^x)-3*log(1+e^(x-50))+2*log(1+e^(x-75)). График хоть в wolfram alpha постройте.
    Вставляя разные коэффициенты в логарифмы вместо 1, вместо e и регулируя сдвиги при x и коэффициенты перед логарифмами, можно изменять график чтобы выглядело как вам нравится.

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

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

    Но вообще, какая-то фигня у вас с кодом. Всего различных состояний для поля 3*4: 3^12 = 531441. Это включая невозможные состояния после завершения игры и те, где крестиков сильно больше чем ноликов например. Потому что каждая клетка может иметь 3 варианта (пустая, крестик, нолик). Вот 12 клеток дают 3^12. Вы же там как-то 1.2 миллиона насчитали - более чем в 2 раза больше. На самом деле это раз в 10 больше чем должно быть, если считать только допустимые состояния.

    Для 4x4 всего состояний меньше 3^16 = 43миллиона. Это никак не может переполнить память, даже если на каждое состояние выделять по все 16 байт, то это займет едва 700 мегабайт. Даже если в 2 раза умножить на любые дополнительные расходы питоновских словарей, то будет полтора гига.

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

    А если еще и поле хранить в виде битовой маски, по 2 бита на клетку, то все поле 4x4 займет у вас 32 бита, поместится в обычную интовую переменную. И манипуляциями с битами можно быстро проверять, если где-то 3 в ряд.

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