NxNグリッドの行、列、および対角線を1〜Nで塗りつぶします


26

仕事

入力Nが与えられると、各行、列、および2つの対角線に1〜N(またはNそれが簡単な場合は0〜- 1)の数字が含まれるNxNグリッドを生成して出力します。

入力

入力は正の整数Nです。グリッド内の列と行の数を表します。この問題についてNは、妥当なサイズであると想定できます4 ≤ N ≤ 8(または1 ≤ N ≤ 8、以下のボーナスを利用する場合)。

出力

出力はN× Nグリッドになります。グリッドでは、各行には1〜の数字のみが含まれN、各列には1〜の数字のみが含まれ、N長さの2つの対角線N(0,0)to (N-1,N-1)から(0,N-1)toまで(N-1, 0))には1〜の数字のみが含まれNます。0〜の数字を使用できますN−1。それぞれについてN、多くの可能な解決策がありますが、最初に見つけたものだけを印刷する必要があります。数字の間にスペースを印刷する必要はありません。

制約

コードはの結果を繰り返し生成できる必要がありますN >= 7。つまり、N = 7毎回コードから実際に実行して解決策を得ることができれば、それで十分です。絶対的な制限に関しては、コードはN = 7実行するたびに10分以内に解決できる必要があります(つまり、乱数に依存している場合、最悪の場合、コードは10分以内に終了しますN = 7) 。

  • 入力: 4

    1つの可能な出力:

    1 2 3 4
    3 4 1 2
    4 3 2 1
    2 1 4 3
    
  • 入力: 5

    1つの可能な出力:

    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つの可能な出力:

    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
    

得点

これはであるため、1つの例外を除き、バイト単位の最短コードが優先されます。入力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を

非常に関連性の高い
Level River St

@steveverrillそれはコードゴルフではありませんでしたが。
randomra

1
たぶん、あなたはN = 1ケースについてより明確にすべきです:ボーナスを目指す回答1は空の文字列ではなく、返されるべきです。
リン

回答:


3

Python 3、275 260バイト* 0.8 = 220 208バイト

再帰的/バックトラッキングアプローチ。Rは再帰関数、l列は列、行wK次のエントリです。

インデックスを簡単にするために、1次元配列に入れて最後にきれいに印刷することにしました。

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
  • n = 1の場合0、正しく出力されます(唯一のソリューション)。
  • n = 2およびn = 3の場合の空の出力。
  • exeにコンパイルすると、n = 7の場合に約8¼分かかります(パフォーマンスが改善されるまでに約1時間かかりました)。(インタプリタを使用して)コンパイルしなければ、約1.5倍の時間がかかるため、コンパイラを使用する価値があります。
  • 個人的なマイルストーンとして、最初に擬似コード言語でプログラムを作成せずにFuncitonプログラム全体を作成したのはこれが初めてです。私は最初に実際のC#でそれを書きました。
  • (ただし、これはFuncitonで何かのパフォーマンスを大幅に改善するために変更を行ったのは初めてではありません。それを行ったのは階乗関数でした。乗算のオペランドの順序を入れ替えることにより、乗算アルゴリズムのしくみ。好奇心が強い場合に備えて。)

難しい話は抜きにして:

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

最初のバージョンの説明

最初のバージョンでは、n = 7 を解決するのに約1時間かかりました。以下では、この低速バージョンがどのように機能するかについて主に説明します。下部では、10分以内に変更するためにどのような変更を行ったかを説明します。

ビットへの遠足

このプログラムにはビットが必要です。それには多くのビットが必要であり、それらをすべて適切な場所に必要とします。経験豊富なFuncitonプログラマーは、nビットが必要な場合、式を使用できることを既に知っています。

2 ^ n-1

Funcitonでは次のように表現できます

(1 << n)-1

パフォーマンスの最適化を行うと、次の式を使用して同じ値をはるかに高速に計算できることがわかりました。

¬(−1 << n)

この投稿の方程式のグラフィックスをすべて更新しなかったことをお許しください。

ここで、連続したビットブロックが必要ない場合を考えてみましょう。実際、次のように、k番目のビットごとに一定の間隔でnビットが必要です。

                                 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

このために、次の4つの値を計算します。

  • 現在の行:我々は必要Nビット毎のnビット目(セルごとに1つ)、および現在の行にそれを移行R、すべての行を記憶することは含まN ²ビット。

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

  • 現在の列:ビットごとにnビット(行ごとに1つ)が必要です。次に、現在の列cにシフトします。すべての列にnビットが含まれることに注意してください。

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

  • 前方対角線:nビットが必要です...(注意しましたか?すばやく見つけてください!)... nn +1)番目のビット(よくやった!)前方対角線:

    ((1 <<n²(n + 1))-1)/((1 << n(n + 1))-1)c = rの場合

  • 後方対角線: 2つのことがここにあります。最初に、後方の対角線にいるかどうかをどのようにして知るのでしょうか?数学的には、条件はc =(n − 1)− rであり、これはc = n +(− r − 1)と同じです。ねえ、それは何かを思い出させますか?そうです、2の補数なので、デクリメントの代わりにビットごとの否定(ファンシトンで非常に効率的)を使用できます。第二に、上記の式は最下位ビットを設定することを前提としていますが、逆方向の対角線では設定しないため、上にシフトする必要があります...ご存知ですか?...そうです、nn − 1)。

    ((1 <<n²(n-1))− 1)/((1 << n(n-1))− 1)<< n(n-1)if c = n +¬r

    これは、n = 1の場合に0で割る可能性がある唯一の方法でもあります。ただし、Funcitonは気にしません。0÷0はちょうど0です、わかりませんか?

コードでは、関数Җ(一番下の関数)はnインデックス(除算と剰余によるrcの計算元)を取り、これらの4つの値を計算orし、一緒にsします。

ブルートフォースアルゴリズム

ブルートフォースアルゴリズムはӁ(上部の関数)によって実装されます。それはとりn個(グリッドサイズ)、インデックス(ここで、グリッドに我々は現在、数を配置している)、および撮影した(と数nはその数字我々は各セルの場所まだすることができ、私たちに言って³ビット)。

この関数は、文字列のシーケンスを返します。各文字列は、グリッドに対する完全なソリューションです。完全なソルバーです。許可するとすべてのソリューションが返されますが、遅延評価されたシーケンスとして返されます。

  • インデックスが0に達した場合、グリッド全体を正常に埋めたので、空の文字列(セルをカバーしない単一のソリューション)を含むシーケンスを返します。空の文字列はです0。ライブラリ関数を使用して、それを単一要素のシーケンスに変換します。

  • 以下のパフォーマンス改善で説明されているチェックがここで行われます。

  • インデックスがまだ0に達していない場合は、1をデクリメントして、数字を配置する必要があるインデックスを取得します(そのixを呼び出します)。

    0からn − 1の値を含む遅延シーケンスを生成するために使用します。

    次にɓ、以下を順番に実行するラムダで(モナドバインド)を使用します。

    • 内の関連するビット初めて目に撮影した数は、ここか有効であるかどうかを決定します。私たちは、数置くことができ、私がしている場合にのみあれば取ら&(1 <<(N × IX)<< iは)すでに設定されていませんが。設定されている場合は、戻ります0(空のシーケンス)。
    • Җ現在の行、列、および対角線に対応するビットを計算するために使用します。で、それをシフトI、その後、orそれが上に撮影しました
    • Ӂ残りのセルのすべてのソリューションを取得するために再帰的に呼び出し、新しい取得およびデクリメントされたixを渡します。これは、不完全な文字列のシーケンスを返します。各文字列にはix文字があります(グリッドはインデックスixまで埋められます)。
    • ɱ(map)を使用して、こうして見つかったソリューションを調べ、iをそれぞれの末尾に連結するために使用します。インデックスnの倍数の場合は改行を追加し、そうでない場合はスペースを追加します。

結果を生成する

メインプログラムの呼び出しӁ(ブルートの収納箱)とNインデックス = N ²(我々は後方グリッドを埋める覚えている)と、撮影した = 0(最初は何も行われません)。この結果が空のシーケンス(ソリューションが見つからない)の場合、空の文字列を出力します。それ以外の場合は、シーケンスの最初の文字列を出力します。これは、シーケンスの最初の要素のみを評価することを意味することに注意してください。これが、ソルバーがすべての解を見つけるまで続行しない理由です。

パフォーマンスの改善

(すでに古いバージョンの説明を読んでいる人のために:プログラムは、出力用の文字列に個別に変換する必要があるシーケンスのシーケンスを生成しなくなりました。単に文字列のシーケンスを直接生成します。 。しかし、それは主な改善点ではありませんでした。

私のマシンでは、最初のバージョンのコンパイル済みexeがn = 7 を解決するのにほぼ1時間かかりました。これは、指定された10分の制限時間内ではなかったため、休みませんでした。(まあ、実際、私が休まなかった理由は、それを大幅に高速化する方法についてこのアイデアを持っていたからです。)

上記のアルゴリズムは、取得した番号のすべてのビットが設定されているセルに遭遇するたびに検索を停止し、バックトラックし、このセルには何も配置できないことを示します。

ただし、アルゴリズムは、これらすべてのビットが設定されているセルまでグリッドを無駄に埋め続けます。まだ入力されいないセルにすべてのビットが設定されたらすぐに停止できれば、はるかに高速になります。それ。しかし、すべてのセルを通過せずにセルのnビットが設定されているかどうかを効率的に確認するにはどうすればよいですか

トリックは、取得した数値にセルごとに1ビットを追加することから始まります。上に示したものの代わりに、次のようになります。

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で、中間のセルにa を配置しただけで、取得した数値が次のようになっているとします。

                 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)

これらの2つの数値を加算するとどうなりますか?

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
私は2番目の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の他の1つに基づいて書いた古い数独ソルバーを覚えています。これは私の最初のコードのゴルフポストまたは試みです。

これはNothingfor n=2,3Just <result>forを返すため、ボーナスを満たします。n>=4ここ<result>で、は整数値の2D配列です。

オンライン通訳についてはこちらをご覧ください。そのコードは実際には投稿のコードよりも長くなっています。これは、オンラインインタープリターが完全なプログラムを構成するものについてより厳しい要件を持っているためです(ルールは提出が機能であると言う)。この送信は、関数の引数として入力を受け取ります。


1
いくつかの簡単なヒント:a)を定義するためc=[1..r]oおよび内で使用できますw。B)minimumBy(\(a,_)(b,_)->compare a b)[...]ですhead$sortOn fst[...]。c)vin v=g0!sは一度しか使用されないため、定義しないでくださいl=length$g0!s。d)まだ2文字のパラメーター名があります。e)に置き換えるTrue1<2してFalse2<1。f)and[case g0!s of{[_]->True;_->False}|s<-u]all((==1).length.(g0!))uです。
nimi

クイックヒント、パートII:g)(&)m k=挿入語を定義できます:m&k=。h)not(delem (g0!p))notElem d$g0!pです。i)concat(...)id=<<(...)です。J)のための中置演算子を使用するhなど、as%bs=
nimi

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

それはたった4x4で、0.5秒弱で実行されます。これはいくつかの試行のうちで最高だからです。ほとんどの試行は1〜2秒かかります。

私はまだ5x5のタイミングを取得していません(一度完了しましたが、REPLにあり、タイミングを計っていませんでした)。

時間制限のルールは、この回答が投稿された後にのみ質問に編集されることに注意してください。


私はこれが10分以内に7x7を行うことができないと思いますか?^^
リン

@Maurisまあ、時にはできる...;)それは私が逃した要件ですか?質問に制限時間について言及しているものは見当たりません。
ドアノブ

コメントに表示されます(新しいコメントではなく、12時間前)
edc65

申し訳ありませんが、そのことについて、私は今、それを述べた誰か、私は挑戦を編集しますまで考えていなかった
hargasinski

1
コメント付きバージョンの抽象ASCIIアートの+1。:)
イルマリカロネン

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).

編集:@Matのおかげで16バイト節約

使用法

a(5).通訳を呼び出しますN=5。これを返すfalseためN=2N=3

CLPFDライブラリを使用するため、これは純粋なブルートフォースではありません。このプログラムはN=20、コンピューターで約15秒で解決策を見つけることができます。

Ungolfed +説明:

これは基本的に数独ソルバーのように機能しますが、ブロックの制約が対角線の制約に置き換えられます。

:-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ライブラリー、のためにだ
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<=このリストから最初の要素をポップし、残りの要素からth番目の要素Nを取得しますmin(N, 4) % 4。の場合N >= 4、それは出力です。それ以外の場合は、最初の3つのケースの特別なケースの出力です。

ここで試してみてください


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;}}}

こちらからオンラインでお試しください

いくつかの回答がすでに投稿されているので、例の出力を生成するために使用したコードのゴルフ版を投稿すると思いました。回答するのはこれが初めてなので、すべてのフィードバックを歓迎します。:)

Ungolfed +説明:

本質的に、コードは強引なソリューションです。最初の行から始まり、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と、タブー検索を介して実際の最適化を行う匿名関数の2つの部分に分かれています

更新:短く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)です。

役に立たないスワップでは多くのCPUサイクルが無駄になります。たとえば、でスワップ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]]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.