Такую фундаментальную вещь, как уникальность адреса для объектов любых типов, трогать нельзя.
Это наблюдаемое частое, а не фундаментальное свойство. Например, константные строки могут иметь один в могут иметь разный адрес. И сравнение их через сравнение указателей может работать а может нет. И проблема не в компиляторе, когда он сворачивает или нет константы, а в программисте, который полагается на свою логику а не факты
А зачем работа со строками на каждый фрейм нужна? Я как-то ожидал, что строки -- для удобства программирования, но в рантайме от них должен остаться только запах и указатели, не постоянное генерирование...
Ну нас интересует не количество валидных расстановок, а поиск какого-либо решения. Мы же не ищем количество решений, ни пытаемся найти какое-то особое решение -- нам устроит любое.
С этой точки зрения, "каждая строка -- отдельный цвет" это лучший, а не худший случай, мы найдём решение практически с первой попытки.
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а.
Есть много способов отследить и убедиться в этом. Как правило, игнорируемые по причине того, что код -- одноразовый. Написал и забыл, редактируется настолько редко, что нагораживание защит вокруг дороже обойдётся.
Лучший способ в данном конкретном случае (и всех аналогичных) -- использовать статический анализатор, который может поймать косвенно (как в статье) по использованию, или попытаться "догадаться" что массивы должны быть 1-в-1 с enum'ом.
Я удивлён, кстати ( @Andrey2008 ), что PVS-Studio не догадывается что это дескриптор для ENUMа и не проверяет на равенство элементов и порядка констант унутре. А ведь косяк в таких массивах часто получается из-за потерянной запятой.
Это наблюдаемое частое, а не фундаментальное свойство.
Например, константные строки могут иметь один в могут иметь разный адрес. И сравнение их через сравнение указателей может работать а может нет. И проблема не в компиляторе, когда он сворачивает или нет константы, а в программисте, который полагается на свою логику а не факты
Не понимаю примера. Можно в цифрах?...
вот это -- каждая строка свой цвет. Данная раскладка имеет 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)
ручной валидатор кода заточенный под конкретный случай, например, автогенерация юнит теста.
Это навскидку
Есть много способов отследить и убедиться в этом. Как правило, игнорируемые по причине того, что код -- одноразовый. Написал и забыл, редактируется настолько редко, что нагораживание защит вокруг дороже обойдётся.
Лучший способ в данном конкретном случае (и всех аналогичных) -- использовать статический анализатор, который может поймать косвенно (как в статье) по использованию, или попытаться "догадаться" что массивы должны быть 1-в-1 с enum'ом.
Я удивлён, кстати ( @Andrey2008 ), что PVS-Studio не догадывается что это дескриптор для ENUMа и не проверяет на равенство элементов и порядка констант унутре. А ведь косяк в таких массивах часто получается из-за потерянной запятой.
Поможет гарантировать, что к правильным индексами привязаны правильные строки, но не гарантирует, что для всех индексов прописаны строки.
Причем, если к последней определённой константе строка есть -- пропущенные в середине countof() не поймает.
Ссылки на статью не будет, только 50 ссылок на телегу? И ссылка "на источник" только для галочки?
а какой адрес у регистра ecx ?
Да, это "расширение" стандарта как они считают.
В коде это вот тут: https://github.com/gcc-mirror/gcc/blob/885143fa77599c44bfdd4e8e6b6987b7824db6ba/gcc/c-family/c-common.cc#L3378
Прошелся по истории, появилось это в
https://gcc.gnu.org/git/?p=gcc.git;a=commit;f=gcc/cp/typeck.c;h=8d08fdba598cf87c3794df53beae1026345ebb02
То есть это еще с 1994го года, со времён свинины... "From-SVN: r6613"
https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html -- и оно там с доисторических времён, возможно, даже с прошлого тысячелетия... почему?
Работает, в gcc.