Ответы пользователя по тегу Алгоритмы
  • Как рассчитать какое слово будет сгенерировано?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Всего слов 4^6=4096, так что 5000-го слова не будет. Если бы было, надо было бы перевести 4999 в четверичную систему счисления, и заменить цифры на буквы (0 на q, 1 на w...)
    Ответ написан
    8 комментариев
  • Как решить проблему на С++?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    А откуда взялась задача? Есть ли ссылка на оригинал? Есть ли там примеры?
    Пока впечатление, что авторы хотели чего-то другого.
    Ответ написан
    1 комментарий
  • Почему рекурсивные алгоритмы работают медленнее своих линейных аналогов?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Совсем не факт, что рекурсивные алгоритмы медленнее. Я только что написал тестовый пример - перебор чисел, все цифры в которых различны. Рекурсивная форма работает примерно на 10% быстрее, чем нерекурсивная, и на её написание ушло в несколько раз меньше времени.
    Ответ написан
    2 комментария
  • Как найти угол поворота объекта (компьютерная графика)?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    1) строите границы Г1, Г2.
    2) для каждой считаете:
    - центр масс (x0,y0)
    - три интеграла: a11=int((x-x0)^2), a12=a21=int((x-x0)*(y-y0)), a22=int((y-y0)^2)
    - собственные значения m1,m2 (m1 < m2) и собственные вектора v1,v2 матрицы ((a11 a12) (a21 a22)).
    Пусть это было вычислено для Г1, а для Г2 получается m1', m2', v1', v2'.
    Если искажений при повороте нет, то m1=m1', m2=m2'. Угол поворота определяется углом z между v1 и v1' (но надо сообразить, в какую сторону). К сожалению, он определён с точностью до 180 гр - так что надо будет как-нибудь сравнить варианты поворота на z и на z+180, и выбрать лучший. Допустим, что это z.
    Осталось найти точку, при повороте вокруг которой на угол z точка (x0,y0) переходит в (x0',y0'). Проще всего записать это в комплексных числах: p0=x0+i*y0, p1=x0'+i*y0', f=exp(i*z). Если искомый центр c, то f*(p0-c)=p1-c, откуда c=(p1-f*p0)/(1-f).
    И всё. Ничего сложного, первый курс ангема...
    Ответ написан
    Комментировать
  • Генерация всех позможных вариантов заполнения квадратной матрицы N-м числом графов. Алгоритм..?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если мы возьмём любую цепочку, проходящую через все точки (например, змейку или спираль), и разрежем её на куски всеми возможными способами, а потом часть кусков ещё и развернём, у нас уже получится примерно 2^(N^2)/3 вариантов. Уже при N=7 перечислить их нереально (при N=6 их всего 10 миллиардов - это не так много). Но поскольку исходных цепочек (проходящих через все точки) много, а кроме того, не все конфигурации можно получить разрезанием одной цепочки, вариантов будет ещё больше.
    Алгоритмы - как рекурсия, так и перебор состояний клеток - не очень сложные. В обоих случаях нужно искать разбиение на неориентированные цепочки, а потом всеми способами задавать их направление.
    В случае перебора состояний (он проще) делаем так. Замечаем, что у каждой клетки есть 10 возможных состояний - 4 для конца цепочки (направления, куда из этой клетки цепочка идёт), 4 для клетки, где цепочка поворачивает, и 2 для клетки, через которую она проходит прямо. Кроме того, есть ограничения: цепочка не может упираться в стенку, и общее ребро двух соседних клеток либо пересекается цепочкой с каждой стороны, либо нет. Ну, и не должно быть циклов.
    Алгоритм получается таким. Перебираете клетки матрицы по строкам. Для каждой очередной клетки смотрите состояние её соседей, и получаете список возможных состояний самой клетки - их не более трёх. В случае, если есть состояние, которое соединяет две соседние цепочки, надо пройтись по ним - проверить, не являются ли они концами одной цепочки (если да - получится цикл, а он запрещён). Теперь подставляем в клетку по очереди все допустимые состояния, и рекурсивно вызываем функцию перебора для следующей клетки. Когда дойдём до конца - фиксируем найденное разбиение.
    Ответ написан
    Комментировать
  • Как сгенерировать все слова из букв, заданной длины?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    void PrintAllWords(string alphabet,int length){
      PrintAllWords(alphabet,length,"");
    }
    void PrintAllWords(string alphabet,int length,string prefix){
      if(length==0) Console.WriteLine(prefix);
      else foreach(char c in alphabet) PrintAllWords(alphabet,length-1,prefix+c);
    }
    Ответ написан
    Комментировать
  • Как преобразовать определенные биты в числе?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если номер позиции pos, число разрядов len, а число, которое надо изменить - x, то ответом будет
    x | (((1 << len) - 1) << pos)
    Ответ написан
    1 комментарий
  • Какой алгоритм использовать для поиска кратчайшего пути в ненаправленном графе через все вершины?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Гамильтонов путь тут ни при чём - ведь нет запрета проходить одну вершину дважды? Но это в чистом виде задача коммивояжера. Тоже входит в класс NP. Точное решение будет слишком долгим, надо искать приближённые.
    Ответ написан
    3 комментария
  • Как сделать подбор слов из словаря чтобы получилась заданная фраза (анаграммы)?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Многомерная задача о рюкзаке.

    Допустим, у нас есть фраза "aaabbc" и словарь
    aab
    abb
    abc
    bbcc
    aac

    Для фразы считаем, сколько раз в ней встретилась каждая буква. Буква 'a' встречается na=3 раза, буква 'b' - nb=2 раза, буква 'c' - nc=1 раз.
    Заводим битовый массив B с размерностями 0..na, 0..nb. 0..nc (в нашем примере это 24 бита). В элемент (0,0,0) кладём 1, в остальные 0.
    1000 0000
    0000 0000
    0000 0000

    Теперь перебираем слова из словаря. Для каждого слова считаем количество каждой буквы в нём (для aab это ma=2,mb=1,mc=0), и строим массив B1, в котором B1[a,b,c] = B[a,b,c] | B[a-ma,b-mb,c-mc] (для отрицательных индексов считаем значения нулевыми). В нашем случае получится
    1000 0000
    0010 0000
    0000 0000

    Продолжаем для остальных слов. После добавления каждого слова проверяем элемент B[na,nb,nc]. Если он ненулевой - мы нашли вариант (правда, помним из него только последнее слово - остальные восстановим на следующих проходах). Вариант запомним, элемент B[na,nb,nc] обнулим.
    У нас получится:
    abb=(1,2,0)
    
    1000 0000
    0010 0000
    0100 0000
    
    abc=(1,1,1)
    
    1000 0000
    0010 0100
    0100 0001

    Первый вариант есть - он кончается на "abc". Обнуляем B[3,2,1] и продолжаем.
    Слово bbcc=(0,2,2) можно не рассматривать, в нём mc > nc.

    aac=(2,0,1)
    
    1000 0010
    0010 0100
    0100 0001


    Нашли второй вариант - кончается на "aac". Слова в словаре кончились.

    Теперь надо построить фразу "aab" из словаря
    aab
    abb

    и фразу "abb" из словаря
    aab
    abb
    abc
    bbcc

    Теоретически, это можно делать одновременно - но придётся отслеживать несколько индеков. И обнулять их уже нельзя, надо смотреть, не пытаются ли в них записать 1 (даже если там уже было значение 1).
    Если на массив у вас есть 2 ГБ памяти, а разных букв не более 26, то этого хватит на фразу из 39 букв (худший случай - когда 13 букв встречаются по 1 разу, а 13 по 2 раза).
    Ответ написан
    Комментировать
  • Нахождение общей площади, образованной объединением прямоугольников?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Довольно просто решить за O(N^3).
    Пусть координаты k-го прямоугольника - (x1[k],x2[k],y1[k],y2[k]).
    Берёте все x1,x2 (их 2*N штук), сортируете, выкидываете одинаковые. Это проекции вершин на ось X. Обозначим полученный массив A.
    Аналогично с y1,y2 - получается массив B (тоже длиной не больше 2*N).
    Теперь перебираем прямоугольники C=[A[p],A[p+1]]*[B[q],B[q+1]] для всех p,q. Ни один из них не пересекается сторонами исходных прямоугольников, так что если центр C лежит в одном из исходных прямоугольников, то весь прямоугольник принадлежит объединению, если нет - то не имеет с ним общих внутренних точек. Суммируем площади всех прямоугольников, принадлежащих объединению, и пишем ответ.
    Можно соптимизировать до O(N^2). Насчёт O(N*log(N)) не знаю.
    Ответ написан
    6 комментариев
  • Как построить полную сетку из не полной?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Думаю, что лучше ещё раз посмотреть в сторону триангуляции.
    После того, как триангуляция Делоне исходных точек построена, то для любого треугольника и любого уровня высоты пересечение изолинии с треугольником будет либо пустым, либо точкой, либо отрезком. Координаты концов отрезка определяются линейной интерполяцией. И продолжать изолинию можно будет просто шагая по треугольникам.
    Проблема возникнет, когда изолиния уткнётся в вершину. Теоретически, из вершины может быть больше двух путей - если она оказалась седловой точкой. Чтобы этого избежать, достаточно просмотреть в начале все данные, и если попадётся "круглое" число - слегка его изменить.
    Ответ написан
    5 комментариев
  • Как увеличить хеш-таблицу?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Заполненность обычно берут 70-75% от размера таблицы. Можете также попробовать посчитать число шагов в цикле rehash, и если оно для какого-то элемента больше критической величины (например, 3 или 5), значит, пора.
    "Умножить на 2 и взять следующее простое число" - нормально.
    Хешировать заново, конечно, придётся - как же без этого :)
    Кстати, то, что rehash не знает про исходный ключ - это правильно? Ведь так получается, что если для двух ключей rehash дал одинаковые результаты, то и вся последующая цепочка для них совпадёт, и будут повышаться шансы, что новые элементы будут попадать именно в эту цепочку.
    Ответ написан
    5 комментариев
  • Поиск наибольшей возрастающей подпоследовательности?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если сравнивать код с алгоритмом, то пропущено условие d[j-1] < a[i] (видимо, в коде оно должно выглядеть, как ub==v.begin() || ub[-1] < a[i] ). Похоже, что без него алгоритм будет искать не возрастающую, а неубывающую последовательность (но я в этом не уверен).
    Ответ написан
    Комментировать
  • Как определить пересечение отрезка и полигона?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Сначала лучше перейти в систему координат, в которой стороны прямоугольника параллельны координатным осям, а центр - в точке (0,0): пусть при преобразовании (x,y) -> (p*x+q*y+r,-q*x+p*y+s) прямоугольник переходит в abs(X) <= A, abs(Y) <= B.
    Пусть (X1,Y1) и (X2,Y2) - координаты вершин отрезка после преобразования.
    Если max(X1,X2) < -A || min(X1,X2) > A || max(Y1,Y2) < -B || min(Y1,Y2) > B, то не пересекается: отрезок находится за одной из сторон прямоугольника.
    В противном случае условием пересечения, насколько я понимаю, будет
    abs(X1*Y2-Y1*X2) <= abs(A*(Y2-Y1)) + abs(B*(X2-X1))
    Тщательно не проверял, но шансы, что формула правильная, хорошие.
    Ответ написан
    Комментировать
  • Как найти статью на хабре о получении числа 100 из 6 цифр?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Возведения в степень там точно не было. Список вот:
    https://www.dropbox.com/s/v1gp0yp62kr1kdh/badticke...
    Но я не помню, разрешался там унарный минус, или нет.
    Дата создания файла 3 апреля 2013, но вряд ли это поможет.

    UPD. habrahabr.ru/post/174715
    К счастью, она была в хабе "алгоритмы".
    Ответ написан
    Комментировать
  • Практическое применение гиперболических функций?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Редко применяются. Главным образом, потому, что они хорошо выражаются через обычную экспоненту. Конечно, написать (exp(x)+exp(-x))/2 сложнее, чем cosh(x), но функция обычно нужна не сама по себе, а как часть большого выражения.
    Логика подсказывает, что гиперболические функции удобно использовать для формул, связывающих углы и стороны треугольника на плоскости Лобачевского, но та же логика говорит, что в реальной жизни это нужно чуть реже, чем никогда. Можно встретить эти функции в каких-то задачах на теплопроводность... и получается ответ - используйте эти функции тогда, когда встретите их в справочнике. Из остальных случаев могу вспомнить только использование tanh() в формуле релятивистского сложения скоростей. Почему-то перейти от скорости к "быстроте" мне тогда показалось удобным.
    Ну, и полезное применение tanh() - что она отображает всю числовую прямую на интервал (-1,1). Хотя для положительных чисел проще использовать x/(1+x).
    Ответ написан
    Комментировать
  • Дан массив S из n действительных чисел, а также число x. Как за время O(nlogn) определить, можно ли представить х в виде суммы двух элементов из S?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    1) сортируете массив
    2) пускаете два указателя - один с начала, другой с конца. Если сумма элементов под ними меньше X - увеличиваете первый, если больше - уменьшаете второй. Если равна - вы победили, возьмите приз с полки.
    3) если указатели встретились, а сумма так ни разу и не равнялась X - то проиграли, можно стреляться.
    Ответ написан
    2 комментария
  • Как ускорить алгоритм?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Сначала учитываем 3*N^2 треугольников, у которых вершина с прямым углом лежит на одной из координатных осей (или вообще в точке (0,0)).
    Теперь посчитаем все остальные треугольники.
    Пусть (a,b) - координаты вершины с прямым углом, 0 < a <= N, 0 < b <= N.
    Если c=НОД(a,b), a1=a/c, b1=b/c, то оставшаяся вершина должна иметь координаты (x,y)=(a+t*b1,b-t*a1), где t - ненулевое целое число.
    Должны выполняться условия 0 <= x,y <= N. Перепишем их в виде условий на t:
    -a/b1 <= t <= (N-a)/b1, -(N-b)/a1 <= t <= b/a1 (заметим, что a1, b1 больше нуля, так что делить можно). Учитывая, что t должно быть целым, оно лежит на отрезке от
    t0 = -floor(min(a/b1,(N-b)/a1))
    до
    t1 = floor(min((N-a)/b1,b/a1))
    И, поскольку запрещённое значение 0 всегда принадлежит этому отрезку, получаем, что число треугольников с вершиной (a,b) равно t1-t0.
    Перебираем все точки (a,b), считаем t1-t0 и суммируем.
    Всё. Сложность N^2*log(N), основное время тратится на вычисление НОД.
    Если надо быстрее - надо будет думать. Скорее всего, надо будет перебирать взаимно простые пары (a1,b1), считать, сколько точек попало в перекошенный квадрат (переведённый в базис (a1,b1), (-b1,a1)), искать для них формулу и суммировать. Может быть, получится, может быть, нет. Какого порядка N?
    Ответ написан
    Комментировать
  • Как проверить делимость чисел из последовательности 1,11,111,...,11..1 на их порядковые номера?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если N небольшое (скажем, не больше 100000), то можно так:
    char *A=new char[N+1];
      for(int n=1;n<=N;n++){
        int a=0;
        for(int b=0;b<n;b++) a=(10*a+1)%n;
        A[n]=!a;
      }

    При больших N нужно пользоваться аналогом быстрого возведения в степень.
    Получается, что кроме 3 и 37, в числах оказываются такие простые делители, как 163, 757, 1999, 9397... Не понимаю, откуда они берутся.
    UPD.
    757 - делитель 10^27-1
    163 и 9397 - делители 10^81-1
    1999 - делитель 10^999-1
    Ответ написан
    1 комментарий
  • Возможно ли "соединить" два файла, не перемещая данные?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    В любом случае, для этого нужно, чтобы длина первого файла была кратна размеру кластера. Тогда, при наличии физического доступа к диску, шансы могут быть ненулевыми.
    Если файлы для задачи были промежуточными, то, возможно, проще было бы ввести промежуточный тип - "многофайловый файл", который сам разбирается, из какого файла что читать. Там надо переопределить метод ReadBytes() (и всякие мелочи вроде Seek, Position, Length...) Заодно и в других программах может пригодиться.
    Ответ написан
    Комментировать