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

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

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

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

    Вот набросок кода на C++, думайте, что это псевдокод.

    vector<int> ConvexHull(vector<double> x, vector<double> y) {
      int first = ...; // Тут поиск самой нижней-левой точки.
      const int n = x.size();
      int cur = first;
      vector<int> ans{first};
      // Текущий вектор стороны может идти вправо от самой нижней точки.
      double side_x = 1;
      double side_y = 0;
      while (true) { 
        int bsti = -1;
        // Вектор на лучшую точку.
        double bst_x = 0;
        double bst_y = 0;
        // Расстояние до нее.
        double bst_dist = 0;
        for (i = 0; i < n; ++i) {
          if (i == cur) continue;
          // Вектор на текущую точку.
          double cur_x = x[i] - x[cur];
          double cur_y = y[i] - y[cur];
          // Отсекаем точки назад вдоль оболочки на той же стороне.
          // Можно заменить пометкой в bool in_hull[],
          // которая ставится при добавлении точки в ответ (ans).
          if (side_x*cur_y-side_y*cur_x == 0 && side_x*cur_x+side_y*cur_y < 0) continue;
          double vec = bst_x*cur_y-bst_y*cur_x;
          double dist = cur_x*cur_x+cur_y*cur_y;
          // Берем точку, если она правее текущего кандидата. Или на той же прямой, но ближе.
          if (bsti == -1 || vec < 0 || (vec == 0 && dist < bst_dist)) {
            bsti = i;
            bst_dist = dist;
            bst_x = cur_x;
            bst_y = cur_y;
          } 
        }
        // Сделали полный оборот.
        if (bsti == first) break;
        side_x = bst_x;
        side_y = bst_y;
        and.push_back(bsti);
        cur = bsti;
      }
      return ans;
    }
    Ответ написан
    Комментировать
  • Как возвести в степень значение biginteger на biginteger?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Отличное свойство модуля в том, что можно при вычислениях брать модуль на каждом этапе (если нет делений - в этом случае надо искать обратное по модулю). Поэтому исходное выражение можно переписать так: (g^m mod n^2) * (r^n mod n^2) mod n^2

    И код вычисления этого такой:
    nn = n.multiply(n);
    res = g.modPow(m, nn).multiply(r.modPow(n, nn)).mod(nn);
    Ответ написан
  • Что не так с поиском математического ожидания?

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

    Только после исправления ваше решение, скорее всего, не пройдет по времени. Потому что K до 10^16 в задаче и вы итерируетесь до него.

    Преобразуйте вашу формулу для difference - вынесите за скобки chess[i].number. Получите что-то вроде abs(chess[i].number*A + B).

    И найти минимум этой линейной функции можно в одно действие - это -B/A. Но это если искомое число может быть дробным. Для целых чисел вам надо посмотреть 2 значения - округленное дробное вниз и вверх. Можно их получить проводя все вычисления только в целых числах. А дальше надо выбрать одну из двух меньших разностей, при условии, что chess[i].number там не равен K.
    Ответ написан
  • Есть ли уже существующая структура данных для данной задачи?

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

    Постройте двумерный массив, который будет говорить, можно ли собрать данную сумму первыми k объектами.

    Используя его можно относительно быстро восстанавливать все множества с заданной суммой а заодно посмотреть, какие суммы возможно набрать.
    Ответ написан
    1 комментарий
  • Как найти нижнюю границу сложности алгоритма?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Тут на глаз можно. Очевидно же, что T(n) > n. Поэтому можно взять n как нижнюю границу.
    Ответ написан
    Комментировать
  • Что не так с решением задачи?

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

    Надо порисовать на бумажке и убедится, что из строки S вы можете получить только строки вида S(k1)S(k2)S(k3)...S(kn), Где S(x) - x-ый префикс строки S, т.е. первые x символов.
    При этом k1 >= k2, ... k1>= kn.

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

    Доказывается по индукции по количеству операций. Изначально строка именно такого вида (k1=длина строки). После каждой операции, эта строка сколько-то раз копируется и как-то обрезается по длине. Поэтому любая полученная строка будет такого вида. И каждую такую строку можно построить: применяйте операции с k равными k1, k1+k2, k1+k2+k3... Так вы за n операций соберете все префиксы по одному.

    Теперь, раз вам во входном файле задана подстрока результата, вам надо проверить, что эта строка состоит из подстроки S, за которой идут префиксы строки S.

    Так, пример из условия bcabc состоит из подстроки bc и одного префикса abc.

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

    Потом нужно построить граф: из позиции 0 сделайте ребра во все позиции, где заканчивается какая-то подстрока первой строки в начале второй. Из позиции i>0 сделайте ребра во все позиции j такие, что подстрока i...j префикс первой строки.

    Потом, если в этом графе есть путь из вершины 0 в вершину с номером длины второй строки - YES. Иначе - NO.

    Это решение за квадрат, если подстроки искать не совсем в тупую: для проверки на суффикс внешний цикл ведите по правому концу, а внутренний - по левому концу убывая. Так можно сдвигать второй указатель, пока строка остается суффиксом, проверяя каждый раз только один символ. Похожим образом можно проверять все подстроки на совпадение с префиксом.
    Ответ написан
    Комментировать
  • Почему скорость битовых операций отличается в 1000 раз?

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

    Если вы место обращения к массиву будете делать currentByte & (1 << (7-j)), то должно работать почти также быстро.

    P.s. и вообще у вас какой-то странный выбор порядка битов. Обычно сдвигают вправо, к младшему биту. Тогда вместо маски будет тупо 0x1.
    Ответ написан
  • Может есть алгоритм равномерного распределения на основе хэша SHA256?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Если вам надо, чтобы после увеличения n, хеши выдавали те же позиции - то это невозможно сделать равномерно без запоминания. Потому что пусть n=10 изначально - тогда ЛЮБОЙ хеш должен выдать значение меньше 10. А потом при увеличении n любой хеш, все-равно должен выдать 0-9. Пусть даже n станет 100000 - нельзя использовать добавленные позиции, кроме 10 первых.

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

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

    Подсказка: тут отлично зайдет цикл do ... while. Одна переменная должна указывать, кто из людей сейчас активный. Вы должны дописать к ответу его букву и перейти к другому человеку взяв соответствующий элемент из массива A. Цикл должен работать, пока вы не вернетесь к первому человеку. Само решение будет буквально 3 строки, если не считать скобки.
    Ответ написан
    Комментировать
  • Как усовершенствовать алгоритм для уравнения Диофанта?

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

    Там по вашей ссылке в задаче есть подсказка: уравнение можно переписать в виде (x-2y)(x+2y)=n

    Отсюда получается решение: Перебирайте все делители n, не превосходащие корень. Пусть делитель d, тогда второй множитель будет n/d.

    Осталось решить систему линейных уравнений:

    x-2y=d
    x+2y=n/d


    Решается в уме - x=(d+n/d)/2, y=(n/d-d)/4

    Отслось только учесть диофантовость уравнения - ответ должен быть неотрицательные целые числа. Значит, надо чтобы оба делителя d, n/d давали одинаковый остаток от деления на 4 и 2. Ну и d<=n/d, но это учтется перебором делителя до корня.

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

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

    Во первых, тут есть неоднозначность. Может быть так что теги A,B,C попарно несовместимы, но заодно попарно несовместимы теги A,D,E - куда относить тег A, в какую группу?

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

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

    Еще есть Алгоритм Брона — Кербоша. Он перебирает все максимальные клики.
    Ответ написан
  • Preimage - атака нахождения прообраза. Теория + практика. Пофантазируем?

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

    Посмотрите, сколько ваша видео карточка выдает хэшей. Беглый гуглеж дает что-то порядка 3Ghash/s Поделите 10^k на 3*10^9 - получите максимальное время в секундах. Можно ожидать, что в среднем понадобится половина этого времени.

    Если вы используете кластер, то вам понадобится (сколько времени вы насчитали выше)/(сколько времени вы готовы ждать) одинаковых компов в кластере. Если это обычные юзеры, то нельзя сильно нагружать и понадобится в 10 раз больше компов.

    Если я не сбился в расчетах, то вам понадобится 5-6 лет на одной карточке, чтобы подобрать текст. Если вы готовы ждать месяц, то вам понадобится 600 компов в кластере.
    Ответ написан
    1 комментарий
  • Что не так с алгоритмом создания максимального числа из исходного, используя двоичную СС?

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

    Тут есть 2 куска 1110. Но за первым идет "010", а за вторым "011". Начинать со второго - выгоднее.

    В этой задаче очень маленькие ограничения - можно полностью перебирать все возможные числа и брать максимальное. Можно даже не переводить в двоичную систему, а воспользоваться битовыми операциями.
    Когда вы узнали, сколько битов в числе, то самый младший бит x можно получить как x&1. Сдвинуть все биты числа на одну позицию вправо - это x >> 1. При этом младший бит пропадает. Чтобы вставить новый бит b слева нужно сделать x | (b << k) - тут k - номер позиции этого бита, считая с 0.

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

    И так, для развития: Если бы ограничения были слишком большие (число в десятки тысяч бит), то тут пришлось бы применять умные строковые алгоритмы. Это была бы задача на поиск лексикографически максимального циклического сдвига. Решается с помощью суффиксного дерева, суффиксного массива или суффиксного автомата.
    Ответ написан
    2 комментария
  • Как вывести определенное значение в центр массива при его разной размерности?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Edit:

    Раз задача построить концентрические вложенные квадраты, то есть 2 подхода.

    Простой для понимания, но менее эффективный (но все-равно отлично быстрый для смешных ограничений в задаче) - напишите функцию, которая в двумерном массиве рисует квадрат заданного размера вокруг центра. Это тупо 4 последовательных, не вложенных цикла. Каждый рисует одну сторону квадрата. Тупо цикл до n, где n - длина стороны квадрата. Там одна координата фиксирована, а другая пробегает вдоль стороны. Надо чуть-чуть подумать, и составить формулы, какие строки и столбцы будут закрашены.

    Заведите двумерный массив, заполните его пробелами, и потом циклом от n%2 до n с шагом 2 рисуйте квадраты.

    Второй, более эффективный, подход - это немного подумать. Возьмите клетчатый лист, или в редакторе каком-либо нарисуйте ответ для n=9,10. Подумайте над паттернами. Первая строка будет всегда из n #. Вторая будет #, n-2 " ", #. Следующая "# #...# #" и так далее.

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

    Подсказка для формулы - имеет значение, как близко строка к середине массива и четность n. Расстояние до середины можно получить как abs(n/2 - i)
    Ответ написан
    4 комментария
  • Как реализовать распознавание кривых на canvas?

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

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

    Составим функцию ошибки f(x1, y1, x2, y2) = (X(i/n)-x_i)^2+(Y(i/n)-y_i)^2. Тут x_i, y_i - координаты i-ого пикселя в нарисованной кривой, пронумерованные от 0 до n, X(t), Y(t) - координаты точки на кривой Безье - линейные комбинации x1, x2 и y1, y2 с известными вычисляемыми от t коэффициентами. Это фактически сумма квадратов расстояний от каждого пикселя до неизвестной кривой. Ее можно минимизировать подобно методу наименьших квадратов - считайте производную по всем переменным, приравнивайте к 0. Получите 4 неизвестные и 4 линейных уравнения. Можно вообще на бумажке руками методом Краммера решить, а можно алгоритмом Гаусса подсчитать.

    Тут есть допущение, что i-ый пиксель будет аппроксимироваться t=i/n точкой на кривой. Вообще говоря, это не гарантированно, но в качестве некой грубой меры оптимальности подойдет. Может вообще отлично работать будет и так, я не знаю. Поэксперементируйте. Но еще можно потом честно искать ближайшую точку на кривой к заданному пикселю как оптимум полинома 6-ей степени от t, когда все опорные точки кривой фиксированы. Тут надо брать производную, находить ее нули и среди них брать лучшее t. Чтобы найти нули полинома 5-ой степени можно рекурсивно брать производную, находить нули полинома меньшей степени, и потом на каждом монотонном отрезке искать пересечение с OX бинарным поиском. Или использовать метод Ньютона. Это давно решенная задача - должно быть куча готовых решений.

    Когда вы так научились считать расстояние от кучки пикселей до кривой Безье, можно локально градиентным спуском по 4-м координатам x1, x2, y1, y2 улучшать эту правильную метрику с оптимума полученного грубой метрикой.

    Вот мы и умеем аппроксимировать кучку пикселей одной кривой. Заодно мы получаем метрику близости кривой к пикселям. Но надо сделать ее не зависящей от длины - поэтому складывайте квадраты расстояний и делите на количество пикселей.

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

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

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

    Уберите ненужные переменные. Еще, зачем у вас там i % columns?

    Похоже, надо в output.txt выводить, а не в stdout.

    Еще, советую выводить перевод строки всегда, и не пропускать его на последней строке вывода.

    Чтобы не было проблем с пробельными символами - читайте в двух вложенных циклах до rows и columns по одному char (а не string до eof). Это и ввод упростит и сделает его более безопасным ко всяким артефактам в тестах.
    Ответ написан
  • Сколько шагов в нахождении наибольшего делителя в алгоритме Евклида?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    n=5, m=1. первый шаг - поделить, осталось n=1, m=0. Второй шаг - проверить, заметить 0 и остановиться.

    n=5,m=3, первый шаг - поделить. осталось n=3, m=2. Второй шаг - опять делим, остается m=2, n=1. Третий шаг - делим, остается n=1, m=0, четвернтый шаг - видим 0, остановились.

    для n=m=5 ответ 1, возможно, потому что в алгоритме стоит проверка m==0 || m == n.
    Ответ написан
  • Каким образом получить такой набор? Какие методы использовать?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это похоже на задачу set cover. Легких алгоритмов тут нет. Только полный перебор с отсечениями. Еще всякие методы отжига и эволюционные алгоритмы могут найти хорошее решение. Ну и, задачу можно переформулировать в виде integer linear programming и решать какой-то из существующих библиотек.

    Если количество слов маленькое (типа 25-30), то можно решать динамическим программированием по маске покрытых слов.

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

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Можно попробовать вычислить корень быстрым алгоритмом. Но там сложно. Гуглите Karatsuba square root. Есть открытые реализации. Есть еще какой-то адский метод через быстрое преобразование Фурье, попробуйте погуглить и его.

    Более простой в реализации, но менее быстрый метод вычисления корня - бинарный поиск. Храните l, r, l^2, r^2 и lr. По этим числам можно вычислить m=(l+r)/2, m^2, m*l, m*r без длинных умножений, а только складывая длинные числа и деля на 2. Вам надо поддерживать, чтобы l^2 <= n <= r^2. Изначально можно сделать l=1, r=10^51 (или больше - половина длины входного числа + немного, чтобы точно квадрат был больше n), потом делить отрезок пополам и отбрасывать ненужную половину.

    Еще есть вероятностный метод через символ Лежандра/Якоби. Это будет самым быстрым методом.

    Это как смотреть на последнюю цифру. Квадраты могут давать там 0, 1, 4, 9, 6, 5. Но нет ни одного квадрата, который оканчивался бы на 2. Т.е. если число заканчивается на 2, то можно сразу говорить, что это не квадрат. Это мы взяли остаток от деления на 10 (последняя цифра) и посмотрели, какие из них хорошие. Вот символ Лежандра - это такая проверка для модуля по любому простому числу (а не 10).

    Если брать некоторое простое число p, то n может быть квадратом, только если символ Лежандра (n/p) - равен 1 или 0 (По научному: n - должно быть квадратичным вычетом).

    Если брать небольшие (<64-битные) простые числа, то можно за линию считать n%p и потом вычислять символ Лежандра (n%p/p) по алгоритму через символ Якоби за O(log(p)^2). Когда подсчитали символ Лежандра и если он -1, то n - точно не корень.

    Тут проблема в том, что это необходимый, но недостаточный критерий - если для какого-то p вы получили -1 - то это точно не квадрат. Но возможно можно подобрать такое число, что все ваши тесты дадут 1, а оно не квадрат. Поэтому надо брать много простых чисел. Скажем, 20. Желательно еще числа брать достаточно большими. Но их не надо искать каждый раз, можно захардкодить. Грубая прикидка говорит, что вероятность ошибки такого алгоритма 2^(-количество простых чисел).

    Т.е. берете много простых чисел. Считаете для каждого n%p выполняя деление большого числа на короткое (один проход по массиву цифр). Потом считаете символ Лежандра. Если получили где-то -1 - то точно не квадрат. Иначе - скорее всего квадрат.

    Можно совместить вероятностный тест и вычисление корня. Сначала проверьте парочку простых чисел на символ Лежандра для отсечения точно не квадратов. Еще проверку последней цифры можно сделать, это очень дешево. Если не отсеклись, то считайте корень. Так будет всегда работать правильно но будет быстрее работать в некоторых случаях.
    Ответ написан
    Комментировать
  • Перевод фото таблицы в матрицу из 0 и 1?

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

    На изображении квадратики размера 2x2, между ними один пиксель пустой. И по границам еще пустые пиксели. Если обрезать 1 пиксель справа и снизу, то получится, что каждой ячейке соответствует квадрат 3x3: или целиком белый, или с 4-мя черными пикселями в правом нижнем углу.

    Соответственно, достаточно проверять пиксель с координатами 3*i+1, 3*j+1 для получения значения ячейки [i][j] (все индексы с 0).

    Т.е. в вашем языке программирования получаете изображение в виде матрицы пикселей, потом двумя циклами до 100 заполняете все ячейки матрицы - смотрите на нужный пиксель и, если он не белый, ставите 1.

    Если изображение с артефактами сжатия, то можно брать среднее между 4-мя пикселями 3*i+x, 3*j+y (x,y=1..2) по всем трем компонентам (RGB) и смотреть, что оно сильно отличается от черного. Для этого делаете еще 2 вложенных цикла по x и y, там прибавляете значение цветов пикселя в переменную, потом делите на 12 и сравниваете, допустим, с 200. Если меньше - это черный квадрат, если больше - то белый.
    Ответ написан
    2 комментария