Заповніть рядки, стовпці та діагоналі сітки NxN від 1 до N


26

Завдання

Давши вхід 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 = 710 хвилин щоразу, коли ви його запускаєте (тобто, якщо ви залежите від випадкових чисел, в гіршому випадку ваш код все одно повинен закінчитися за 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% знижки на кількість байтів.


1
Пов’язано , але така сітка не працюватиме тут через вимогу діагоналей.
xnor

Мені подобається цей виклик, але я не можу розробити алгоритм для рівних значень 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
user81655

Дуже пов'язаний, можливий дублікат: codegolf.stackexchange.com/q/47846/15599
Level River St

@steveverrill Це був не гольф коду.
випадкова

1
Можливо, ви повинні бути більш чіткими щодо N = 1справи: відповіді, спрямовані на бонус, повинні повертатися 1, а не порожній рядок.
Лінн

Відповіді:


3

Пітон 3, 275 260 байт * 0,8 = 220 208 байт

Рекурсивний / зворотний підхід. Rє рекурсивна функція, lце coLumn, wце рядок, Kце наступний запис.

Я вирішив поставити його в 1d масив і досить роздрукувати його в кінці, щоб зробити індекси простішими.

r=range
n=(int)(input())
def R(A,I):
 l=I%n;w=I//n
 if I==n*n:[print(A[i*n:i*n+n])for i in r(n)];exit()
 for K in r(n):
  if all([all([A[i*n+l]!=K,w!=l or A[i+n*i]!=K,w!=n-1-l or A[n*i+n-i-1]!=K])for i in r(w)]+[A[w*n+i]!=K for i in r(l)]):R(A+[K],I+1)
R([],0)

Безгольова версія:

def Recurse(A,I,n):
 column=I%n
 row=I//n
 if I==n*n:
     [print(*A[i*n:i*n+n])for i in range(n)] # output
     exit() #end recursion
 for K in range(n):
    # try every possibility. Test if they satisfy the constraints:
    # if so, move the index on. If none of them do, we'll return None.
    # if a child returns None, we'll move onto the next potential child:
    # if all of them fail it will backtrack to the next level.
    if all([
        all([
            A[i*n+column]!=K, # column constraint
            row!=column or A[i+n*i]!=K, # diagonal constraint
            row!=n-1-column or A[n*i+n-i-1]!=K # antidiagonal constraint
            ]) for i in range(row)
        ]+[
            A[row*n+i]!=K for i in range(column) # row constraint
            ]):
        Recurse(A+[K],I+1,n)

Recurse([],0,(int)(input()))

22

Функціонал , неконкурентний

ОНОВЛЕННЯ! Масивне поліпшення продуктивності! n = 7 зараз закінчується за 10 хвилин! Дивіться пояснення внизу!

Це було гарно весело писати. Це вирішення цієї проблеми, написане у Funciton. Деякі фактори:

  • Він приймає ціле число на STDIN. Будь-яке стороннє пробіл порушує його, включаючи новий рядок після цілого числа.
  • Він використовує числа 0 до n - 1 (не 1 до n ).
  • Він заповнює сітку "назад", тож ви отримуєте рішення там, де нижній рядок читається, 3 2 1 0а не де читається верхній рядок 0 1 2 3.
  • Він правильно виводить 0(єдине рішення) для n = 1.
  • Порожній вихід для n = 2 і n = 3.
  • Коли компілюється в exe, потрібно приблизно 8¼ хвилин протягом n = 7 (це було приблизно за годину до підвищення продуктивності). Без компіляції (за допомогою інтерпретатора) це займає приблизно в 1,5 рази довше, тому використання компілятора варто.
  • Як особиста віха, я вперше написав цілу програму Funciton, не попередньо написавши програму мовою псевдокоду. Я написав це у фактичному С # першому, хоча.
  • (Однак, це не перший раз, коли я вніс зміни, щоб масово покращити продуктивність чогось у Funciton. Перший раз, коли я це робив, був на факторній функції. Заміна порядку операндів множення зробила величезну зміну через як працює алгоритм множення . На всякий випадок, якщо вам було цікаво.)

Без зайвого галасу:

            ┌────────────────────────────────────┐           ┌─────────────────┐
            │                                  ┌─┴─╖ ╓───╖ ┌─┴─╖   ┌──────┐    │
            │                    ┌─────────────┤ · ╟─╢ Ӂ ╟─┤ · ╟───┤      │    │
            │                    │             ╘═╤═╝ ╙─┬─╜ ╘═╤═╝ ┌─┴─╖    │    │
            │                    │               └─────┴─────┘   │ ♯ ║    │    │
            │                  ┌─┴─╖                             ╘═╤═╝    │    │
            │     ┌────────────┤ · ╟───────────────────────────────┴───┐  │    │
          ┌─┴─╖ ┌─┴─╖   ┌────╖ ╘═╤═╝ ┌──────────┐         ┌────────┐ ┌─┴─╖│    │
          │ ♭ ║ │ × ╟───┤ >> ╟───┴───┘        ┌─┴─╖       │ ┌────╖ └─┤ · ╟┴┐   │
          ╘═╤═╝ ╘═╤═╝   ╘══╤═╝          ┌─────┤ · ╟───────┴─┤ << ╟─┐ ╘═╤═╝ │   │
    ┌───────┴─────┘ ┌────╖ │            │     ╘═╤═╝         ╘══╤═╝ │   │   │   │
    │     ┌─────────┤ >> ╟─┘            │       └───────┐      │   │   │   │   │
    │     │         ╘══╤═╝            ┌─┴─╖   ╔═══╗   ┌─┴─╖ ┌┐ │   │ ┌─┴─╖ │   │
    │     │           ┌┴┐     ┌───────┤ ♫ ║ ┌─╢ 0 ║ ┌─┤ · ╟─┤├─┤   ├─┤ Ӝ ║ │   │
    │     │   ╔═══╗   └┬┘     │       ╘═╤═╝ │ ╚═╤═╝ │ ╘═╤═╝ └┘ │   │ ╘═╤═╝ │   │
    │     │   ║ 1 ╟───┬┘    ┌─┴─╖       └───┘ ┌─┴─╖ │   │      │   │   │ ┌─┴─╖ │
    │     │   ╚═══╝ ┌─┴─╖   │ ɓ ╟─────────────┤ ? ╟─┘   │    ┌─┴─╖ │   ├─┤ · ╟─┴─┐
    │     ├─────────┤ · ╟─┐ ╘═╤═╝             ╘═╤═╝   ┌─┴────┤ + ╟─┘   │ ╘═╤═╝   │
  ┌─┴─╖ ┌─┴─╖       ╘═╤═╝ │ ╔═╧═╕ ╔═══╗ ┌───╖ ┌─┴─╖ ┌─┴─╖    ╘═══╝     │   │     │
┌─┤ · ╟─┤ · ╟───┐     └┐  └─╢   ├─╢ 0 ╟─┤ ⌑ ╟─┤ ? ╟─┤ · ╟──────────────┘   │     │
│ ╘═╤═╝ ╘═╤═╝   └───┐ ┌┴┐   ╚═╤═╛ ╚═╤═╝ ╘═══╝ ╘═╤═╝ ╘═╤═╝                  │     │
│   │   ┌─┴─╖ ┌───╖ │ └┬┘   ┌─┴─╖ ┌─┘           │     │                    │     │
│ ┌─┴───┤ · ╟─┤ Җ ╟─┘  └────┤ ? ╟─┴─┐   ┌─────────────┘                    │     │
│ │     ╘═╤═╝ ╘═╤═╝         ╘═╤═╝   │   │╔════╗╔════╗                      │     │
│ │       │  ┌──┴─╖ ┌┐   ┌┐ ┌─┴─╖ ┌─┴─╖ │║ 10 ║║ 32 ║    ┌─────────────────┘     │
│ │       │  │ << ╟─┤├─┬─┤├─┤ · ╟─┤ · ╟─┘╚══╤═╝╚╤═══╝ ┌──┴──┐                    │
│ │       │  ╘══╤═╝ └┘ │ └┘ ╘═╤═╝ ╘═╤═╝     │ ┌─┴─╖ ┌─┴─╖ ┌─┴─╖                  │
│ │     ┌─┴─╖ ┌─┴─╖  ┌─┴─╖  ┌─┴─╖ ╔═╧═╕     └─┤ ? ╟─┤ · ╟─┤ % ║                  │
│ └─────┤ · ╟─┤ · ╟──┤ Ӂ ╟──┤ ɱ ╟─╢   ├───┐   ╘═╤═╝ ╘═╤═╝ ╘═╤═╝                  │
│       ╘═╤═╝ ╘═╤═╝  ╘═╤═╝  ╘═══╝ ╚═╤═╛ ┌─┴─╖ ┌─┴─╖   │     └────────────────────┘
│         └─────┤      │            └───┤ ‼ ╟─┤ ‼ ║   │        ┌──────┐
│               │      │                ╘═══╝ ╘═╤═╝   │        │ ┌────┴────╖
│               │      │                      ┌─┴─╖   │        │ │ str→int ║
│               │      └──────────────────────┤ · ╟───┴─┐      │ ╘════╤════╝
│               │          ┌─────────╖        ╘═╤═╝     │    ╔═╧═╗ ┌──┴──┐
│               └──────────┤ int→str ╟──────────┘       │    ║   ║ │ ┌───┴───┐
│                          ╘═════════╝                  │    ╚═══╝ │ │ ┌───╖ │
└───────────────────────────────────────────────────────┘          │ └─┤ × ╟─┘
           ┌──────────────┐                                  ╔═══╗ │   ╘═╤═╝
╔════╗     │ ╓───╖ ┌───╖  │                              ┌───╢ 0 ║ │   ┌─┴─╖ ╔═══╗
║ −1 ║     └─╢ Ӝ ╟─┤ × ╟──┴──────┐                       │   ╚═╤═╝ └───┤ Ӂ ╟─╢ 0 ║
╚═╤══╝       ╙───╜ ╘═╤═╝         │                       │   ┌─┴─╖     ╘═╤═╝ ╚═══╝
┌─┴──╖ ┌┐ ┌───╖ ┌┐ ┌─┴──╖ ╔════╗ │                       │ ┌─┤   ╟───────┴───────┐
│ << ╟─┤├─┤ ÷ ╟─┤├─┤ << ║ ║ −1 ║ │                       │ │ └─┬─╜ ┌─┐ ┌─────┐   │
╘═╤══╝ └┘ ╘═╤═╝ └┘ ╘═╤══╝ ╚═╤══╝ │                       │ │   └───┴─┘ │   ┌─┴─╖ │
  │         └─┘      └──────┘    │                       │ └───────────┘ ┌─┤ ? ╟─┘
  └──────────────────────────────┘         ╓───╖         └───────────────┘ ╘═╤═╝
                               ┌───────────╢ Җ ╟────────────┐                │
      ┌────────────────────────┴───┐       ╙───╜            │
      │                          ┌─┴────────────────────┐ ┌─┴─╖
    ┌─┴─╖                      ┌─┴─╖                  ┌─┴─┤ · ╟──────────────────┐
    │ ♯ ║ ┌────────────────────┤ · ╟───────┐          │   ╘═╤═╝                  │
    ╘═╤═╝ │                    ╘═╤═╝       │          │     │              ┌───╖ │
┌─────┴───┘    ┌─────────────────┴─┐   ┌───┴───┐    ┌─┴─╖ ┌─┴─╖          ┌─┤ × ╟─┴─┐
│              │                 ┌─┴─╖ │   ┌───┴────┤ · ╟─┤ · ╟──────────┤ ╘═╤═╝   │
│              │ ┌───╖ ┌───╖  ┌──┤ · ╟─┘ ┌─┴─┐      ╘═╤═╝ ╘═╤═╝        ┌─┴─╖ │     │
│         ┌────┴─┤ ♭ ╟─┤ × ╟──┘  ╘═╤═╝   │ ┌─┴─╖ ┌───╖└┐ ┌──┴─╖      ┌─┤ · ╟─┘     │
│         │      ╘═══╝ ╘═╤═╝ ┌───╖ │     │ │ × ╟─┤ Ӝ ╟─┴─┤ ÷% ╟─┐    │ ╘═╤═╝ ┌───╖ │
│   ┌─────┴───┐     ┌────┴───┤ Ӝ ╟─┴─┐   │ ╘═╤═╝ ╘═╤═╝   ╘══╤═╝ │    │   └───┤ Ӝ ╟─┘
│ ┌─┴─╖ ┌───╖ │     │ ┌────╖ ╘═╤═╝   │   └───┘   ┌─┴─╖      │   │    └────┐  ╘═╤═╝
│ │ × ╟─┤ Ӝ ╟─┘     └─┤ << ╟───┘   ┌─┴─╖ ┌───────┤ · ╟───┐  │ ┌─┴─╖ ┌───╖ │    │
│ ╘═╤═╝ ╘═╤═╝         ╘══╤═╝   ┌───┤ + ║ │       ╘═╤═╝   ├──┴─┤ · ╟─┤ × ╟─┘    │
└───┤     └────┐ ╔═══╗ ┌─┴─╖ ┌─┴─╖ ╘═╤═╝ │ ╔═══╗ ┌─┴─╖ ┌─┴─╖  ╘═╤═╝ ╘═╤═╝      │
  ┌─┴─╖ ┌────╖ │ ║ 0 ╟─┤ ? ╟─┤ = ║  ┌┴┐  │ ║ 0 ╟─┤ ? ╟─┤ = ║    │     │ ┌────╖ │
  │ × ╟─┤ << ╟─┘ ╚═══╝ ╘═╤═╝ ╘═╤═╝  └┬┘  │ ╚═══╝ ╘═╤═╝ ╘═╤═╝    │     └─┤ << ╟─┘
  ╘═╤═╝ ╘═╤══╝ ┌┐     ┌┐ │     │     └───┘       ┌─┴─╖   ├──────┘       ╘═╤══╝
    │     └────┤├──┬──┤├─┘     ├─────────────────┤ · ╟───┘                │
    │          └┘┌─┴─╖└┘       │     ┌┐   ┌┐     ╘═╤═╝ ┌┐   ┌┐            │
    └────────────┤ · ╟─────────┘   ┌─┤├─┬─┤├─┐     └───┤├─┬─┤├────────────┘
                 ╘═╤═╝             │ └┘ │ └┘ │         └┘ │ └┘
                   └───────────────┘    │    └────────────┘

Пояснення першої версії

Перша версія потребувала приблизно години, щоб вирішити n = 7. Далі пояснюється здебільшого, як працювала ця повільна версія. Внизу я поясню, яку зміну я вніс, щоб досягти цього за 10 хвилин.

Екскурсія в шматочки

Для цієї програми потрібні біти. Для цього потрібно багато бітів, і вони потрібні у всіх потрібних місцях. Досвідчені програмісти Funciton вже знають, що якщо вам потрібно n біт, ви можете використовувати формулу

2 ^ n-1

який у фунцитоні можна виразити як

(1 << n) - 1

Роблячи оптимізацію продуктивності, мені спало на думку, що я можу обчислити те саме значення набагато швидше, використовуючи цю формулу:

¬ (−1 << n)

Сподіваюся, ви пробачте мені, що я не оновив усі графіки рівнянь у цій публікації відповідно.

Тепер, скажімо, ви не хочете суцільного блоку бітів; насправді, ви хочете n біт через рівні проміжки часу кожен k- ний біт, як-от так:

                                 LSB
                                  ↓
00000010000001000000100000010000001
                            └──┬──┘
                               k

Формула для цього досить зрозуміла, як тільки ви це знаєте:

((1 << nk) - 1) / ((1 << k) - 1)

У коді функція Ӝприймає значення n і k і обчислює цю формулу.

Слідкуйте за використаними номерами

У підсумковій сітці є n ² числа, і кожне число може бути будь-яким із n можливих значень. Для того, щоб відслідковувати, які числа дозволені в кожній комірці, ми підтримуємо число, що складається з n ³ біт, в якому встановлюється біт, який вказує на те, що прийнято певне значення. Спочатку це число 0, очевидно.

Алгоритм починається в правому нижньому куті. Після «відгадування» першим числом є 0, нам слід відслідковувати той факт, що 0 більше не дозволено в жодній комірці вздовж того ж рядка, стовпця та діагоналі:

LSB                               (example n=5)
 ↓
 10000 00000 00000 00000 10000
 00000 10000 00000 00000 10000
 00000 00000 10000 00000 10000
 00000 00000 00000 10000 10000
 10000 10000 10000 10000 10000
                             ↑
                            MSB

Для цього обчислюємо наступні чотири значення:

  • Поточний рядок: нам потрібно n біт кожного n -го біта (по одному на комірку), а потім перенести його на поточний рядок r , пам'ятаючи, що кожен рядок містить n ² біт:

    ((1 << n²) −1) / ((1 << n) −1) << n²r

  • Поточний стовпчик: нам потрібно n біт кожен n ²-й біт (по одному на рядок), а потім перенести його на поточний стовпець c , пам'ятаючи, що кожен стовпець містить n біт:

    ((1 << n³) −1) / ((1 << n²) −1) << n²r

  • Діагональ вперед: нам потрібні n біт кожні ... (ви звернули увагу? Швидке, зрозумійте!) ... n ( n +1) -міт (добре зроблено!), Але тільки якщо ми насправді на передня діагональ:

    ((1 << n² (n + 1)) - 1) / ((1 << n (n + 1)) - 1), якщо c = r

  • Діагональ назад: Тут дві речі. По-перше, як ми можемо знати, чи перебуваємо ми на відсталій діагоналі? Математично умовою є c = ( n - 1) - r , що таке ж, як c = n + (- r - 1). Гей, це щось вам нагадує? Це правильно, це доповнення двох, тому ми можемо використовувати побітове заперечення (дуже ефективне у Funciton) замість декременту. По-друге, наведена вище формула передбачає, що ми хочемо встановити найменш суттєвий біт, але в діагоналі відсталого ми цього не зробимо, тому нам доведеться змістити його на ... ви знаєте? ... Правильно, n ( n - 1).

    ((1 << n² (n-1)) - 1) / ((1 << n (n-1)) - 1) << n (n-1), якщо c = n + ¬r

    Це також єдиний, де ми потенційно ділимо на 0, якщо n = 1. Однак Funciton не хвилює. 0 ÷ 0 - це лише 0, ви не знаєте?

У коді функція Җ(нижня) бере n та індекс (з якого вона обчислює r і c шляхом поділу та решти), обчислює ці чотири значення та ors їх разом.

Алгоритм грубої сили

Алгоритм грубої сили реалізований Ӂ(функція вгорі). Він бере n (розмір сітки), індекс (де в сітці ми зараз розміщуємо число), і береться (число з n ³ бітами, яке вказує нам, які числа ми можемо розмістити у кожній комірці).

Ця функція повертає послідовність рядків. Кожна рядок є повноцінним рішенням сітки. Це повний вирішувач; він би повертав усі рішення, якщо ви дозволите, але він повертає їх як послідовно оцінену послідовність.

  • Якщо індекс досяг 0, ми успішно заповнили всю сітку, тому повертаємо послідовність, що містить порожню рядок (єдине рішення, яке не охоплює жодної з комірок). Порожній рядок є 0, і ми використовуємо функцію бібліотеки, щоб перетворити його в одноелементну послідовність.

  • Перевірка, описана в поліпшенні продуктивності нижче, відбувається тут.

  • Якщо індекс ще не дорівнював 0, ми зменшуємо його на 1, щоб отримати індекс, на якому зараз нам потрібно поставити число (зателефонуйте цьому ix ).

    Ми використовуємо для створення ледачої послідовності, що містить значення від 0 до n - 1.

    Тоді ми використовуємо ɓ(монадичне зв’язування) з лямбда, яка робить наступне для того, щоб:

    • Спочатку подивіться на відповідний біт, який взяли, щоб вирішити, чи дійсне тут номер чи ні. Ми можемо розмістити число i, якщо і лише тоді, коли прийнято & (1 << ( n × ix ) << i ) ще не встановлено. Якщо вона встановлена, поверніться 0(порожня послідовність).
    • Використовуйте Җдля обчислення бітів, що відповідають поточному рядку, стовпцю та діагоналі. Зсуньте його на i, а потім orна зняте .
    • Рекурсивно вимагають Ӂотримати всі рішення для осередків, що залишилися, передаючи їм нові прийняті та зменшені ix . Це повертає послідовність неповних рядків; кожен рядок містить ix символи (сітка заповнена до індексу ix ).
    • Використовуйте ɱ(карту) для перегляду знайдених таким чином рішень і використовуйте для об'єднання i до кінця кожного. Додайте новий рядок, якщо індекс кратний n , інакше пробіл.

Генерування результату

Основна програма викликає Ӂ(грубий форсер) з n , індекс = n ² (пам'ятаємо, ми заповнюємо сітку назад) і приймаємо = 0 (спочатку нічого не береться). Якщо результат цього - порожня послідовність (рішення не знайдено), виведіть порожню рядок. В іншому випадку виведіть перший рядок у послідовності. Зауважте, що це означає, що він оцінить лише перший елемент послідовності, тому розв’язувач не продовжується, поки не знайде всі рішення.

Поліпшення продуктивності

(Для тих, хто вже читав стару версію пояснення: програма більше не генерує послідовність послідовностей, яку потрібно окремо перетворити на рядок для виведення; вона просто генерує послідовність рядків безпосередньо. Я відповідно відредагував пояснення . Але це було не головне поліпшення. Ось воно і приходить.)

На моїй машині складений exe першої версії знадобився майже рівно 1 годину, щоб вирішити n = 7. Це не було протягом заданого часового обмеження 10 хвилин, тому я не відпочивав. (Ну, насправді, я не відпочивав через те, що в мене з'явилася ідея про те, як їх масово пришвидшити.)

Описаний вище алгоритм зупиняє його пошук і відслідковує кожен раз, коли він стикається з коміркою, в якій встановлені всі біти у прийнятому номері, що вказує на те, що в цю клітинку нічого не можна вставити.

Однак алгоритм продовжуватиме марно заповнювати сітку до комірки, в якій встановлені всі ці біти. Це було б набагато швидше, якби воно могло зупинитися, як тільки в будь - якій ще заповненій комірці вже встановлені всі біти, що вже вказує на те, що ми не можемо вирішити решту сітки незалежно від того, які цифри ми поставимо це. Але як ви ефективно перевіряєте, чи встановлена будь-яка клітинка n своїх бітів, не проходячи їх усіх?

Трюк починається з додавання одного біта на комірку до прийнятого числа. Замість того, що було показано вище, тепер це виглядає так:

LSB                               (example n=5)
 ↓
 10000 0 00000 0 00000 0 00000 0 10000 0
 00000 0 10000 0 00000 0 00000 0 10000 0
 00000 0 00000 0 10000 0 00000 0 10000 0
 00000 0 00000 0 00000 0 10000 0 10000 0
 10000 0 10000 0 10000 0 10000 0 10000 0
                                       ↑
                                      MSB

Замість n ³ в цьому числі є n ² ( n + 1) біт. Функція, яка заповнює поточний рядок / стовпчик / діагональ, була відповідно змінена (насправді, повністю переписана, щоб бути чесною). Ця функція все ще заповнює лише n біт на комірку, тому додатковий біт, який ми тільки що додали, завжди буде 0.

Тепер, скажімо, ми перебуваємо на півдорозі обчислення, ми просто помістили 1в середню комірку, і взяте число виглядає приблизно так:

                 current
LSB              column           (example n=5)
 ↓                 ↓
 11111 0 10010 0 01101 0 11100 0 11101 0
 00011 0 11110 0 01101 0 11101 0 11100 0
 11111 0 11110 0[11101 0]11100 0 11100 0    ← current row
 11111 0 11111 0 11111 0 11111 0 11111 0
 11111 0 11111 0 11111 0 11111 0 11111 0
                                       ↑
                                      MSB

Як бачимо, верхньо-ліва клітина (індекс 0) та клітина середньої лівої (індекс 10) тепер неможливі. Як ми найбільш ефективно це визначаємо?

Розглянемо число, в якому встановлено 0-й біт кожної комірки, але тільки до поточного індексу. Таке число легко обчислити за знайомою формулою:

((1 << (n + 1) i) - 1) / ((1 << (n + 1)) - 1)

Що ми отримали, якби додати ці два числа разом?

LSB                                               LSB
 ↓                                                 ↓
 11111 0 10010 0 01101 0 11100 0 11101 0           10000 0 10000 0 10000 0 10000 0 10000 0        ╓───╖
 00011 0 11110 0 01101 0 11101 0 11100 0     ║     10000 0 10000 0 10000 0 10000 0 10000 0            ║
 11111 0 11110 0 11101 0 11100 0 11100 0  ═══╬═══  10000 0 10000 0 00000 0 00000 0 00000 0  ═════   ╓─╜
 11111 0 11111 0 11111 0 11111 0 11111 0     ║     00000 0 00000 0 00000 0 00000 0 00000 0  ═════   ╨
 11111 0 11111 0 11111 0 11111 0 11111 0           00000 0 00000 0 00000 0 00000 0 00000 0          o
                                       ↑                                                 ↑
                                      MSB                                               MSB

Результат:

             OMG
              ↓
        00000[1]01010 0 11101 0 00010 0 00011 0
        10011 0 00001 0 11101 0 00011 0 00010 0
═════   00000[1]00001 0 00011 0 11100 0 11100 0
═════   11111 0 11111 0 11111 0 11111 0 11111 0
        11111 0 11111 0 11111 0 11111 0 11111 0

Як бачимо, додавання переливається в додатковий біт, який ми додали, але тільки якщо всі біти для цієї комірки встановлені! Тому все, що вам залишається зробити, це замаскувати ці біти (така ж формула, як вище, але << n ) і перевірити, чи результат 0:

00000[1]01010 0 11101 0 00010 0 00011 0    ╓╖    00000 1 00000 1 00000 1 00000 1 00000 1         ╓─╖ ╓───╖
10011 0 00001 0 11101 0 00011 0 00010 0   ╓╜╙╖   00000 1 00000 1 00000 1 00000 1 00000 1        ╓╜ ╙╖    ║
00000[1]00001 0 00011 0 11100 0 11100 0   ╙╥╥╜   00000 1 00000 1 00000 0 00000 0 00000 0  ═════ ║   ║  ╓─╜
11111 0 11111 0 11111 0 11111 0 11111 0   ╓╜╙╥╜  00000 0 00000 0 00000 0 00000 0 00000 0  ═════ ╙╖ ╓╜  ╨
11111 0 11111 0 11111 0 11111 0 11111 0   ╙──╨─  00000 0 00000 0 00000 0 00000 0 00000 0         ╙─╜   o

Якщо вона не дорівнює нулю, сітка неможлива, і ми можемо зупинитися.


3
СВЯТИЙ ФУК. Чувак, це вражаюче.
Deusovi

1
Я другий коментар @ Deusovi, дякую, що ви доклали стільки зусиль до цього
hargasinski

7

Haskell, 790 * 0,80 = 632 байти

import Data.List
import Control.Monad
import Data.Array
s r=let{h as bs=[(a,b)|a<-as,b<-bs];(&)m k=(\(Just x)->x)$lookup k m;j=Just;n=Nothing;c=[1..r];q=delete;u=h[1..r]c;o=[(s,[u |u<-[h[1..r][c]|c<-c]++[h[r]c|r<-[1..r]]++[zip[1..r][1..r],zip[1..r][r,r-1..1]],s`elem`u])|s<-u];k=foldr(>=>)j;a p d g0=k[t p d2|d2<-q d(g0!p)]g0;t p d g0|not(d`elem`(g0!p))=j g0|[]<-v=n|[d2]<-v=k[t s2 d2|s2<-[(s,delete s$nub(concat(o&s)))|s<-u]&p]g1|True=k[l[s|s<-u,not(d`elem`v)]|u<-o&p]g1 where{v=q d(g0!p);g1=g0//[(p,v)];l[]_=n;l[d3]g=a d3 d g;l _ r=j r};w g0|and[case g0!s of{[_]->True;_->False}|s<-u]=j g0|True=msum[a s' d g0>>=w|d<-g0!s']where(_,s')=minimumBy(\(a,_)(b,_)->compare a b)[(l,s)|s<-u,let v=g0!s;l=length v,l>1]}in fmap(fmap(\[x]->x))$w$array((1,1),(r,r))[((i,j),[1..r])|i<-[1..r],j<-[1..r]]

Я помітив, що ця проблема дуже схожа на судоку. Я пам’ятаю старий вирішувач судоку, який я писав у Haskell на основі цього іншого в Python. Це мій перший пост або спроба коду для гольфу.

Це відповідає бонус , тому що він повертається Nothingдо n=2,3і Just <result>для n>=4, де <result>це 2D масив цілочисельних значень.

Дивіться тут для онлайн - перекладача. Цей код насправді довший, ніж той, який розміщено в дописі, оскільки онлайн-перекладач має більш суворі вимоги щодо того, яка форма повноцінної програми (правила кажуть, що подання може бути функцією). Це подання приймає дані як аргумент функції.


1
Кілька швидких порад: а) Ви визначаєте 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. f) and[case g0!s of{[_]->True;_->False}|s<-u]є all((==1).length.(g0!))u.
німі

Швидкі поради, частина II: г) (&)m k=може бути визначено Інфікси: m&k=. з) not(dелем (g0!p))є notElem d$g0!p. і) concat(...)є id=<<(...). к) використовувати оператор інфіксне для h, наприклад as%bs=.
німі

3
Швидкі мета поради: ви можете розмежувати код , який лапки в його правильно використовуючи прямі подвійні лапки ​``like`this``​!
Лінн

4

Pyth, 41 байт

#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K
#                                      ;   # while(True)
 Km.SQQ                                    # K = random QxQ 2d list
       I                               ;   # if ...
        .A                                 # all of...
          m                          Q     # map(range(Q))...
                +                          # concat
                 .TK                       # transpose K
                    .Tm,@@Kdd@@Kt-Qdd      # diagonals of K
                      m             d      # map(range(d))
                       ,                   # 2-elem list of...
                        @@Kdd              # K[n][n]
                             @@Kt-Qd       # and K[len(K)-n-1][n]
                    .T                     # transpose
           qQl{d                           # subarrays have no dups...
                                      B;   # ... then, break
                                        K  # output final result

Груба сила ftw!

Оскільки це в основному продовжує пробувати випадкові перетасування, поки це не працює (добре, він продовжує намагатися n * [shuffle(range(n))]), це займає дуже-дуже довго. Ось кілька тестових прогонів, щоб дати вам уявлення про те, як триває:

llama@llama:~$ time echo 4 | pyth <(echo '#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K')               
[[2, 1, 0, 3], [0, 3, 2, 1], [3, 0, 1, 2], [1, 2, 3, 0]]
echo 4  0.00s user 0.00s system 0% cpu 0.001 total
pyth <(echo '#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K')  0.38s user 0.00s system 96% cpu 0.397 total

Це всього 4х4, і він працює за трохи менше півсекунди. Я насправді обманюю, бо це найкраще з кількох випробувань - більшість з них забирають секунду чи дві.

Мені ще потрібно встановити хронологію на 5x5 (вона закінчилася до завершення один раз, але це було в REPL, і я її не проводив).

Зауважте, що правило про обмеження часу було відредаговане у запитання лише після опублікування цієї відповіді.


Я думаю, це не може зробити 7x7 протягом десяти хвилин? ^^
Лінн

@Mauris Ну, іноді це може бути ...;) Це вимога, яку я пропустив? Я нічого не згадую про обмеження часу у питанні.
Дверна ручка

Я бачу це в коментарях, (не новий коментар, 12 годин тому)
edc65

Вибачте з цього приводу, я не думав про це, поки хтось не згадав про це, я зараз відредагую виклик
hargasinski

1
+1 за абстрактне мистецтво ASCII у коментованій версії. :)
Ільмарі Каронен

3

SWI-Prolog, 326 * 0,80 = 260,8 байт

:-use_module(library(clpfd)).
a(N):-l(N,R),m(l(N),R),append(R,V),V ins 1..N,transpose(R,C),d(0,R,D),maplist(reverse,R,S),d(0,S,E),m(m(all_distinct),[R,C,[D,E]]),m(label,R),m(p,R).
l(L,M):-length(M,L).
d(X,[H|R],[A|Z]):-nth0(X,H,A),Y is X+1,(R=[],Z=R;d(Y,R,Z)).
p([H|T]):-write(H),T=[],nl;write(' '),p(T).
m(A,B):-maplist(A,B).

Редагувати: збережено 16 байт завдяки @Mat

Використання

Зателефонуйте a(5).до свого перекладача для N=5. Це повертається falseдля N=2або N=3.

Оскільки він використовує бібліотеку CLPFD, це не є чистою грубою силою. Ця програма може знайти рішення протягом N=20приблизно 15 секунд на моєму комп’ютері.

Недозволені + пояснення:

Це в основному працює як розв'язувач судоку, за винятком того, що обмеження блоків замінені обмеженнями діагоналей.

:-use_module(library(clpfd)).      % Import Constraints library

a(N):-
    l(N,R),                        % R is a list of length N
    maplist(l(N),R),               % R contains sublists, each of length N
    append(R,V),                   
    V ins 1..N,                    % Each value in the matrix is between 1 and N
    maplist(all_distinct,R),       % Values must be different on each row
    transpose(R,C),
    maplist(all_distinct,C),       % Values must be different on each column
    d(0,R,D),
    maplist(reverse,R,S),          
    d(0,S,E),
    all_distinct(D),               % Values must be different on the diagonal
    all_distinct(E),               % Values must be different on the "anti"-diagonal
    maplist(label,R),              % Affects actual values to each element
    maplist(p,R).                  % Prints each row

l(L,M):-length(M,L).               % True if L is the length of M

d(X,[H|R],[A|Z]):-nth0(X,H,A),Y is X+1,(R=[],Z=R;d(Y,R,Z)). % True if the third argument is the diagonal of the second argument

p([H|T]):-write(H),T=[],nl;write(' '),p(T).  % Prints a row separated by spaces and followed by a new line

Дуже хороша! Ви можете зберегти байти за допомогою:maplist(maplist(all_distinct), [R,C,D,E])
мат

1
@mat Дякую за пропозицію, економить 16 байт. Мені потрібно використовувати [R,C,[D,E]]хоча, тому що Eі Dце прості списки.
Фаталізувати

Правильно, дуже приємне рішення!
мат

2
@Fatalize Просто щоб повідомити, ваше рішення було найбільш вражаючим, оскільки це єдине рішення, яке вирішуютьN=20
hargasinski

1
@Zequ Дякую! Але це, головним чином, завдяки чудовій бібліотеці CLPFD Prolog, яка робить важкий підйом у таких проблемах :)
Fatalize

2

CJam, 87 байт - 20% бонус = 69,6 байт

qi__"@I/l
ŤˏūȻ
܀ᅀ൹৽჈͚
㑢鴑慚菥洠㬝᚜
"N/=:i0+\,m!f=`1LL](4e<=

Жорсткі коди відповідей. Містить деякі недруковані матеріали. Працює N = 1наскрізь N = 8.

По суті, кожен рядок у цій загадковій рядку містить індекси до списку перестановок range(N), закодованих як символи Unicode.

=:i0+\,m!f=індексує до списку перестановок, додаючи спочатку 0 до кінця списку індексів, що представляє нижній рядок 0 1 2 ... N-1. Бо N < 4отриманий 2D масив - це нісенітниця.

`1LL]створює список [N, pretty output, 1, "", ""]. Потім (4e<=вискакує перший елемент із цього списку Nта витягує цей min(N, 4) % 4елемент з решти його. Бо N >= 4це вихід, і в іншому випадку це виводи спеціального випадку для перших трьох випадків.

Спробуйте тут .


0

C ++, 672 * 0,80 645 * 0,80 = 516 байт

#include <iostream>
int **b,**x,**y,*d,*a,n;
#define R(i) for(i=0;i<n;i++){
#define E(e) e=new int[n];
int f(int c,int r) {int i=0,p=c-1;if(r>=n)return 1;if(c == n + 1)return f(1,r+1);R(i)int m=r==i,s=r+i==n-1;if(!b[r][i]&&!x[r][p]&&!(m&&d[p])&&!(s&&a[p])&&!y[i][p]){b[r][i]=c;x[r][p]=1;y[i][p]=1;if(m)d[p]=1;if(s)a[p]=1;if(f(c+1,r))return 1;b[r][i]=0;x[r][p]=0;y[i][p]=0;if(m)d[p]=0;if(s)a[p]=0;}}return 0;}
int main(){std::cin>>n;int i=0,j=0;b=new int*[n];x=new int*[n];y=new int*[n];E(d);E(a);R(i)E(b[i]);E(x[i]);E(y[i]); d[i]=0;a[i]=0;R(j)b[i][j]=0;x[i][j]=0;y[i][j]=0;}}if(f(1,0)){R(i)R(j)std::cout<<b[i][j];}std::cout<<std::endl;}}}

Спробуйте його онлайн тут

Оскільки пара відповідей уже опублікована, я думав, що опублікую гольф-версію коду, який використовував для отримання результатів для прикладів. Я вперше відповідаю на з , тому всі відгуки вітаються. :)

Недозволені + пояснення:

По суті, код є жорстоким форсуванням рішення. Він починається в першому ряду, з 0. Він починається на першому місці, якщо ця пляма проходить всі перевірки, вона переходить до наступного числа. Якщо він заповнює рядок, він переходить до наступного ряду. Якщо це зроблено всі рядки, це означає, що рішення було знайдено. Якщо пляма не пройшла всіх перевірок, вона переходить на наступне місце. Якщо рядок зроблено, то він повертається назад, оскільки число в одному з попередніх рядків запобігає неможливості рішення.

#include <iostream>

// global variables to save bytes on passing these are function arguments
int **b, // this will store the state of the board
    **x, // if x[i][j] is true, row i of b contains the number j
    **y, // if y[i][j] is true, column i of b contains the number j
    *d,  // if d[i] the main diagonal of b contains i
    *a,  // if a[i] the antidiagonal of a contains i
    n;

// preprocessor to save bytes on repeated statements
#define R(i) for(i=0;i<n;i++){
#define E(e) e=new int[n];

// Recursively looks for a solution 
// c - the current number to insert in row r
// r - the current row to fill
int f (int c, int r) {
        int i=0,p=c-1;
        if (r >= n) return 1;             // we are done
        if (c == n + 1) return f(1,r+1);  // this row is full, move to the next row
        R(i)                              // go through the positions in this row,
                                                                            // trying to fill them with c
                int m=r==i, s=r+i==n-1;   // check if this position (r,i) is on ones
                                                                            // of the diagonals
                // if this spot isn't filled, and the row (r), column (i) and diagonals
                // (if it's on the diagonal) doesn't contain the number, fill the spot
                if (!b[r][i] && !x[r][p] && !(m&&d[p]) && !(s&&a[p]) && !y[i][p]) {
                        // fill the spot, and indicate that this row, column and diagonals 
                        // contain this number, c
                        b[r][i]=c; x[r][p]=1; y[i][p]=1;
                        if (m) d[p]=1; if (s)a[p]=1;

                        // move onto to the next number, if you find a solution, stop
                        if (f(c+1,r)) return 1;

                        // with this number in this spot, a solution is impossible, clear
                        // its, and clear the checks
                        b[r][i]=0; x[r][p]=0; y[i][p]=0;
                        if (m) d[p]=0; if (s) a[p]=0;
                }
        }

        return 0; // a solution wasn't found
}

int main() {
        std::cin >> n; // get n from STDIN

        // initialization 
        int i=0,j=0;
        b=new int*[n]; x=new int*[n]; y=new int*[n];
        E(d); E(a);
        R(i)
                E(b[i]); E(x[i]); E(y[i]); // initialization the inner arrays of b, x, y
                d[i]=0; a[i]=0;

                R(j)
                        b[i][j]=0; x[i][j]=0; y[i][j]=0; // ensure each point is initialized as 0
                }
        }

        // find a solution starting at the top-left corner and print it if it finds one
        if (f(1,0)) {
                R(i)
                        R(j)
                                std::cout<<b[i][j];
                        }
                        std::cout<<std::endl;
                }
        }
}

Після того, як перечитувати код, я зрозумів , деякі з перевірок не може бути необхідно, наприклад , як if (x[r][p]) return f(c+1,r);. Я працюю над скороченням.
hargasinski

0

Clojure, (215 + 258) * 0,8 = 378,4 (174 + 255) * 0,8 = 343,2

Розділити на дві частини: підрахунок помилок Sта анонімну функцію, яка робить фактичну оптимізацію за допомогою пошуку Tabu .

Оновлення: коротше S(підраховує чіткі значення в групах), менш оптимальний початковий стан (без перетасування).

(defn S[I n](count(mapcat set(vals(apply merge-with concat(flatten(for[R[(range n)]i R j R v[[(I(+(* n i)j))]]][{[1 i]v}{[2 j]v}(if(= i j){3 v})(if(=(- n 1 i)j){4 v})])))))))
#(if-let[s({1[0]2()3()}%)]s(loop[I(vec(for[R[(range %)]i R j R]i))P #{}](let[[s I](last(sort-by first(for[R[(range(count I))]i R j R I[(assoc(assoc I i(I j))j(I i))]:when(not(P I))][(S I %)I])))](if(=(*(+(* % 2)2)%)s)(partition % I)(recur I(conj P I))))))

Одні основні орієнтири (у мілісекундах) за 4, 5, 6 та 7 виконання 3 рази:

[[  131.855337   132.96267    138.745981]
 [ 1069.187325  1071.189488  1077.339372]
 [ 9114.736987  9206.65368   9322.656693]
 [36546.309408 36836.567267 36928.346312]]

Оригінал:

(defn S[I n](apply +(flatten(for[p(concat(partition n I)(for[p(apply map vector(partition n(range(count I))))](map I p))[(take-nth(inc n)I)][(rest(butlast(take-nth(dec n)I)))])](remove #{1}(vals(frequencies p)))))))
#(if-let[s({1[0]2()3()}%)]s(loop[I(vec(flatten(map shuffle(repeat %(range %)))))P #{}](let[[s I](first(sort-by first(for[R[(range(count I))]i R j R I[(assoc(assoc I i(I j))j(I i))]:when(not(P I))][(S I %)I])))](if(= s 0)(partition % I)(recur I(conj P I))))))

Я хотів би, щоб він Sбув коротшим, але оскільки він підраховує кількість випадків, що перевищують один / розділ, критерій зупинки простий (= s 0).

Багато цикли процесора даремно для не-корисних свопів, наприклад , це не покращує рахунок , якщо ви помінятися 2з 2, і вам не потрібні числами свопу між рядами , як всі вони мають різні значення для початку.

Орієнтовні показники з Intel 6700K (в мілісекундах):

(defn S[I n]( ... )
(def F #( ... ))

(defmacro mytime[expr]
  `(let [start# (. System (nanoTime)) ret# ~expr]
     (/ (double (- (. System (nanoTime)) start#)) 1000000.0)))

(pprint(vec(for[n[4 5 6 7]](vec(sort(repeatedly 5 #(mytime (F n)))))))

[[  43.445902   45.895107   47.277399   57.681634    62.594037]
 [ 222.964582  225.467034  240.532683  330.237721   593.686911]
 [2285.417473 2531.331068 3002.597908 6361.591714  8331.809410]
 [3569.62372  4779.062486 5725.905756 7444.941763 14120.796615]]

Багатопотокові pmap:

[[   8.881905  16.343714   18.87262  18.9717890   22.194430]
 [  90.963870 109.719332  163.00299  245.824443  385.365561]
 [ 355.872233 356.439256 1534.31059 2593.482767 3664.221550]
 [1307.727115 1554.00260 2068.35932 3626.878526 4029.543011]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.