• Почему в Python последовательность append-ов суммируется в O(n), а не в O(n logn)?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    а общее число перераспределений для n добавлений уменьшается как O(logn). Почему тогда в таком случае для n последовательности append-ов общая сложность равна O(n), а не O(n logn)?


    Потому что эти распределения разного размера. Первое - совсем маленькое. Второе чуть больше и т.д. Чтобы суммарно было O(n Log n), каждое из них должно быть пропорционально n.

    1+4+10+⋯≤O(n)


    Вот это и есть ответ. Первые слагаемые маленькие. Получается геометрическая прогрессия, которая дает линейную сумму от n.
    Ответ написан
    Комментировать
  • Как корректно распределить сумму внутри элементов массива?

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

    Далее алгоритм (на псевдокоде):
    total_price - изначальная цена. goods[i].price - цена товара, goods[i].amount - количество товара, discount - сколько скидки.
    discount_left = discount
    for i in 1..n {
      cur_discount = discount * goods[i].price / total_price # целочисленное деление нацело с округлением вниз.
      goods[i].price -= cur_discount
      discount_left -= cur_discount*goods[i].amount
    }
    for i in 1..n {
      if discount_left == 0: break
      if goods[i].amount <= discount_left {
        goods[i].price -= 1;
        discount_left -= goods[i].amount
      }  else {
       // разбиваем категорию на 2 штуки, в одной discount_left товаров, в другой остальное.
       new_good = Good{.price = goods[i].price-1, .amount = discount_left}
       goods[i].amount -= discount_left
       discount_left = 0
       goods.append(new_good)
       break
      }
    }


    Как это работает: если бы мы могли идеально делить копейки с любой точностью, то каждый товар получил бы скидку price*discount/total_price. Одинаковый процент скидки везде. но у нас-то целые копейки, поэтому если мы эти скидки округлим вниз, то мы сколько-то копеек недоскинем. Но для каждой штуки товара мы потеряли меньше одной копейки, а значит суммарно - меньше общего количества товаров. Значит можно из каких-то товаров вычесть 1 и все сойдется. Вот и вычитаем из первых discount_left товаров. Если категория целиком покрыта - просто уменьшаем у нее цену. Если нет, то разбиваем на 2 куска и у одного цену уменьшаем.

    Тут могут быть проблемы, если скидка очень большая и есть товары очень маленькой стоимости. Скажем, товар стоимостью 5 копеек и кидка в 85% уже между 0 и 1. Округленная вниз она будет 4, но вот если вы из этого товара потом еще и 1 вычтите, то может так получится, что вы снизите цену до 0. Допустимо ли это? Чтобы этого избежать надо товары отсортировать по убыванию цены. Но все равно может не помочь, тогда надо тогда такие товары ценой в 1 копейку исключить из рассмотрения и запустить цикл вычитания 1 опять, если после цикла discount_left не 0.

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

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

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

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

    по двум сторонам и прилежащему углу.


    А вот это не правда, если угол не между двумя равными сторонами. Вот заданы нам 2 длины и угол. Построим такой треугольник: отрезок заданной длины, луч из одной вершины с заданным уголом, окружность заданной длины из второй вершины. Луч и окружность пересекаются в двух точках. Значит, есть 2 разных варианта для третьей вершины, удовлетворяющих условиям. Т.е. есть 2 разных треугольника у которых равны 2 стороны и угол (не между ними).
    Ответ написан
  • Почему не работает программа на C++ с решением задачи об "Игре в жизнь"?

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

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

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

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

    Скорее всего у вас внутри конструктора BoxContainer идет присвоение в член nbox. Но у вас не определен оператор присвоения (или конструктор копирования) для NumberBox. Определите его (можно default). Так что с передачей по ссылке все хорошо, но вот то, что вы дальше с этой ссылкой пытаетесь сделать у вас не работает.

    Руководствуйтесь правилом трех-пяти.
    Ответ написан
    2 комментария
  • Как доказать что мы действительно не пропустим такую пару i,j которая дает правильный ответ в методе двух указателей?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    В такой реализации это плохо видно, но можно переписать основной цикл так:
    j = Len(B)-1
    for i in range(len(A)):
      while j >= 0 and A[i] + B[j] > c:
        --j;
      if A[i] + B[j] == c:
        found = True
        break


    В этом случае после цикла while поддерживается инвариант, что a[i]+b[j] <= c и это максимальное такое j.
    Ведь, если этот инвариант поддерживался для пердыдущей итерации, то у нас было a[i-1] +b[j] <=c и a[i-1]+b[j+1]>c. Отсюда получается, a[i]+b[j+1] >= a[i-1]+b[j+1] > c. Т.е. если мы уменьшим j в цикле while инвариант останется действовать - это будет самое большое j, т.ч. a[i]+b[j] <= c.

    А этот инвариант гарантирует, что когда в цикле по i переберется значение из ответа, j гарантированно будет указываать на j из ответа. Потому что, если существуют i', j', т.ч. a[i']+b[j'] = c, то можно увеличить j' пока b там не меняется, т.ч. b[j']>b[j'], отсюда получается a[i']+b[j'] <=c и a[i']+b[j+1] > c - а это и есть наш инвариант. Т.е. на итерации i=i' найдется именно j=j'.
    Ответ написан
    3 комментария
  • В чем суть ассиметричного графа?

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

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Отладчик есть? Или хотя бы отладочный вывод добавьте. Выводите все переменные там, где они меняются. Мне кажется, что вы из строк все пробелы удаляете и у вас segment получается "sqrt(x);". Ведь stringstream будет читать одно слово целиком до пробельного символа, а их там тупо нет. И потом проверка segment == "sqrt" не срабатывает. Но мне лень ваш код запускать, перепроверьте эту гипотезу сами.

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

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

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

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Могли бы вы привести тип PMIB_IPNETTABLE? Поищите ее в исходниках, все должно стать понятно.

    Не специалист по паскалю, но если это структура от winAPI, то она сишная, и там идет работа с указателями и никакой длины массива нет (как часть table). А массивы в паскале по другому реализованы - там длина должна рядом хранится. В этой структуре длина тоже есть, но как часть структуры, а не часть массива. Поэтому с точки зрения паскаля у вас там массив длины 1.
    Через указатели все работает, потому что проверок никаких не делается на выход за границу массива.
    Массив бы тоже сработал, но там проверка длины встроенная.
    Ответ написан
    7 комментариев
  • Поднятие публичного сервера, как сделать?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    На роутере можно пробросить порт к компьютеру. Ищите по словам forward port. Но остается проблема с меняющимся айпишником. Можно или заплатить провайдеру за белый айпи, или можно воспользоваться сервисом dyndns. На роутере как-то настраивается, что он определенному серверу будет сообщать, какой же у него айпишник.
    Ответ написан
    1 комментарий
  • Как получить нужное значение на разных версиях виндовс?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Заведите, например enum со всеми версиями виндоуз и занумеруйте их. Смещения ваши раскладывайте не по неймспейсам, а массиве. Во время исполнения через winapi получайте версию винды и приводите ее к значению в вашем энуме. Его используйте как индекс в массиве.
    Ответ написан
    2 комментария
  • Не могу найти ошибку в коде?

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

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

    Сначала напишите программу на тупо с, потом замените циклы на условия и goto. Потом каждый if распишите через if/goto:
    if (a) {
    B
    } else {
    C
    }
    
    if (a) goto labelB;
    C
    goto end;
    labelB: B
    end:


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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Если вам можно брать вершины только из одной доли, то это задача set cover (покрытие множества). Каждая вершина в R задает множество в L и вам надо взять минимальное их количество, чтобы все L было покрыто. Это NP-сложная задача и у нее есть только медленные решения, вроде перебора. Еще можно свести ее к задаче целочисленного линейного программирования и решать каким-нибудь солвером.
    Ответ написан
    2 комментария
  • Как расположить плоскую текстуру сегмента на кольцо?

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

    x_r = (sqrt(x^2+y^2)-r0)/(r1-r0)*Width
    y_r = (atan(y/x)/pi+1/2)*Height


    Тут (x,y) - координаты на кольце. Центр кольца в (0,0), внутренний радиус r0, внешний r1. Width, Height - размеры прямоугольной текстуры.
    Ответ написан
    1 комментарий
  • Как это решается?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Соотношение углов намекает, что стоит провести биссектрису угла ABC. Пусть она пересечет сторону AC в точке E. Можно заметить, что треугольник AEB будет равнобедренный и EM - его высота и медиана и параллельна СВ. Теперь проведите прямую через E, параллельную AB. У вас там сверху получится треугольник, подобный ABC, и отрезок CD будет его высотой. И там будет малая часть, равная искомому DM. Еще CBE будет подобен CAB. Вот составьте кучу соотношений для этих трех подобных треугольников.
    Ответ написан
    Комментировать
  • Как нагрузить расчёт в однопоточной программе C++ до 90-100% на используемом ядре?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Во-первых, вы уверены, что у вас эти 10-15% - это загрузка ядра, а не вcего процессора? Диспетчер задач обычно показывает как 100% полную загрузку всех ядер.
    Во-вторых, что у вас там за вычесления? Работа с целыми числами? Float? Всякие векторные инструкции? Точно нет никаких пауз вроде sleep()?

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

    Самое вероятное место для тормозов - это вывод на экран/в файл. Если вы много отладочной информации выводите, это будет бутылочным горлышком.

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

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