Совершенно верно! Оптимизация перебора -- мастхэв для ускорения решения с ростом размерности задачи. Это не меняет худший случай, зато сильно улучшает средний случай.
Но реализация интеллектуального перебора требует значитнельно больше времени, чем экономится. Сейчас типичное поле решается на 0.05-0.1 сек, практически равно дисперсии времени наведения на кнопку и нажатия на неё. Как быстро окупится 30 минут дополнительного кодинга и усложнение когда при сокращении этого времени даже в 10 раз?
В случае же, если этот код болтается в продакшене, при загрузке проца в полку -- уже интереснее расклад. До тех пор -- только как развлечение.
Там откусывается не сверху, а снизу от факториала несколько строк. С практической точки зрения -- это всё мелкая константа, которой можно пренебречь и считать что оно факториальное.
Total count of solutions: 2 checked: 100
При такой задаче, для поиска всех надо перебрать 100 вариантов. От полного факториала (5!=120) недалеко ушли :)
Первое решение найдено на 56м варианте, второе -- на 81м.
Такую фундаментальную вещь, как уникальность адреса для объектов любых типов, трогать нельзя.
Это наблюдаемое частое, а не фундаментальное свойство. Например, константные строки могут иметь один в могут иметь разный адрес. И сравнение их через сравнение указателей может работать а может нет. И проблема не в компиляторе, когда он сворачивает или нет константы, а в программисте, который полагается на свою логику а не факты
А зачем работа со строками на каждый фрейм нужна? Я как-то ожидал, что строки -- для удобства программирования, но в рантайме от них должен остаться только запах и указатели, не постоянное генерирование...
Ну нас интересует не количество валидных расстановок, а поиск какого-либо решения. Мы же не ищем количество решений, ни пытаемся найти какое-то особое решение -- нам устроит любое.
С этой точки зрения, "каждая строка -- отдельный цвет" это лучший, а не худший случай, мы найдём решение практически с первой попытки.
p.s.: ради интересу добавил вывод числа найденных решений в скрипт; сегодня только одно решение. в задаче от 28го (та что в статье) тоже только одно решение. интересно...
Хм. Чет я и правда затупил. У нас не дерево, а матрица.
Разверну чуть больше.
Рекурсия используется в качестве программного "цикла циклов", соответственно, эквивалентная схема:
for( i1 in range(N)):
for( i2 in range(N)):
for( i3 in range(N)):
..
for( iN in range(N)):
то есть таки да, это цикл на N^N итераций.
Проверки вертикалей (проверки горизонталей всегда true так как циклы работают каждый на своей выделенной горизонтали) превращают в факториал, так как мы заменяем вызов следующих циклов на константную проверку массива.
Проверок диагоналей у нас нет. Проверка соседей меняет выкидывание одной клетки в строке на выкидывание почти трёх:
0 1 0 0 => 0 1 0 0 => 0 1 0 0
? ? ? ? ? x ? ? x x x 0
Но это только в прямо последующей строке. Фактически, снижает с полного факториала до факториала N-2 что не влияет.
Проверка цветовой области не сильно влияет на оценку, так как в худшем случае у нас каждая строка -- отдельный цвет, и не влияет на все другие строки.
Таким образом, фактическая сложность получается факториал, а не квадрат.
... что только подтверждает, что решение "в лоб" обычно далеко от эффективности; ... что не меняет того факта, что решатель справляется за практически невидимое для глаза время, так как решение точно существует всегда; задача предполагает решение человеком; и максимальный размер я пока за ~150 дней видел только 15*15.
Мы не проверяем каждую клетку с каждой клеткой. Переход на следующую строку только если успешно поставили ферзя, поэтому сложность проверки первой строки - просто число ячеек. Тот факт, что мы для каждой ячейки куда смогли ставим а потом убираем (если не прокатило) ферзя - это просто накладные расходы, спрятаны в не показанной константе при О-большом
Имеется ввиду любая реализация проверки на число элементов.
Да, как, и писал выше, явные индексы делают эту проверку бесполезной. В остальном случае, проверка достаточна с практической точки зрения для напоминания о месте использования. Типичные случаи добавления нового элемента проблем не доставляют, и пропущенную запятую при первоначальном наполнении ловит.
sizeof -- размер в байтах, countof -- размер в элементах. Существует как _countof и в вариациях имени типа array_size, ARRAY_SIZE, std::ranges::size и все прочие. Просто проверяем, на момент компиляции, что размер массива сопадает числу элементов ENUMа.
А англоязычные вернулись? 🤔
Целый мемориал котикам в Talos Principle 2
Совершенно верно! Оптимизация перебора -- мастхэв для ускорения решения с ростом размерности задачи. Это не меняет худший случай, зато сильно улучшает средний случай.
Но реализация интеллектуального перебора требует значитнельно больше времени, чем экономится. Сейчас типичное поле решается на 0.05-0.1 сек, практически равно дисперсии времени наведения на кнопку и нажатия на неё. Как быстро окупится 30 минут дополнительного кодинга и усложнение когда при сокращении этого времени даже в 10 раз?
В случае же, если этот код болтается в продакшене, при загрузке проца в полку -- уже интереснее расклад. До тех пор -- только как развлечение.
Потому, что если не разминать мозги, они застывают.
Там откусывается не сверху, а снизу от факториала несколько строк. С практической точки зрения -- это всё мелкая константа, которой можно пренебречь и считать что оно факториальное.
При такой задаче, для поиска всех надо перебрать 100 вариантов. От полного факториала (5!=120) недалеко ушли :)
Первое решение найдено на 56м варианте, второе -- на 81м.
У каждой клетки есть цвет, и число цветов равно числу столбцов и рядов. Цвета еще и связные области, но для имеющегося решения это не важно.
Это наблюдаемое частое, а не фундаментальное свойство.
Например, константные строки могут иметь один в могут иметь разный адрес. И сравнение их через сравнение указателей может работать а может нет. И проблема не в компиляторе, когда он сворачивает или нет константы, а в программисте, который полагается на свою логику а не факты
Не понимаю примера. Можно в цифрах?...
вот это -- каждая строка свой цвет. Данная раскладка имеет 14 решений, но первое находится буквально сразу, как первая же возможная расстановка:
Hidden text
А зачем работа со строками на каждый фрейм нужна? Я как-то ожидал, что строки -- для удобства программирования, но в рантайме от них должен остаться только запах и указатели, не постоянное генерирование...
У нас в задаче N строк, N столбцов и N разных цветов. То есть если есть только одна область -- задача представляет собой одну единственную клетку 1*1
Ну нас интересует не количество валидных расстановок, а поиск какого-либо решения. Мы же не ищем количество решений, ни пытаемся найти какое-то особое решение -- нам устроит любое.
С этой точки зрения, "каждая строка -- отдельный цвет" это лучший, а не худший случай, мы найдём решение практически с первой попытки.
p.s.: ради интересу добавил вывод числа найденных решений в скрипт; сегодня только одно решение. в задаче от 28го (та что в статье) тоже только одно решение. интересно...
для VR очень даже надо рендерить два кадра одновременно.
Хм. Чет я и правда затупил. У нас не дерево, а матрица.
Разверну чуть больше.
Рекурсия используется в качестве программного "цикла циклов", соответственно, эквивалентная схема:
то есть таки да, это цикл на N^N итераций.
Проверки вертикалей (проверки горизонталей всегда true так как циклы работают каждый на своей выделенной горизонтали) превращают в факториал, так как мы заменяем вызов следующих циклов на константную проверку массива.
Проверок диагоналей у нас нет. Проверка соседей меняет выкидывание одной клетки в строке на выкидывание почти трёх:
Но это только в прямо последующей строке. Фактически, снижает с полного факториала до факториала N-2 что не влияет.
Проверка цветовой области не сильно влияет на оценку, так как в худшем случае у нас каждая строка -- отдельный цвет, и не влияет на все другие строки.
Таким образом, фактическая сложность получается факториал, а не квадрат.
... что только подтверждает, что решение "в лоб" обычно далеко от эффективности;
... что не меняет того факта, что решатель справляется за практически невидимое для глаза время, так как решение точно существует всегда; задача предполагает решение человеком; и максимальный размер я пока за ~150 дней видел только 15*15.
Мы не проверяем каждую клетку с каждой клеткой.
Переход на следующую строку только если успешно поставили ферзя, поэтому сложность проверки первой строки - просто число ячеек.
Тот факт, что мы для каждой ячейки куда смогли ставим а потом убираем (если не прокатило) ферзя - это просто накладные расходы, спрятаны в не показанной константе при О-большом
25 лет суммарно. 300 интернов с опытом по месяцу
удивительно, как странно выглядит код в gcc если добавить -fPIC. clang'овый сразу уже позиционно-независим и не меняется от флага
Имеется ввиду любая реализация проверки на число элементов.
Да, как, и писал выше, явные индексы делают эту проверку бесполезной. В остальном случае, проверка достаточна с практической точки зрения для напоминания о месте использования. Типичные случаи добавления нового элемента проблем не доставляют, и пропущенную запятую при первоначальном наполнении ловит.
sizeof -- размер в байтах, countof -- размер в элементах. Существует как _countof и в вариациях имени типа array_size, ARRAY_SIZE, std::ranges::size и все прочие. Просто проверяем, на момент компиляции, что размер массива сопадает числу элементов ENUMа.
Средствами языка и без накладных расходов:
Когда в массиве элементы перечислены последовательно -- сравнивая countof() в compile-time, мой основной подход;
Мета-макросы, как @RR_Zz упоминул ниже;
unit-testing с перечислением всех вариантов (я считаю форменным издевательством для данного случая, впрочем).
Средствами языка с накладными расходами:
Используя enum class + функцию с case вместо массива;
Проверки assert()'ы или просто if()'s в функциях доступа для проверок или включая проверки на доступ вне массива
Средставми вне языка без накладных расходов в рантайме:
стандартные статические анализаторы;
генератор кода по внешнему источнику (в каком-то смысле вариация x-macro)
ручной валидатор кода заточенный под конкретный случай, например, автогенерация юнит теста.
Это навскидку