задача
При заданном входном N сгенерируйте и выведите сетку NxN, где каждая строка, столбец и две диагонали содержат числа от 1 до N
(или от 0 до N
-1, если это проще).
вход
На входе положительное целое число N
. Он представляет количество столбцов и строк в сетке. Для этой проблемы, вы можете предположить, N
будет разумный размер 4 ≤ N ≤ 8
или ( 1 ≤ N ≤ 8
если вы идете за бонус ниже).
Выход
На выходе будет N
× N
сетка. В сетке каждая строка содержит только цифры от 1 до N
, каждый столбец содержит только цифры от 1 до N
, а две диагонали длины N
(одна от (0,0)
до (N-1,N-1)
и одна от (0,N-1)
до (N-1, 0)
) содержат только цифры от 1 до N
. Вы можете использовать цифры от 0 до N−1
. Для каждого N
существует множество возможных решений, вам нужно распечатать только первое, которое вы найдете. Вам не нужно печатать пробелы между числами.
Ограничения
Ваш код должен иметь возможность повторять результаты для N >= 7
. То есть, если вы действительно можете запускать и получать решение N = 7
из своего кода каждый раз, у вас все хорошо. С точки зрения абсолютного ограничения, ваш код должен быть в состоянии решить N = 7
менее чем за 10 минут каждый раз, когда вы его запускаете (т. Е. Если вы зависите от случайных чисел, в худшем случае ваш код должен завершиться менее чем за 10 минут N = 7
) ,
Примеры
Входные данные:
4
Один из возможных выходов:
1 2 3 4 3 4 1 2 4 3 2 1 2 1 4 3
Входные данные:
5
Один из возможных выходов:
1 2 3 4 5 5 3 1 2 4 2 5 4 3 1 4 1 2 5 3 3 4 5 1 2
Входные данные:
8
Один из возможных выходов:
1 2 3 4 5 6 7 8 2 3 1 5 4 8 6 7 4 1 2 3 7 5 8 6 6 4 7 8 1 2 3 5 7 5 8 2 6 3 4 1 5 8 4 6 2 7 1 3 8 7 6 1 3 4 5 2 3 6 5 7 8 1 2 4
счет
Это код-гольф , поэтому выигрывает самый короткий код в байтах, за одним исключением. Для входных N = 2, 3
данных нет действительных решений. Если ваш код может обработать это (выполнить до завершения, не выводя ничего для этих случаев или не выдав пустую строку), и все еще обрабатывает N = 1
(выводит 1
для него), снимите 20% от вашего количества байтов.
источник
N
. Этот код JavaScript работает,N = 1, 5 or 7
хотя, если он кому-нибудь помогает:for(o="",y=N;y--;o+="\n")for(x=N;x--;)o+=(((N-y)*2+x)%N)+1
N = 1
случая: должны возвращаться ответы, которые нацелены на бонус1
, а не пустая строка.Ответы:
Python 3,
275260 байт * 0,8 =220208 байтРекурсивный / возвратный подход.
R
является рекурсивной функцией,l
является столбцом,w
является строкой,K
является следующей записью.Я решил поместить его в 1-мерный массив и в конце довольно распечатать, чтобы упростить индексы.
Безголовая версия:
источник
Funciton , неконкурентный
ОБНОВИТЬ! Массовое улучшение производительности! n = 7 теперь завершается менее чем за 10 минут! Смотрите объяснение внизу!
Это было весело писать. Это решатель грубой силы для этой задачи, написанный на Funciton. Некоторые факты:
3 2 1 0
а не верхний ряд0 1 2 3
.0
(единственное решение) для n = 1.Без дальнейших церемоний:
Объяснение первой версии
Первая версия заняла около часа, чтобы решить n = 7. Далее объясняется, как работает эта медленная версия. Внизу я объясню, какие изменения я внес, чтобы получить менее 10 минут.
Экскурсия на кусочки
Эта программа нуждается в битах. Ему нужно много битов, и они нужны во всех нужных местах. Опытные программисты Funciton уже знают, что если вам нужно n бит, вы можете использовать формулу
который в Funciton может быть выражен как
При выполнении оптимизации производительности мне пришло в голову, что я могу вычислить то же значение гораздо быстрее, используя эту формулу:
Я надеюсь, вы простите меня, что я не обновил всю графику уравнений в этом посте соответственно.
Теперь, допустим, вам не нужен непрерывный блок битов; на самом деле, вы хотите n бит через равные интервалы каждый k-й бит, вот так:
Формула для этого довольно проста, если вы знаете это:
В коде функция
Ӝ
принимает значения n и k и вычисляет эту формулу.Отслеживание используемых номеров
В итоговой сетке n n чисел, и каждое число может иметь любое из n возможных значений. Чтобы отслеживать, какие числа разрешены в каждой ячейке, мы поддерживаем число, состоящее из n ³ битов, в котором бит установлен, чтобы указать, что определенное значение взято. Первоначально это число 0, очевидно.
Алгоритм начинается в правом нижнем углу. После «угадывания» первое число равно 0, нам нужно отследить тот факт, что 0 больше не разрешен ни в одной ячейке в той же строке, столбце и диагонали:
Для этого мы рассчитываем следующие четыре значения:
Текущая строка: нам нужно n битов каждый n-й бит (по одному на ячейку), а затем сдвинуть его к текущей строке r , помня, что каждая строка содержит n ² битов:
Текущий столбец: нам нужно n битов через каждый n ²-й бит (по одному на строку), а затем сдвинуть его к текущему столбцу c , помня, что каждый столбец содержит n битов:
Прямая диагональ: нам нужно n бит каждый ... (вы обратили внимание? Быстро, разберитесь!) ... n ( n +1) -й бит (хорошо!), Но только если мы на самом деле включены прямая диагональ:
Обратная диагональ: две вещи здесь. Во-первых, как мы узнаем, что находимся на обратной диагонали? Математически, это условие c = ( n - 1) - r , что совпадает с c = n + (- r - 1). Эй, это напоминает тебе о чем-то? Это верно, это дополнение к двум, поэтому мы можем использовать побитовое отрицание (очень эффективное в Funciton) вместо декремента. Во-вторых, вышеприведенная формула предполагает, что мы хотим установить младший значащий бит, но в обратной диагонали мы этого не делаем, поэтому мы должны сдвинуть его вверх на ... вы знаете? ... Это верно, n ( п - 1).
Это также единственный, где мы потенциально делим на 0, если n = 1. Однако, Funciton это не волнует. 0 ÷ 0 это просто 0, ты не знаешь?
В коде функция
Җ
(нижняя) берет n и индекс (из которого она вычисляет r и c делением и остатком), вычисляет эти четыре значения иor
складывает их вместе.Алгоритм перебора
Алгоритм грубой силы реализован
Ӂ
(функция вверху). Он принимает n (размер сетки), индекс (где в сетке мы в настоящее время размещаем число) и берется (число с n ³ битами, указывающими, какие числа мы еще можем разместить в каждой ячейке).Эта функция возвращает последовательность строк. Каждая строка является полным решением для сетки. Это полный решатель; он вернет все решения, если вы позволите, но вернет их в виде последовательности с отложенным вычислением.
Если индекс достиг 0, мы успешно заполнили всю сетку, поэтому мы возвращаем последовательность, содержащую пустую строку (единственное решение, которое не охватывает ни одну из ячеек). Пустая строка есть
0
, и мы используем библиотечную функцию,⌑
чтобы превратить ее в одноэлементную последовательность.Проверка, описанная ниже в разделе « Улучшение производительности», происходит здесь.
Если индекс еще не достиг 0, мы уменьшаем его на 1, чтобы получить индекс, по которому нам теперь нужно разместить число (назовите ix ).
Мы используем
♫
для генерации ленивую последовательность, содержащую значения от 0 до n - 1.Затем мы используем
ɓ
(монадное связывание) с лямбдой, которая делает следующее по порядку:0
(пустая последовательность).Җ
для вычисления битов, соответствующих текущей строке, столбцу и диагонали. Сдвинь его на меня, а затемor
на взятый .Ӂ
для извлечения всех решений для оставшихся ячеек, передавая ему новые принятые и уменьшенные ix . Это возвращает последовательность неполных строк; каждая строка имеет символы ix (сетка заполнена до индекса ix ).ɱ
(map), чтобы просмотреть найденные решения и использовать,‼
чтобы объединить i до конца каждого. Добавьте новую строку, если индекс кратен n , в противном случае пробел.Генерация результата
Основная программа вызывает
Ӂ
(брутфорсер) с n , index = n ² (помните, что мы заполняем сетку задом наперед) и берется = 0 (изначально ничего не берется). Если результатом является пустая последовательность (решение не найдено), выведите пустую строку. В противном случае выведите первую строку в последовательности. Обратите внимание, что это означает, что он будет оценивать только первый элемент последовательности, поэтому решатель не продолжает работу, пока не найдет все решения.Улучшение производительности
(Для тех, кто уже прочитал старую версию объяснения: программа больше не генерирует последовательность последовательностей, которую нужно отдельно превратить в строку для вывода; она просто генерирует последовательность строк напрямую. Я соответственно отредактировал объяснение Но это было не главное улучшение. Вот оно.)
На моей машине скомпилированный exe первой версии занял ровно 1 час, чтобы решить n = 7. Это не было в заданном временном интервале в 10 минут, поэтому я не отдыхал. (Ну, на самом деле, причина, по которой я не отдыхал, заключалась в том, что у меня была идея о том, как это значительно ускорить.)
Алгоритм, как описано выше, останавливает свой поиск и отслеживает каждый раз, когда он сталкивается с ячейкой, в которой установлены все биты в взятом числе, указывая, что в эту ячейку ничего нельзя вставить.
Однако алгоритм будет продолжать бесполезно заполнять сетку до ячейки, в которой установлены все эти биты. Было бы намного быстрее, если бы он мог остановиться, как только в любой еще не заполненной ячейке уже установлены все биты, что уже указывает на то, что мы никогда не сможем решить остальную часть сетки, независимо от того, какие числа мы вставим в Это. Но как вы эффективно проверяете, есть ли в любой ячейке свои n битов, не пройдя их все?
Трюк начинается с добавления одного бита на ячейку к взятому числу. Вместо того, что было показано выше, теперь это выглядит так:
Вместо n ³ теперь в этом числе n ² ( n + 1) бит. Функция, которая заполняет текущую строку / столбец / диагональ, была соответствующим образом изменена (на самом деле, полностью переписана, если честно). Эта функция будет по-прежнему заполнять только n битов на ячейку, поэтому добавленный нами дополнительный бит будет всегда
0
.Теперь предположим, что мы на полпути к вычислению, мы просто поместили a
1
в среднюю ячейку, и полученное число выглядит примерно так:Как видите, верхняя левая ячейка (индекс 0) и средняя левая ячейка (индекс 10) теперь невозможны. Как мы можем наиболее эффективно определить это?
Рассмотрим число, в котором установлен 0-й бит каждой ячейки, но только до текущего индекса. Такое число легко рассчитать по знакомой формуле:
Что мы получим, если сложим эти два числа вместе?
Результат:
Как видите, сложение переполняется добавленным нами дополнительным битом, но только если все биты для этой ячейки установлены! Поэтому все, что осталось сделать, это замаскировать эти биты (та же формула, что и выше, но << n ) и проверить, равен ли результат 0:
Если это не ноль, сетка невозможна, и мы можем остановиться.
источник
Haskell, 790 * 0,80 = 632 байта
Я заметил, что эта проблема очень похожа на судоку. Я помню старый решатель судоку, который я написал на Хаскеле, основанный на этом другом на Python. Это мой первый пост или попытка.
Это выполняет бонус, потому что он возвращает
Nothing
дляn=2,3
иJust <result>
дляn>=4
, где<result>
двумерный массив интегральных значений.Смотрите здесь для онлайн-переводчика. Этот код на самом деле длиннее, чем в посте, потому что онлайн-переводчик предъявляет более строгие требования к тому, что формирует законченную программу (правила говорят, что представление может быть функцией). Это представление принимает входные данные в качестве аргумента функции.
источник
c=[1..r]
, чтобы вы могли использовать его вo
иw
. б)minimumBy(\(a,_)(b,_)->compare a b)[...]
естьhead$sortOn fst[...]
. в)v
вv=g0!s
используется только один раз, так что не определить его вообще:l=length$g0!s
. г) у вас еще есть несколько двухбуквенных имен параметров. д) заменитьTrue
на1<2
иFalse
с2<1
. е)and[case g0!s of{[_]->True;_->False}|s<-u]
естьall((==1).length.(g0!))u
.(&)m k=
может быть определен инфикс:m&k=
. з)not(d
Элем(g0!p))
естьnotElem d$g0!p
. я)concat(...)
естьid=<<(...)
. j) использовать инфиксный операторh
, например, дляas%bs=
.``like`this``
!Pyth, 41 байт
Грубая сила ФТВ!
Так как это в основном продолжает пробовать случайные тасовки, пока оно не работает (ну, это продолжает пытаться
n * [shuffle(range(n))]
), это занимает очень, очень много времени. Вот несколько тестовых прогонов, чтобы дать вам представление о том, сколько времени это займет:Это только 4х4, и он работает чуть меньше полсекунды. Я на самом деле обманываю, потому что это лучшее из нескольких испытаний - большинство из них занимают секунду или две.
У меня еще не было времени на 5х5 (оно дошло до завершения, но это было в REPL, и я не рассчитывал его).
Обратите внимание, что правило для ограничения времени было отредактировано в вопросе только после публикации этого ответа.
источник
SWI-Prolog, 326 * 0,80 = 260,8 байта
Редактировать: сохранено 16 байт благодаря @Mat
использование
Позвоните
a(5).
своему переводчику дляN=5
. Это возвращаетfalse
дляN=2
илиN=3
.Поскольку он использует библиотеку CLPFD, это не просто грубая сила. Эта программа может найти решение
N=20
примерно за 15 секунд на моем компьютере.Ungolfed + объяснения:
Это в основном работает как решатель судоку, за исключением того, что ограничения блоков заменяются ограничениями диагоналей.
источник
maplist(maplist(all_distinct), [R,C,D,E])
[R,C,[D,E]]
хотя, потому чтоE
иD
простые списки.N=20
CJam, 87 байт - бонус 20% = 69,6 байт
Жесткие коды ответов. Содержит некоторые непечатные. Работает на
N = 1
протяженииN = 8
.По сути, каждая строка в этой загадочной строке содержит индексы в списке перестановок
range(N)
, закодированных в виде символов Юникода.=:i0+\,m!f=
индексирует в список перестановок, добавляя сначала 0 в конец списка индексов, представляя нижнюю строку0 1 2 ... N-1
. ИбоN < 4
результирующий двумерный массив - нонсенс.`1LL]
создает список[N, pretty output, 1, "", ""]
. Затем(4e<=
извлекает первый элемент из этого спискаN
и извлекает егоmin(N, 4) % 4
из остальной части. ВедьN >= 4
это выходные данные, а в остальном это выходы для особых случаев для первых трех случаев.Попробуй это здесь .
источник
C ++,
672 * 0,80645 * 0,80 = 516 байтПопробуйте онлайн здесь
Поскольку пара ответов уже была опубликована, я подумал, что опубликую версию кода, которую я использовал для создания выходных данных для примеров. Я впервые отвечаю на код-гольф , поэтому все отзывы приветствуются. :)
Ungolfed + объяснения:
По сути, код является грубым решением. Он начинается в первом ряду с 0. Он начинается в первом месте, если этот пункт проходит все проверки, он переходит к следующему числу. Если он заполняет строку, он перемещается в следующую строку. Если это сделано все строки, это означает, что решение было найдено. Если место не проходит все проверки, оно переходит на следующее место. Если он завершил строку, то он возвращается, поскольку число в одной из предыдущих строк предотвращает возможность решения.
источник
if (x[r][p]) return f(c+1,r);
. Я работаю над его сокращением.Clojure,
(215 + 258) * 0,8 = 378,4(174 + 255) * 0,8 = 343,2Разделен на две части: подсчет ошибок
S
и анонимная функция, которая выполняет фактическую оптимизацию с помощью поиска в Tabu .Обновление: короче
S
(считает различные значения в группах), менее оптимальное начальное состояние (без перемешивания).Одноядерные тесты (в миллисекундах) для 4, 5, 6 и 7 запускаются 3 раза:
Оригинал:
Хотелось бы, чтобы он
S
был короче, но, поскольку он учитывает вхождения более одного / раздела, критерий остановки прост(= s 0)
.Многие циклы процессора впустую для не-полезных свопов, например , это не улучшает счет , если вы поменяться
2
с2
, и вам не нужны числами свопа между рядами , как все они имеют различные значения для начала.Тесты с Intel 6700K (в миллисекундах):
Многопоточный с
pmap
:источник