Страницы

Поиск по вопросам

Показаны сообщения с ярлыком алгоритм. Показать все сообщения
Показаны сообщения с ярлыком алгоритм. Показать все сообщения

среда, 15 апреля 2020 г.

Оптимизация алгоритма вычисления “разности” списков IP-диапазонов

#алгоритм #java

                    
Здравствуйте!
Понятия

IPAddressRange - Диапазон IP-адресов, задан в виде network/mask или ipFirst, ipLast
(но в итоге все равно приводится ко второму виду). Так же подразумевается, как некоторое
множество (совокупность) последовательных (упорядоченных) чисел (числовая интерпретация
IP-адресов), заданная двумя значениями, первым и последним из диапазона (прощу прощения
за сложное пояснение)
List - Список IP-диапазонов (множеств)

Что имеем:

Список IP-диапазонов - база
Список IP-диапазонов - исключения
Функция выполняющая вычитание множеств и возвращающая ноль, один или два диапазона
Язык программирования - Java

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

Выбираем последовательно элементы из списка исключения
Для каждого элемента из списка база проверяем, входит ли выбранный элемент списка
исключения в множество (сравниваем границы)
Если не входит - добавляем во временный список элемент из база
Если входит - выполняем разность множеств
"Отработавший" элемента списка база заменяется на новый(е) (два случая рассматривается:
границы элемента из исключения больше или равны границам элемента из база (получаем
0 (обе границы - >=) или 1 (одна из границ - >=) новый элемент), границы элемента из
исключения < границ элемента из база (получаем 2 новых элемента))
Новые элементы добавляются во временный список
Элемент списка исключения удаляется, когда пройдет все элементы списка база, элементы
из временного списка переносятся в список база, процедура повторяется

Хотелось бы оптимизировать мое решение, открыт для идей и наставлений.    


Ответы

Ответ 1



Разделить IPv4 и IPv6 интервалы и обрабатывать их отдельно Надо понимать, что каждый конец интервала - это просто long числа. Предположим, у нас есть реализация для получения такого числа для любого из концов. Предполагаем, что интервалы заданы верно (first <= last) Сортируем списки интервалов по их началам как long числам Идём по спискам по порядку по обоим спискам. Почти как в известной задачке про книги на полках и попутно строим третий список. Не забываем о возможности пересечений соседних интервалов в обоих списках. Сложность зависит от типа применяемой сортировки, но она будет лучше, чем M * N в случае, если M и N достаточно велики (понятно, что если списки 3 на 2, то быстрее будет просто всех со всеми сравнить).

Ответ 2



Как обещал в своем комментарии подумал. @cy6erGn0m в своем ответе (на мой взгляд) оптимальный алгоритм описал. Плюс к этому я обязательно добавил бы после сортировки диапазонов списка (п. 4 у @cy6erGn0m) СЛИЯНИЕ пересекающихся диапазонов. Это проводится за один проход списка и может сильно сократить его размер (вплоть до одного элемента, как в Вашем комментарии: "база" - 0.0.0.0 - 255.255.255.255 и 10.10.10.0 - 10.10.10.255). Более того (как мне кажется) реализация п.5 тоже станет проще. Далее за один параллельный проход обоих списков из множества базы исключаем элементы множества исключений. Количество операций можно оценить в N*log(N) + M*log(M) + M + N + m + n (m, n - количество диапазонов после слияния).

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

#javascript #алгоритм #случайные_числа

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


Ответы

Ответ 1



Простейший вариант: var num; // очень большое простое число. var seed; // затравка - то самое инициализирующее число. var start_number; // основа var counts = new Array(); var max_num = 10; // Инт, который не превосходит наше число. У вас 0-9 числа. for(var i=0; i<1000; i++){ counts[i] = (Math.pow(start_number+i,seed)%num)%max_num; } Механизм простой: возводим основу, изменяемую каунтером в степень затравки и берем остаток от деления на большое число. Потом берем последние несколько цифр(в нашем случае - одну). Как можно оптимизировать: бинарное возведение в степень(пользуясь тем, что у нас кольцо) Замечания: 1) num должен быть достаточно большим, чтобы обеспечить разнообразие. 2) число в степени затравки должно, тем не менее, превосходить это простое число. 3) от основы можно отказаться вовсе, если счет инкрементора начинать с 2, а seed взять, хотя бы, от ~20

Ответ 2



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

Ответ 3



Вопрос успешно обсуждался на оригинальном SO: https://stackoverflow.com/questions/424292/how-to-create-my-own-javascript-random-number-generator-that-i-can-also-set-the-s

Игра нарды на Java

#java #алгоритм #разработка_игр

                    
Здравствуйте! Подскажите, можно ли написать игру нарды в настольном приложение! Подскажите,
как в java создать доску, фишки и камни для игры?
Опишите, пожалуйста, алгоритм игры нарды или дайте ссылку по разработке игры нарды?    


Ответы

Ответ 1



Учи java Swing, тебе этого будет достаточно, что бы сделать интерфейс, правила игры, смотри в другом месте, если делать игру - не примитивной, камни, фишки и тд, надо будет нарисовать или стырить =)

Параметрическое нахождение ближайшей точки в заданном направлении

#алгоритм #php #mysql

                    
Есть массив точек со случайными координатами.
Выбираем любую из них.
Задача: найти ближайшую к ней точку в заданном направлении.
Например, если речь идет о плоской карте, и надо найти ближайшую точку на востоке,
мы фильтруем угол между северовостоком и юговостоком и ищем точку в нем, тупо сравнивая
расстояния. Для трехмерного пространства все еще хуже. Особенно, если попытаться ввести
систему ранжирования: если есть две точки, одна из них точно на восток, но на X дальше,
а другая - на восток-северовосток под углом a, но чуть ближе, то будет выбрана та,
для которой соблюдается определенное отношение X и a.

Вопрос: 


как поступают умные люди в данной ситуации?

это задача для базы(MYSQL) или PHP?


Код писать не надо: как-нибудь справлюсь. Нужен алгоритм или волшебный пендель.    


Ответы

Ответ 1



SQL: select TOP 1 id from ( select id, my_range_fn( dir_x, dir_y, dir_z, x0, y0, z0, x, y, z ) as range from Points p ) where range < 0 order by range desc Пример ранжирующей функции ( JS ): //2D карта на JS //Север - 2, Запад - 4, Юг - 6, Восток - 0 //Рассматриваем попадание в угол в +- 45 градусов от направления function my_range_fn( dir, x0, y0, x, y ){ var dy = y - y0, dx = x - x0, rast = Math.sqrt( dx * dx + dy * dy ), my_dir = Math.PI * ( ( dy > 0 ) ? 1 : 0 ) - Math.acos( dx / rast ), my_dir = ( my_dir < 0 ) ? Math.PI * 2 + my_dir : my_dir, dir = Math.PI * dir / 4; delta = Math.abs( my_dir - dir ); return ( ( delta < Math.PI/4 ) ? -1 : 1 ) * rast - 2 *delta; } Используется коэф. 2 на разность желаемого направления и полученного Примерно означает что при (x0, y0) = (0, 0) и направлении Сервер, выберет (0,5) вместо (2,4)

Ответ 2



Можно и с помощью MySQL. Если рассматривать вариант плоской сетки координат с точкой отсчета в центре, то северо-восток - это верхний правый квадрат. То-есть ограничения в запросе по х > 0 и у < 0 с учетом точки отсчета. Расчет расстояния по теореме Пифагора. Убывающая сортировка по расстоянию. Лимит 1 на вывод. На выходе ближайшая точка в заданном квадрате.

Ответ 3



Для начала выберем систему координат с центром в первой точке Особенность задачи - в наличии ограничений на угол (|fi|<45), что наталкивает на мысли о полярной системе координат и уравнениях вида r=R(cos(fi)-cos(pi/4)). При этом в качестве критерия оптимальности напрашивается параметр уравнения R = r/(cos(fi)-sqrt(2)/2) = r^2/(x-r*sqrt(2)/2) с размерностью расстояния. И тогда останется главное - правильно учесть ограничения.

Список алгоритмов на C#

#c_sharp #алгоритм

                    
Недавно набрел на сайт "Список алгоритмов и структур данных на Java" например вот
описан алгоритм имитации отжига . В общем показалось это очень удобно, может кто видел
такое же для C#? Сложно ли будет на крайний случай сконвертировать?    


Ответы

Ответ 1



Для таких задач - абсолютно не сложно. JLCA 80% работы сделает, визуализационную часть придётся ручками доработать. Как пример - Mp3Sharp, декодер mp3, он конвертирован из явы.

Рекурсия и ее проблемы

#рекурсия #алгоритм

                    
К каким проблемам может привести использование рекурсии и как их избежать?    


Ответы

Ответ 1



Оверхед на вызовы. Немного время, а главное -- стек. При большой глубине рекурсии быстро расходуется. Метод борьбы -- использование хвостовой рекурсии, которую нормальные компиляторы трансформируют в итерации. Добавлено. На тему "заменить рекурсию стеком". Рекурсивная функция вычисления факториала. fact.c++ int fact0(int k, int n) { if (n > 1) return fact0(k*n, n-1); else return k; } fact0.s .file "fact0.c++" .text .p2align 4,,15 .globl _Z5fact0ii .type _Z5fact0ii, @function _Z5fact0ii: .LFB0: .cfi_startproc .cfi_personality 0x0,__gxx_personality_v0 pushl %ebp .cfi_def_cfa_offset 8 movl %esp, %ebp .cfi_offset 5, -8 .cfi_def_cfa_register 5 movl 12(%ebp), %edx movl 8(%ebp), %eax cmpl $1, %edx jg .L5 jmp .L2 .p2align 4,,7 .p2align 3 .L7: movl %ecx, %edx .L5: leal -1(%edx), %ecx imull %edx, %eax cmpl $1, %ecx jne .L7 .L2: popl %ebp .p2align 4,,2 ret .cfi_endproc .LFE0: .size _Z5fact0ii, .-_Z5fact0ii .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits Что тут заменять и на что?

Ответ 2



Слишком глубокую рекурсию всегда можно заменить используя стэк.

понедельник, 13 апреля 2020 г.

Методы сортировки смешанных данных (текст + числа)

#база_данных #сортировка #алгоритм

                    
Какие есть способы сортировки данных типа:

Профессиональное училище № 21
Профессиональное училище № 30
Профессиональное училище № 120

или:

pgAdmin v1.8.4
pgAdmin v1.10.5
pgAdmin v1.12.3

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


Ответы

Ответ 1



имхо у вас проблема не с сортировкой данных а с проэктированием СУБД. значения то неатомарные, - посему должна иметь место оптимизация и разбиение данных на 2 поля: имя + номер или продукт + версия если вы не слышали ничего про реляционные СУБД (втч. про нормальные формы) есть и для вас пару вариантов: для всех полей которые являются комплексными нужно добавить по одному numeric полю в таблицу. и на вставку/обновление повесить триггер который будет для каждого такого поля заполнять соответствующие им поля _order. которые и будут использоватся для сортировки. или же сами можете заполнять при вставке. (имеет смысл хранить эти данные в отдельной таблице) можно добавить функцию которая будет подсчитывать order для сортировки на лету. выглядеть это будет как-то так: 1) select x.afield from xtable x order by get_order (x.afield) или 2) select x.afield from xtable x order by get_order ('xtable', 'xfield', x.afield) настройки для функции get_order могут хранится в какой-то отдельной таблице. в первом примере вам придется автоматически определять шаблон который использовать для определения порядка следования записи, в другом примере вы сможете с помощью if elseif... использовать алгоритм в зависимости от входных данных. первый вариант более предпочтительный с точки зрения performance. а правильный, - оптимизация структуры СУБД UPDATE: с одной стороны полное имя учебного заведения это вроде как-бы единое значение. но это только на первый взгляд. "профессиональное" (опциональный префикс, - можно игнорировать) "училище" - тип учебного заведения (академия, институт, университет) может быть использовано для сортировки "№ 43" - номер учебного заведения. "Санкт Петербурга" - явно же город. "имени" васи пупкина - даж незнаю как атрибут назвать но "имени" явно можно использовать для фильтра, - только есть ли смысл, - хз. далее логически анализируем данные: номер учебного заведения + его тип = уникальная комбинация (в пределах одного города) учебное заведение может быть расположено только в одном городе, во всех остальных будут филиалы. хотя называтся могут одинаково. т.е комбинация этих 3х полей не будет уникальной. в результате получается таблица следующего содержимого: id // уникальный id type, // профессиональное училище / университет / академия. можно вынести в отдельную таблицу number, // порядковый номер. его в принцыпе может и не быть. (это надо учесть в сортировке) dedication_id // поле которое будет хранить ссылку на id человека которому посвящено. если null - то никому не посвящено. null тоже можно учесть при сортировке foundation_date // время когда было создано учебное заведение. имхо это важное поле. description // или full_name/display_name - полное имя заведения, то которое вы будете отображать пользователю. и которое никогда не будет участвовать в сортировке. location_id // ссылка на таблицу с местоположениями (ессно будет содержать город) p.s. пожалуй еще лучше было бы создать отдельную таблицу отделения или филиалы. привязанную к конкретному учебному заведению. где каждый отдельный филиал имеет свой конкретный адресс. что позволит хранить данные независимые от других филиалов. напр: количество студентов/наличие кафедр/специальностей итд итп таблица dedication id, // уникальное id prefix, // его величество name, // василий пупкин ^^ таблица нужна, т.к. одному ученому может быть посвящено больше чем 1 заведение. p.s.s если обобщить одной фразой работу с субд: легко проэктировать = сложно работать и наоборот.

Движение объекта (HTML5 canvas)

#canvas #javascript #html5 #алгоритм

                    
Привет всем, нужна реализация следующего: анимация по рандомным координатам ходит
в произвольные стороны в рамках разрешения пользователя. Каков алгоритм сего действия?    


Ответы

Ответ 1



Генерируешь рандомный X Проверяешь, не вылазит ли он за пределы экрана Генерируешь рандомный Y Проверяешь, не вылазит ли он за пределы экрана Указываешь координаты твоему объекту Делаешь паузу Повторяешь 1-6 пункт

Перевод функции arcTo -> bezier

#canvas #алгоритм

                    
Как бы превратить arcTo (стандартная функция postScript, да и не только, например
в HTML5Canvas она есть) с параметрами x1, y1, x2, y2, radius в кривую Безье?    


Ответы

Ответ 1



Вспомнить тригонометрию : ) Бился с аналогичным вопросом в Processing. Вопрос популярный и решения гуглятся. Например, даже с интерактивным примером.

Ответ 2



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

Как передать двоичные данные через микрофон телефона?

#алгоритм #android

                    
На смартфоне получена медицинская информация. Требуется передать её в диагностический
центр. Но в этьй деревне нет сотовой связи - только старинный телефон. Android-программа
должна преобразовать двоичные данные в звук и послать их в динамик смартфона. Смартфон
приложен к телефонной трубке. На другом конце телефонной линии другой смартфон слушает
писк и преобразует его в массив двоичных данных без потери точности.
Будем благодарны за любую информацию по этой теме.     


Ответы

Ответ 1



На ходу: есть вариант кодировки бинарников через DTMF сигналы (по русски говоря сигналы тонового набора). С точки зрения кодирования бинарников на DTMF вроде ничего сложного нет - грубо говоря бинарник раскладывается на десятичный сигнал (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) каждой цифре сопоставлен определенный писк. На том конце провода все сложнее. Стандартный API этого не поддерживает, но быстрое гугление приводит к проекту DTMF декодер Удачи!

суббота, 11 апреля 2020 г.

Помогите реализовать алгоритм

#алгоритм #java

                    
Здравствуйте! Пожалуйста, помогите реализовать алгоритм.
Даны целые числа от N_MIN до N_MAX. Необходимо вычислить все возможные последовательности
этих чисел, при условии, что одно число не должно повторяться в последовательности
дважды. Длина последовательности варьируется от CH_MIN до CH_MAX.
Поработав своим скудным количеством извилин, я пришёл к выводу, что задача решается
с помощью циклов. Моя недоделанная реализация:
        //Сначала будем создавать последовательности с размером n,
        //потом n+1 и так далее, пока не достигнем максимкльного размера
        for (; CH_MIN < CH_MAX; CH_MIN++)
        {
            //С каждой итерацией данного цикла будем добавлять число
            //к последовательности, пока её размер не достигнет CH_MIN
            for (int CH_CUR = 0; CH_CUR < CH_MIN; CH_CUR++)
            {
                String CHAIN = "";
                //Выбираем первое число последовательности (N_MIN)
                for (int i = N_MIN; i < N_MAX; i++)
                {
                    CHAIN += (i + "");
                    //Далее надо выбрать остальные числа последовательности,
                    //но я не знаю, как это сделать.
                }
            }
        }

Была идея делать массив с числами от N_MIN до N_MAX, при каждом добавлении числа
к последовательности убирать добавленное число из массива, чтобы в нём остались допустимые
для добавления числа (это я так пытаюсь избежать повторения чисел). Однако я так запутался,
что не смог реализовать ни идею с массивом, ни с циклами.    


Ответы

Ответ 1



Вот вариант, правда на JS (fiddle; исправлена процедура определения длины всей последовательности): function generateSequence(min, max) { var alphabet = []; for (var i = min; i <= max; i++) alphabet.push(i); var res = []; var cur = null; var n = max-min + 1; var size = n; for (var i = 1; i < n; i++) size += Math.pow(n, i + 1); function getNext(seq) { if (!seq) return [alphabet[0]]; var index = -1; for (var i = 0; i <= seq.length; i++) { if (i == seq.length + 1) { seq[i] = alphabet[0]; break; } index = alphabet.indexOf(seq[i]) + 1; if (index < alphabet.length) { seq[i] = alphabet[index]; break; } else seq[i] = alphabet[0]; } return seq; } var temp; for (var i = 0; i < size; i++) { cur = getNext(cur); temp = ',' + cur.join(',') + ','; if (!temp.match(/,([^,]+),\1|,([^,]+),(?=.*,\2,)/)) res.push(cur.join('-').split('-').reverse().join('-')); } return res; }

Ответ 2



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

Алгоритм попадания точки в кривую

#c_sharp #алгоритм

                    
Есть класс для отрисовки кривой:

class Curve
{
    // Набор точек.
    public List Points {get; set;}

    // Толщина кривой.
    public int Thinkness {get; set;}

    public void Draw(Graphics g)
    {
        using (var pen = new Pen(Color.Black, Thinkness))
        {
            g.DrawCurve(pen, Points);
        }
    }
}


Отрисовка выполняется методом Graphics.DrawCurve. Нужно добавить метод, который будет
определять, принадлежит ли произвольная точка этой кривой:

public bool Contains(Point pt);


Кто-нибудь может подсказать алгоритм, с помощью которого можно решить данную задачу?
Может быть, каким-то образом можно использовать Matrix или сам Graphics?
    


Ответы

Ответ 1



Придумал следующее решение. В методе Draw рисуем не закругление (DrawCurve), а Path (DrawPath). Способ получения Path и пера выносим в отдельные методы. После этого используем метод IsOutlineVisible для получения информации о вхождении точки в Path. class Curve { // Набор точек. public List Points {get; set;} // Толщина кривой. public int Thinkness {get; set;} private GraphicsPath GetPath() { var path = new GraphicsPath(); path.AddCurve(Points.ToArray()); return path; } private Pen GetPen() { return new Pen(Color.Black, Thinkness); } public void Draw(Graphics g) { using (var pen = GetPen()) using (var path = GetPath()) { g.DrawPath(pen, path); } } public bool Contains(Point p) { using (var pen = GetPen()) using (var path = GetPath()) { return path.IsOutlineVisible(p, pen); } } }

Ответ 2



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

Ответ 3



Нам нужны по идее две вещи: проекция точки на прямую и расстояние от точки до прямой. class Line { Point start; UnitVector direction; } Point projectCoord(Point x, Line l) { return Tools.DotProduct(x - line.start, line.direction); } Point projection(Point x, Line l) { return line.Start + line.Direction * projectCoord(x, line); } double signedDistance(Point p, line l) { if (p == line.Start) return null; return Tools.CrossProduct(p - line.Start, line.direction); } Имея эти методы, всё по идее просто: class Segment { Line Line; double StartCoord, EndCoord; } bool belongs(Point p, Segment s, double width) { return IsBetween(projectCoord(p, s.Line), s.StartCoord, s.EndCoord)) && Math.Abs(signedDistance(p, s.Line)) < width; } Вспомогательные функции: // скалярное произведение double DotProduct(Vector v1, Vector v2) { return v1.x * v2.x + v1.y * v2.y; } // векторное произведение double CrossProduct(Vector v1, Vector v2) { return v1.x * v2.y - v1.y * v2.x; } Ну и точка попадает в ломаную линию, если она попадает хотя бы в одну из её частей.

Как расставить узлы произвольного дерева? [закрыт]

#алгоритм #дерево #графы

                            
             
                
                    
                        
                            Закрыт. Этот вопрос необходимо уточнить или дополнить
подробностями. Ответы на него в данный момент не принимаются.
                            
                        
                    
                
            
                    
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Добавьте больше подробностей
и уточните проблему, отредактировав это сообщение.
                        
                        Закрыт 4 года назад.
                    
                
        

Есть произвольное дерево, узлы-братья удалены друг от друга на динамически задаваемое
значение (в данном случаи 20), так же как и узлы-соседи (в данном случаи 40). В тот
момент, когда самая левая ветвь формируется, вызывается метод расстоновка_узлов в который
передается самый верхний-левый и самый верхний-правый и разница на которую увеличился
промежуток между ними (в данном случаи 20).
Вот что происходит в методе расстоновка_узлов -  

  


в цикле проходим от верхний-левой до верхней-правой и считаем промежутки (в данном
случаи это шесть пустых клеточек, которые как говорил ранее по 20, то есть сумма 120);  
делю сумму полученную на предыдущем шаге на кол-во узлов плюс один (120 / 3 = 40);  
проверяю на сколько текущий узел (я начинаю с лева на право и по этому текущий узел
это тот у которого два ребенка) удален от левого узла-брата (в данном случаи это 80).  
теперь я вычитаю из 80 - 40 = 40, это значение расстояние на которое я смещу текущую
ноду чтобы оказаться в нужном месте.
если это значение (40) больше чем то на которое увеличилось расстояние между двумя
крайними нодами, то это значение становится меньшим. То есть рассчитал что 40, но оно
больше позволенного и по этому значение меняется на 20.  
перемещаю ноду.  


  

Повторяю шесть предыдущих шагов для следующей ноды -
1. получаю 60
2. 60 / 2 = 30
3. 40
4. 30
5. 10
6. ... 

  

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

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

Я бы показал код, но я не хочу чтобы Вы смотрели его недочеты, а лишь хочу чтобы
Вы поняли смысл.  

Вот. Но дальше я столкнулся с следующей проблемой, да и возможно это только одна из ...

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


Что есть у нод
indent - значение на которое текущая нода удалена от предыдущей ||->||
leftOffset - это значение на которое я смещаю влево. В самом начале оно ноль
rightOffset = это то значение на которое смещаю вправо. И тоже по дефолту ноль.  

Вот. Сами ноды, как обычные ноды, ссыдка на парента, в котором они хранятся в индексированном
массиве. Есть методы для получения ноды по индексу и получение самого индекса.
    


Ответы

Ответ 1



В общем, нужно расставлять узлы уже по факту, а не извращаться с двиганием в каких-то рамках. Есть обе константы, "братья" и "соседи", причем "соседи" определяет, насколько должны отстоять друг от друга соседние поддеревья на уровнях ниже первого, а "братья" - на первом. Тогда задачу нужно решать снизу вверх: для каждого поддерева вначале выстраиваем каждое из собственных поддеревьев, после чего выстраиваем текущее подерево, вычисляя для каждого следующего поддерева минимальное смещение относительно нуля, при котором на каждом уровне это поддерево не будет мешать уже установленным поддеревьям. В итоге каждое подерево будет иметь параметр "занятый диапазон" на каждом уровне, где у него есть хоть один лист, по нему и будет идти сравнение при размещении текущего поддерева на верхнем уровне. Т.е. алгоритм выглядит так: BuiltTree buildTree(Tree tree) { BuiltTree[] siblings; foreach (subTree in tree.subTrees) siblings.push(buildTree(subTree)); BuiltTree result=new BuiltTree(); // пустое foreach (sibling in siblings) { int minRightPos=0; for (i=0;i

четверг, 9 апреля 2020 г.

Определить значение элемента «спиральной матрицы» по его координатам

#java #алгоритм

                    
Недавно был вопрос об инициализации массива спиральной матрицей. Хотя предложенные
решения его решают, интересно решить эту задачу без использования памяти под массив,
а именно:


  Написать метод getNum(int s, int x, int y), который принимает три целых параметра
— размер квадратной матрицы (s) и координаты позиции в этой матрице (x, y от 0 до s-1)
и возвращает число от 1 до s*s, которое должно стоять в соответствующей ячейке «спиральной
матрицы» (сматывающейся от левого-верхнего угла к центру по часовой стрелке).


Тогда задача вывода данной матрицы на экран должна свестись к такому коду (никакой
массив не нужен):

for (int y = 0; y < s; y++) {
    for (int x = 0; x < s; x++) {
        System.out.printf("%4d", getNum(s, x, y));
    }
    System.out.println();
}


Кроме того можно вообще выводить огромные матрицы, в UI-окошке и скроллить мышкой,
быстро отрисовывая только видимую часть.

Как же реализовать такой метод?

Решения на других языках тоже принимаются.
    


Ответы

Ответ 1



Для начала переведём входные координаты x и y в систему координат относительно центра матрицы. Так как размер может быть чётным или нечётным, удвоим координаты, чтобы не возиться с половинками: x = 2 * x - s + 1; y = 2 * y - s + 1; Скажем, для матрицы 5×5 возможные значения x и y будут -4, -2, 0, 2, 4. А для матрицы 6×6 — -5, -3, -1, 1, 3, 5. Дальше определим, на каком квадрате от центра лежит текущая точка. Это просто максимум модуля обеих координат: int n = Math.max(Math.abs(x), Math.abs(y)); Скажем, для квадрата 5×5 значения будут такие: 4 4 4 4 *4 4 2 2 *2 4 4 2 *0 2 4 4 2 2 2 4 4 4 4 4 4 Заметим, что внутри текущего квадрата (n-1)*(n-1) записей. Попробуем определить, какое число должно быть на правой-верхней диагонали. Надо из квадрата s*s вычесть квадрат n*n и посмотреть, что получается. Оказывается, надо ещё n вычесть. Вот значения s*s-n*n-n для квадрата 5×5: 5 5 5 5 *5 5 19 19 *19 5 5 19 *25 19 5 5 19 19 19 5 5 5 5 5 5 Ура, диагональ получили. С чётной стороной это тоже верно. Теперь посчитаем расстояние от этой диагонали с учётом знака: int p = (y + x) / 2; Прибавим это расстояние к нашему s*s - n*n - n, получим: 1 2 3 4 5 2 17 18 19 6 3 18 25 20 7 4 19 20 21 8 5 6 7 8 9 Отлично, теперь весь правый-верхний треугольник мы правильно выдаём. Чтобы починить левый-нижний, надо для него заменить p на 2*n-p. Точка лежит в левом-нижнем треугольнике, если x < y: if (x < y) p = 2 * n - p; Вот полный код: public static int getNum(int s, int x, int y) { x = 2 * x - s + 1; y = 2 * y - s + 1; int n = Math.max(Math.abs(x), Math.abs(y)); int p = (x + y) / 2; if (x < y) p = 2 * n - p; return s * s - n * n - n + p; }

Ответ 2



Вот чистое C без рекурсии, массивов и с двумя параметрами. Самое элегантное решение, что мне удалось получить. Левый верхний угол, индексация с нуля. Вход: 0 1 2 3 4 5 6 7 8 Выход: 0 1 2 7 8 3 6 5 4 Правда я его так и не проверил, могут быть ошибки, но идея состоит в том, чтобы просто использовать явную формулу: uint32_t spiral(uint32_t size, uint32_t index) { int x = 2 * (index % size) - size; int y = 2 * (index / size) - size; return (abs(x)>abs(y)) ? ((x>0) ? (4*x*x-3*x+y) : (4*x*x-x-y)) : ((y>0) ? (4*y*y-y-x) : (4*y*y-3*y+x)); }

Эффективная работа с большими объемами данных

#алгоритм #big_data

                    
Изначальная архитектура приложения была построена ошибочно - все данные сливались
в один файл, размер которого перевалил теперь за отметку 950ГБ.

Есть ли какой-нибудь эффективный метод (из области big-data) выделения из этого массива
данных групп сущностей, состоящих из одних и тех же символов (так называемых анаграмм)?
    


Ответы

Ответ 1



Стандартная идея вот какая: К каждой сущности дописать её номер в исходном списке. У каждой сущности отсортировать символы. Отсортировать сущности. Теперь анаграммы будут находиться рядом. Теперь для нахождения анаграмм нужен лишь один пробег по данным. Все эти операции хорошо «параллелятся», Кроме, пожалуй сортировки. С учётом этого можно изменить немного алгоритм: Распартиционировать данные как угодно. Пронумеровать сущности и отсортировать их символы на каждом хосте по отдельности (номеру назначать уникальный префикс, чтобы не смешивать) Отсортировать данные каждого хоста. Смёржить все данные. Сначала заливать результат на первый хост, когда 1/n всех данных зальётся — на второй хост и т. д. (Это по сути перепартиционирование.) Далее пробег по данным на каждом хосте. Вместо пункта 4, возможно, более эффективно будет не сливать данные вместе, а делать многохостовый пробег: string currentValue = null; int anagramCount = 0; string[] nextByHost = new string[N]; for (i = 0..n-1) { currentByHost[i] = ""; nextByHost = fetch from host[i]; } while (any of hosts has data) { if any of nextByHost[i] equals to currentValue, anagramCount++ fetch next from host[i] to nextByHost[i] else store currentValue and anagramCount currentValue = min(nextByHost) }

Реализовать Counting Sort в стиле C++

#cpp #алгоритм #шаблоны_с++

                    
Стало интересно, как оформить алгоритм сортировки подсчетом из Кормена по стандартам
C++ для сортировки произвольных объектов, а не только чисел. Достоинство этой сортировки,
кроме скорости, в том, что она устойчива, то есть если набор каких-то объектов пронормирован
целыми числами, но алгоритм не будет перемешивать объекты с одинаковой нормой. Можно
либо задать функцию нормировки, либо создавать пары "объект - число", которые передаются
в функцию сортировки. Вот код:

#include 
#include 
#include 

//Сортировка подсчетом
void csort(const std::vector in, std::vector &out, int max){
    int *c = new int[max + 1];
    std::memset(c, 0, (max + 1) * sizeof(int));
    out.resize(in.size());
    for(int i = 0; i < in.size(); i++)
        c[in.at(i)]++;
    for(int i = 1; i < max + 1; i++)
        c[i] = c[i] + c[i - 1];
    for(int i = in.size() - 1; i >= 0; i--){
        c[in[i]]--;
        out[c[in[i]]] = in[i];
    }
    delete[] c;
}

int main(){
    std::vector u, v;
    int a[17] = {0, 4, 7, 1, 2, 3, 3, 3, 9, 11, 13, 15, 15, 2, 17, 16, 16};
    size_t max = 17;

    u.assign(a, a + 17);
    std::copy(u.begin(), u.end(), std::ostream_iterator(std::cout, " "));
    csort(u, v, max);
    std::cout << std::endl;
    std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, " "));
    std::cin >> max;
}


В C++ советуют вместо массивов везде использовать векторы. Можно каким-то образом
в строке int *c = new int[max + 1]; использовать вектор? И надо ли это вообще? В первой
части c[in.at(i)]++; я использую функцию at() вместо прямого обращения по индексу in[i],
а в конце обращаюсь к элементам напрямую. Какой способ лучше? Например, что в последнем
цикле множество функций at() будет очень затруднять чтение кода. 

Как еще можно переписать этот код? Как правильно писать абстракцию типа данных, которые
не являются числами и которые надо сортировать?
    


Ответы

Ответ 1



В C++ советуют вместо массивов везде использовать векторы. Можно каким-то образом в строке int *c = new int[max + 1]; использовать вектор? Да, примерно так: std::vector c(max + 1, 0); Оно же будет и инициализацией нулями. И надо ли это вообще? Это Ваша гарантия того, что в случае exception ниже, выделенная память будет освобождена. Т.е. в общем случае да, надо. В первой части c[in.at(i)]++; я использую функцию at() вместо прямого обращения по индексу in[i], а в конце обращаюсь к элементам напрямую. Какой способ лучше? Например, что в последнем цикле множество функций at() будет очень затруднять чтение кода. at - генерирует out_of_range exception, в случае, если Вы пытаетесь обратиться к элементу вне размеров массива. Обращение "напрямую" в этом случае приведет к undefined behavior. В остальном они идентичны. На мой взгляд, т.к. Вы точно знаете размеры своего массива и никак не рискуете обратиться вне, и никто, кроме Вас и этого потока этот массив не изменяет, Вы смело можете обращаться "напрямую". Как еще можно переписать этот код? Красивый пример предложен в соседнем ответе :) Я же предложу обратить внимание на возможные проблемы кода. 1) int i = in.size() - 1 - размер массива имеет тип size_t, который с хорошим шансом больше, чем int. И самая печаль, если он имеет ту же разрядность, но, например, unsigned. Это может при определенном размере массива дать Вам отрицательное стартовое значение. И в случае обращения по at(), Вы получите exception, и просто потечете памятью там, где делаете new int, а в случае обращения напрямую, будет undefined behavior, и Ваш процесс, например, полезет взламывать Пентагон :) 2) c[i] = c[i] + c[i - 1]; - теоретически это может привести к переполнению, и получению веселья. Но метод подсчета не используется при большом разбросе значений, а минимум у Вас не устанавливается и по дефолту 0, поэтому на это можно (из соображений здравого смысла) не обращать внимания. 3) Эту часть я бы заменил на более простое. out.resize(in.size()); //... for(int i = 1; i < max + 1; i++) c[i] = c[i] + c[i - 1]; for(int i = in.size() - 1; i >= 0; i--){ c[in[i]]--; out[c[in[i]]] = in[i]; Вот на это: out.reserve(in.size()); for(size_t i = 0; i < max + 1; i++) for (size_t j = c[i]; j > 0; --j) out.push_back(i); Имхо так понятнее, что происходит. Как правильно писать абстракцию типа данных, которые не являются числами и которые надо сортировать? Зависит от того, по какому признаку их надо сортировать. Исходя из этого и будет необходимо сформировать целочисленное представление этого признака. Если же признаков для сортировки нет, и надо отсортировать "как-нибудь", лучше всего сразу на месте объявить массив отсортированным. Ну, по какому-то существующему, известному только Вам секретному правилу :)

Ответ 2



Простая копирующая сортировка подсчетом может выглядеть так: #include #include #include // Сортировка подсчетом требует функцию, возвращающую ключ сортировки. // Identity-функция возвращает переданный ей аргумент без изменений. // Мы будем использовать ее в качестве функции для ключа по-умолчанию. const auto identity = [](auto x) { return x; }; using Identity = decltype(identity); // Сортировка подсчетом использует последовательный доступ для элементов на входе, // и произвольный доступ для элементов на выходе алгоритма. template void counting_sort( ForwardIterator first, ForwardIterator last, // входная последовательность RandomAccessIterator out, // результат Key key = identity) // функция, выдающая ключ элемента { // Проверяем что последовательность не пустая. if (first == last) return; // Определяем минимальный и максимальные ключи элементов. auto compare = [&](const auto& lhs, const auto& rhs) { return key(lhs) < key(rhs); }; auto minmax = std::minmax_element(first, last, compare); auto min_key = key(*minmax.first); auto max_key = key(*minmax.second); // Если ключи не отличаются, то последовательность уже отсортирована. if (min_key == max_key) { std::copy(first, last, out); return; } // Выделяем массив для подсчета элементов. // Тут можно оптимизировать потребление памяти, если использовать счетчики меньшие // чем size_t, если размер входной последовательности достаточно мал. std::vector count(max_key - min_key + 1); // Подсчитываем количество элементов с одинаковым ключом. std::for_each(first, last, [&](const auto& x) { ++count[key(x) - min_key]; }); // Обнуляем первый счетчик и вычисляем частичные суммы. // Если после подсчета в count было {2,2,2,2}, // то после partial_sum там будет {0,2,4,6}, // т.е. индексы, по которым должны лежать элементы с одинаковым ключом. count[0] = 0; std::partial_sum(count.begin(), count.end(), count.begin()); // Копируем элементы в выходной массив, увеличивая индексы в "count". // Копирование можно заменить на перемещение. std::for_each(first, last, [&](auto& x) { out[count[key(x) - min_key]++] = x; }); // Код "count[key(x) - min_key]" встречается два раза, и его можно вынести в отдельную функцию, например // auto count_ref = [&](const auto& x) -> std::size_t& { return count[key(x) - min_key]; }; }

Поиск необходмых заголовков

#java #алгоритм #jsoup

                    
Есть метод, который считает хедеры для таблицы

private List headers(String html)
{
    Document doc = Jsoup.parse(html);
    ArrayList result = new ArrayList<>();
    Elements header;
    Element firstThead = doc.select("thead").first();
    Elements trOfFirstThead = firstThead.children();
    for (Element tr : firstThead.children())
    {
        Elements select = tr.select("th");
        for (Element th : select)
        {
            String s = th.attributes().get("rowspan");
            if (!s.isEmpty() && s.equals(String.valueOf(trOfFirstThead.size())))
            {
                result.add(th.text());
            }
        }
    }
    header = trOfFirstThead.last().children();

    for (Element element : header)
    {
        if (element.tag().getName().equals(tag_th))
        {
            result.add(element.text());
        }
    }
    return result;
}


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

Данный алгоритм работает для таблиц, представленых под номерами 1, 2, и 3 ( см. вложение).
Но для таблицы типа 4 хедеры находятся не правильно.

Требуемая коллекция : 

h4 h10 h11 h12 h6 h7.

При работе алгоритма получается следующая коллекция : 

h4 h7 h10 h11 h12.

Прошу помочь советом/алгоритмом, как можно реализовать нужное поведение.

P.S. исходный код таблиц.



    
    	
    
    
    
  • Таблица 1

    h1 h2 h3
    1 2 3
    4 5 6
    7 8 9
  • Таблица 2

    h4 h5
    h1 h2 h3
    1 2 3
    4 5 6
    7 8 9
  • Таблица 3

    h4 h5
    h1 h2
    1 2 3
    4 5 6
    7 8 9
  • Таблица 4

    h4 h5 h7
    h1 h2 h8 h6
    h10 h11 h12
    1 2 3 4 5 6
    7 8 9 10 11 12


Ответы

Ответ 1



По-моему, решением будет реализация в каком-то объеме прописанного в HTML5 алгоритма построения таблицы, благо обработка там простая. Вот этот код выдает нужный результат на ваших примерах: static class TableHeader { private String[][] cells; private int y_height = 0; private int x_width = 0; public TableHeader( int rows, int columns, Element thead ) { cells = new String[rows][columns]; parseTHead( thead ); } private void ensureCapacity( int rows, int columns ) { if ( rows <= cells.length && columns <= cells[0].length ) return; int nRows = Math.max( cells.length, rows ); int nColumns = Math.max( cells[0].length, columns ); String[][] newCells = new String[nRows][nColumns]; for ( int row = 0; row < cells.length; row++ ) { System.arraycopy(cells[row], 0, newCells[row], 0, cells[row].length ); } cells = newCells; } private void fill( String cellValue, int row, int col, int rowspan, int colspan ) { ensureCapacity( row + rowspan, col + colspan ); for ( int r = 0; r < rowspan; r++ ) { for ( int c = 0; c < colspan; c++ ) { cells[row + r][col + c] = cellValue; } } } private int cellSpan( Element th, String attrName ) { String attrValue = th.attr( attrName ); int result = 1; if ( attrValue.isEmpty() ) return result; try { result = Integer.parseInt( attrValue ); } catch ( NumberFormatException ex ) { /*ignore*/ }; return result; } // http://www.w3.org/TR/html5/tabular-data.html#algorithm-for-processing-row-groups private void parseTHead( Element thead ) { //int y_start = y_height; // #1 int y_current = 0; final Elements rows = thead.children().select( "tr" ); final int rowsNumber = rows.size(); ensureCapacity(rowsNumber, x_width); for ( Element tr : rows ) { // #2 //http://www.w3.org/TR/html5/tabular-data.html#algorithm-for-processing-rows if ( y_height == y_current ) { y_height += 1; } int x_current = 0; //TODO: Run the algorithm for growing 'downward-growing cells'. for ( Element currentCell : tr.children().select( "td, th" ) ) { //6. While xcurrent is less than xwidth and the slot with coordinate (xcurrent, ycurrent) // already has a cell assigned to it, increase xcurrent by 1. while ( x_current < x_width && cells[y_current][x_current] != null ) x_current += 1; if ( x_current == x_width ) { x_width += 1; //# 7 } int colspan = cellSpan( currentCell, "colspan" ); //#8 int rowspan = cellSpan( currentCell, "rowspan" ); //#9 if (colspan == 0) colspan = 1; //TODO: 10. If rowspan is zero and the table element's Document is not set to quirks mode, // then let 'cell grows downward' be true, and set rowspan to 1. // Otherwise, let cell grows downward be false. //FIXME: не позволяем rowspan создавать больше строк, чем есть // как этот вопрос решен в стандарте? rowspan = Math.min( rowsNumber - y_current, rowspan ); if ( x_width < x_current + colspan ) x_width = x_current + colspan; if ( y_height < y_current + rowspan ) y_height = y_current + rowspan; // TODO: If any of the slots involved already had a cell covering them, // then this is a table model error. // Those slots now have two cells overlapping. fill( currentCell.text(), y_current, x_current, rowspan, colspan ); // #13 // TODO: If 'cell grows downward' is true, then add the tuple // {c, xcurrent, colspan} to the list of 'downward-growing cells'. x_current += colspan; //#15 } y_current += 1; } } public List lastRow() { return Arrays.stream( cells[y_height - 1]).limit( x_width ).collect( Collectors.toList()); } } private static List headers3(String html) { Document doc = Jsoup.parse(html); Element firstThead = doc.select("thead").first(); TableHeader header = new TableHeader(10, 10, firstThead); return header.lastRow(); } В реализации не обрабатывается случай с rowspan="0", вроде как все манипуляции с шириной и высотой можно закинуть в fill, и ни на чем, кроме ваших примеров я ее не проверял. В качестве бонуса, такой подход позволяет легко получить полный заголовок столбца. upd: есть очевидная проблема со случаем, когда y_current + rowspan превышает количество , в результате fill создает лишние ряды, чего в браузере не наблюдается. С colspan наверняка та же ситуация. Пока просто ограничил rowspan сверху, но я явно чего-то не понимаю в стандарте.

Стеки фиксированного размера и очередь с max за O(1)

#cpp #алгоритм

                    
При помощи очереди с нахождением максимума за O(1) надо обрабатывать миллиарды элементов.
Чтобы программа работала быстрее, нужно придать фиксированный размер двум стекам, при
помощи которых моделируется очередь. Как это сделать правильно? Мои попытки:

AmpQueue(int size){
    std::vector< std::pair > v_temp;
    v_temp.resize(size);
    std::stack< std::pair, std::vector< std::pair > > s_temp(std::move(v_temp));
    s1.swap(s_temp);
    s2.swap(s_temp);
}


А вот и сама программа:

#include 
#include 
#include 
#include 

// Класс для эффективного нахождения максимальной
// амплитуды на подотрезке
class AmpQueue{
private:
    std::stack< std::pair, std::vector< std::pair > > s1, s2;
public:
    AmpQueue();
    AmpQueue(int size){
        std::vector< std::pair > v_temp;
        v_temp.resize(size);
        std::stack< std::pair, std::vector< std::pair > > s_temp(std::move(v_temp));
        s1.swap(s_temp);
        s2.swap(s_temp);
    }
    // Загрузка нового элемента в очередь с поддержной нахождения максимума
    void push(int new_element){
        int max = s1.empty() ? new_element : std::max (new_element, s1.top().second);
        s1.push(std::make_pair(new_element, max));
    }
    // Удаляет элемент из очереди и возвращает значение удаленного элемента
    int pop(){
        if(s2.empty())
            while(!s1.empty()){
                int element = s1.top().first;
                s1.pop();
                int max = s2.empty() ? element : std::max(element, s2.top().second);
                s2.push(std::make_pair(element, max));
            }
        int result = s2.top().first;
        s2.pop();
        return result;
    }
    // Получение текущего максимума в очереди за O(1)
    int max(){
        if(s1.empty() || s2.empty())
            return s1.empty() ? s2.top().second : s1.top().second;
        else
            return std::max(s1.top().second, s2.top().second);
    }
    // Проверка: пуста ли очередь
    bool empty(){
        return (s1.empty() && s2.empty());
    }
    // Загрузка в очередь последовательности из n чисел
    void load(int n){
        int value;
        while(n--){
            std::cin >> value;
            push(value);
        }
    }
};

int main(){
    int n, current, maximum, element;

    std::cin.sync_with_stdio(false);
    std::cin >> n;
    AmpQueue q(n);

    // Загрузим в очередь первые n значение амплитуды
    q.load(n);
    // Выведем текущий максимум
    std::cout << q.max() << std::endl;

    // Загружаем следующий элемент последовательности и ищем максимум
    std::cin >> current;
    while(current != -1){
        q.pop();
        q.push(current);
        std::cout << q.max() << std::endl;
        std::cin >> current;
    }
}


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

https://ideone.com/bJUpwL

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

Как все исправить?
    


Ответы

Ответ 1



Попробуйте использовать std::vector::reserve() вместо std::vector::resize(). Но в push() вам нужно контроллировать верхний размер в любом случае. Ваш поправленный пример, не могу оценить насколько он работающий: https://ideone.com/uF09OU

вторник, 7 апреля 2020 г.

CSRF-токен в куках

#php #алгоритм #cookie #защита #csrf

                    
почему некоторые сайты хранят CSRF-токен в куках?

Ведь если отправить, к примеру, GET-запрос - 


Ответы

Ответ 1



Само по себе это действительно бесполезно, если сервер помнит соответствие токен-пользователь и проверяет только его. Но в сочетании с передачей такого же токена в параметрах это становится быстрым и простым способом защиты от CSRF: вы ставите пользователю в куки совершенно случайный токен и проверяете, что в параметрах запроса впоследствии приходит точно такой же. Проверяется соответствие запрос-кука. Потенциальный атакующий не сможет достать его из-за Same Origin Policy (можно ещё досыпать сверху HttpOnly, чтобы не получить дыр из-за JS), а потому не сможет его продублировать в запросе. И нет необходимости запоминать что-либо на сервере.

четверг, 2 апреля 2020 г.

Преобразование ломаной линии

#cpp #алгоритм #геометрия #2d

                    
Я имею неравномерную сетку в виде координат узлов в двумерном пространстве
Узлы сетки хранятся в одномерном векторе, где нумерация снизу-вверх слева-направо



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

Количество точек ломаной линии не совпадает с количеством точек результирующей ломаной.

Есть ли у кого-нибудь идеи по решению данной задачи?
    


Ответы

Ответ 1



Алгоритм: Выбираем клетки, через которые проходит ломаная. Циклично проверяем выбранные клетки: 2.1 Берем на границах клетки две точки, в которых ломаная пересекает эту клетку (или одну из крайних точек ломаной) и соединяем отрезком. 2.2 Сдвигаем отрезок так, чтоб обе точки находились на границах клетки (в случае с крайними точками ломаной). 2.3 Считаем углы между отрезком и границами к летки, к которым прилегает отрезок (достаточно неточного расчета в три варианта >45|=45|<45). 2.4 "основная" граница будет та, у которой угол <45 (обведено красным). Если угол =45, то обе границы равнозначны. Повторяем для всех клеток На основе полученных выборок по две границы строим ломаную Может получиться, что ломаная пройдет "вдоль" нескольких клеток, в этом случаем сравниваем результаты проверки текущей клетки и предыдущей. Если сторона клетки с минимальным углом одинаковая в обоих клетках, то вторую сторону не учитываем.

Ответ 2



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

Ответ 3



#include #include #include using namespace std; int main() { // вообше то с такими задачами хранить лучше в std::valarray // допустим количество узловых точек = 4 * 4 и для примера приведу //конкретные цифры // в задаче же уже заданы эти цифры, я лишь для демонстрации идеи const int n = 4; valarray< int > matrix(n * n); // инициализируем первую строку снизу от нулевого индекса `n` штук valarray row{ 2, 4, 7, 8 }; // теперь учитываем что разница между соответствующим элементами // следующей строки должны быть одинаковыми. // и допустим эти цифры заданы в векторе vector dif{ 3, 2, 4 }; // инициализируем последовательность по срезам for (int i = 0; i < n; ++i) { matrix[slice(i * n, n, 1)] = row; if (i == n - 1) break; row += dif[i]; } // точки на кривой лучше хранить в map, так как кривая монотонно растет map curve{{ 3.4, 2.7 }, {4.1, 6}, {5, 9.3}}; // для первой точки на кривой auto para = curve.begin(); // теперь берем ближайшие целые этих точек int x = lround(para->first), y = lround(para->second); //... return 0;} теперь чтобы знать к каким элементам нашей последовательности ближе точка с координатами (x, y) всего лишь дело техники... для сравнения y наверняка понадобится dif, а STL альгоритмы дадут нам возможность рассмотреть любое количество точек с соответствующими предикатами. Вообшем идея такая, дальше подумайте сами

Ответ 4



0) Учащаем точки ломаной: в цикле определяем расстояние между соседними точками (D) у ломаной, если это расстояние больше, чем расстояние диагонали ячейки сетки (d), то вставляем дополнительною точку. Это шаг предобработки ломаной. В конце объясню зачем это нужно 1) Определяем ближайший узел (curr_node) к начальной точке (p) исходной ломаной Далее в цикле: 2) Определяем соседние узлы для curr_node (соседями не считать предыдущий посещенный узел) 3) Определяем расстояние от curr_node и от его соседних узлов до следующей точки ломаной (p_next). 4) Если расстояние от curr_node до p_next меньше, чем минимальное расстояние от его соседей до p_next, то p_next станет следующей точкой ломаной, иначе "посещаю" следующий узел сетки (тот соседний узел, который ближе всего к точке p_next) Далее возвращаемся к шагу 2 В итоге получаем такую картину посещения узлов т.е. собирая посещенные узлы (черные), получаем такую ломаную, проходящую по узлам сетки: А теперь покажу зачем нужно было делать нулевой шаг: Как видно из этих двух рисунков, первый вариант неверный. Вставкой дополнительных точек, добьемся правильного результата