六角形の隣接


28

六角形スパイラルの例

上の画像は、六角形の六角形のグリッドを表示しています。グリッド内の各セルには、図のように中心から反時計回りに螺旋状にインデックスが割り当てられます。グリッドは無期限に継続することに注意してください-上の写真は単に最初のセクションです。次の六角形は60と37に隣接します。

あなたのタスクは、このグリッド上の2つのセルが隣接しているかどうかを判断することです。

2つのセルインデックスを指定して、2つのセルが隣接している場合は真理値を出力し、そうでない場合は偽値を出力するプログラムまたは関数を作成します。

実用的な理由に制限されない場合、コードは理論的にはあらゆるサイズの入力に対して機能するはずです。

真実のテストケース:

0, 1
7, 18
8, 22
24, 45
40, 64
64, 65

偽のテストケース:

6, 57
29, 90
21, 38
38, 60
40, 63
41, 39
40, 40

これはので、バイト単位の最短回答が勝ちです。説明は、非難解な言語であっても奨励されています。

回答:


7

エリキシル263の 257 264 223 214 218 214バイト

a=fn x,y->i=&(&1*(&1-1)*3+1)
[x,y]=Enum.sort [x,y]
if x<1,do: y in 1..6,else: (y-x==1||fn->a=y-trunc i.((r=(:math.sqrt(12*x-3)+3)/6)+1)
t=trunc r
a in [0,1,rem(b=x-i.(t)+1, t)<1&&b-t*6!=0&&2]||b<2&&a==-1 end.())end

オンラインでお試しください!

無償版

def get_ring(x) do
    1/6*(:math.sqrt(12*x-3)+3)
end

def inv_get_ring(x), do: x*(x-1)*3+1

def ring_base(x), do: inv_get_ring(trunc(x))

def is_corner(x) do
    ring = trunc(get_ring(x))
    inv_ring = ring_base(ring)
    stuff = (x-inv_ring+1)
    rem(stuff, ring) == 0
end

def is_last(x),do: ring_base(get_ring(x)+1)-1 == x
def is_first(x),do: ring_base(get_ring(x)) == x

def hex_adj(x, y) do
    {x, y} = {min(x,y), max(x,y)}
    cond do 
        x == 0 ->y in 1..6      
        y-x==1 -> true
        true ->
            adj = trunc(inv_get_ring(get_ring(x)+1))
            number = if is_corner(x)&&!is_last(x), do: 2, else: 1
            if y-adj in 0..number do
                true
            else
                is_first(x) && y == adj-1
            end
    end
end
  • trunc(number) 数値の整数部分を返します
  • rem(a,b) a / bの残りを返します
  • cond do end これは、多くの命令型言語のelse ifまたはswitch case節と同等です

説明

get_ring(index)

インデックスの「リング」を計算します。

例:1-6の場合は1、7-18の場合は2など

これは、結果がfloor編集された場合にのみ適用されます。 末尾の数字は、そのタイルがリングの周りにどれだけ離れているかを表します。

inv_get_ring(ring)

の逆数を計算しget_ring(index)ます。

ring_base(リング)

リング内の最初のタイルのインデックスを計算します。

is_corner(インデックス)

コーナーは、外側のリングに3つの隣接するタイルがあるタイルです。最も内側のリングは完全にコーナーで構成されています。

例:21,24,27,30,33,36

is_last(インデックス)

このインデックスがリング内で最も高い場合はtrueです。

is_first(インデックス)

これがリングのベースタイルである場合はtrueです。


2
エッジケースの修正を含めるように回答を編集しました:)
ガルノ

私はあなたのゴルフバージョンを最初から最後まで繰り返しましたが、その後あなたはあなたのアプローチを変えたように見えました。あなたの現在のゴルフバージョンはまだゴルフバージョンと同等ですか?
ジョンマイケルロー

はい、そうです!Elixirで変数をインラインで宣言できることを知りました。これにより、コードの最初にラムダ関数を取り除くことができました。より効率的にするために、変数を少し整理しました。
ガルノ

5

MATL47 45 44 43 41バイト

s:"JH3/^6:^t5)w5:&)@qY"w@Y"]vYs0hG)d|Yo1=

オンラインでお試しください!または、すべてのテストケースを確認します

ボーナスとして、コードはセル中心の位置をトレースする六角形のスパイラルを生成します。これは、コードの最後の部分を変更することにより、MATL Onlineでグラフィカルに見ることができます。

説明

一般的な考え方    このコードは、最初に単位ステップで十分に大きな六角形のらせんを構築します。らせんは、セルの中心の位置を表す複素数のベクトルとして定義されます。入力ベクトルを使用してそのベクトルにインデックスを付け、絶対差を計算すると、指定された2つのセル間の距離が得られます。結果が1の場合にのみ、セルは隣接しています。ただし、浮動小数点の不正確性のため、1と比較する前に丸めが必要です。

スパイラル    の構築スパイラルには、2つの入力の合計に等しい数の「レイヤー」があります。これは必要以上に(はるかに)大きく、入力セルがスパイラルに存在することを保証します。

スパイラルを構築するために、複素数j 2/3jは虚数単位)が最初に計算されます。これを指数1から6に上げると、基本的な変位のセットが得られ、これらの変位を順番にたどると六角形がトレースされます。この六角形は、「閉じている」ことを除いて、らせんの最も内側の層を形成します。実際には、その六角形を最後のステップで「成長」させ、2倍のポイント(2つのグループに整列)を持つより大きな六角形をトレースして、スパイラルの次の層を形成します。ここの図を参照してください。次のレイヤーには、最初のポイントの3倍のポイントがあります(3つのグループで)。こちらをご覧ください

これを行うために、基本セット(南東方向を指す)からの5番目の変位が「成長」ステップとして選択されます。レイヤーkはそのステップで始まり、最初の5つの基本ステップがk回繰り返され、次に6番目のステップ(東方向)がk -1回繰り返されます。これは、上記の2つの図を見ると明らかになることを願っています。

すべてのレイヤーを含む結果のベクトルは、スパイラルをトレースする複雑な変位を表します。累積和は、セルの中心の実際の座標を示します。

最後に、0にある初期セルは、このベクトルの最後に付加さます。これは、MATLがモジュラー1ベースのインデックスを使用し、インデックス0が配列の最後のエントリを参照するためです。

隣接関係    のテスト入力番号で指定された2つのセルが選択され、それらの座標が減算され、絶対値が丸められて1と比較されます。

コメント付きコード

s         % Implicitly input array of two numbers. Push their sum, say S
:         % Range [1 2 ... S]
"         % For each k in [1 2 ... S]
  J       %   Push 1j
  H3/     %   Push 2, then 3, then divide: gives 2/3
  ^       %   Power
  6:      %   Push [1 2 ... 6]
  ^       %   Element-wise power. This is the array of 6 basic displacements
  t5)     %   Duplicate. Get 5th entry
  w5:&)   %   Swap. Push subarray with entries 1st to 5th, then push 6th
  @qY"    %   Repeat the 6th displacement k-1 times
  w@Y"    %   Swap. Repeat 1st to 5th displacements k times
]         % End
v         % Concatenate everything into a column vector
Ys        % Cumulative sum. This gives the cell center coordinates
0h        % Append a 0
G)        % Index by the input vector
d|        % Absolute difference
Yo        % Round to nearest integer
1=        % Does it equal 1? Implicitly display

説明を追加してもらえますか?
シャギー

@Shaggy一般的な説明を追加しました。明確かどうか教えてください(説明するのは難しいです)。コメント付きのコードは後で追加します
ルイスメンドー

2

05AB1E(レガシー)30 29 27バイト

α2‹i1q}1ݹ+v12y*3-tîÌy+}Ÿ²å

オンラインでお試しください!

コードの説明:

α2‹i1q}                     : if the absolute diff of the two number is 1 or 0 return 1
                          ²å: return that the second number is in
                         Ÿ  : range of {
       1Ý                   :  create [0, 1]
         ¹+                 :  add the first number to the elements
           v            }   :  map that list
            12y*3-tîÌy+     :  calculate the corresponding value where it's an adjacency
                                }

数学の説明:

私はこのゴルフを作るのに約5時間「無駄に」しました。要するに、入力の2Dグラフを作成し始めX、それらが互いに隣接している場所を描画しました。それからパターンを見つけました。OEISとビンゴで検索しました!私はそのシーケンスを見つけ、ウェブサイトで与えられた式を使用しました。


1

C(gcc)175 173バイト

バグをキャッチしてくれたPeter Taylorに感謝します。

おかげceilingcat-2バイトのにます。その〜演算子は、引き続きメインのブラインドスポットです。

c,r,C,L;y(a){a=a<L*2?L-a:a<L*3?-L:a<5*L?a-L*4:L;}z(a){L=ceil(sqrt(a/3.+.25)-.5);C=y(a-=3*L*~-L);L?L=y((L+a)%(L*6)):0;}f(a,b){z(a);c=C,r=L;z(b);a=a-b&&(abs(c-C)|abs(r-L))<2;}

オンラインでお試しください!

このアプローチは、2つのセルの行と列を見つけて比較することに焦点を当てています。隣接するセルの対応する座標が1を超えて異なることはできません。中心から外側に向かって移動すると、各レイヤーには前より6個多くのセルがあることがわかります。これは、各レイヤーLの最高の「インデックス」がフォーム6 *(L *(L-1)*(L-2)...)、またはC = 6 *(L 2 + L)/ 2ここで、Cは「グローバル」セル番号です。物事をシャッフルすると、L 2 + L-C / 3 = 0が得られます。これにより、高校の数学のフラッシュバックが得られます。それから、式ceil(sqrt(1/4 + C / 3)+ 0.5)を取得します。グローバルセルインデックスをそれに挿入すると、セルがどのレイヤーにあるかを受け取ります。

各レイヤーの最初のセルは当然、前のレイヤーの最高セルよりも1つ高いため、L start =(6 *(L-1)2 +(L-1))/ 2であり、3 *(L 2 -L)。それから、レイヤインデックスL index = C-L startを取得します。

次に、各レイヤーがそれぞれ長さLの6つのセクションで構成されていることを確認します。北東から始まり、反時計回りに進むと、最初の2つのセクション(1 <= L インデックス <= 2 * L) 、L-L インデックスから列を取得します。次のセクションL * 2 <L インデックス <= L * 3では、すべてのセルが単一の列-Lを共有しています。次の2つのセクションはL * 3 <L インデックス <= L * 5で、列はL インデックス -L * 4に基づいています。最後に、6番目のセクションはすべて列Lにセルを持っています。コード内のいくつかのバイトを保存します。

それでは、行はどうですか?コードを再利用するには、グリッドを回転させて、セル44が真上になるようにします。次に、列の場合と同じロジックを実行しますが、今回は結果を「行」と呼びます。もちろん、実際にグリッドを回すのではなく、1/6周回するだけです。


@PeterTaylor良いキャッチ、ありがとう!
ガストロプナー

1

Python 3、150バイト

def h(a,b):
 L=[];i=1
 while len(L)<a+b:r=sum((i*[1j**(k/3)]for k in range(4,16,2)),[]);r[0]+=1;L+=r;i+=1
 return.9<abs(sum(L[min(a,b):max(a,b)]))<1.1

私の解決策は、基本的に上記のルイスメンドーと同じ考え方に沿っています。より読みやすく書けば、コードは一目瞭然です。

def h(a,b):
    L=[]
    i=1
    while len(L)<a+b:
        l=sum((i*[1j**(k/3)]for k in range(4,16,2)),[])
        l[0]+=1
        L+=l
        i+=1
return .9<abs(sum(L[min(a,b):max(a,b)]))<1.1
  1. 関数hは次のことを行います。
  2. リストLには、各数値の(複雑な)位置が含まれます。
  3. i リング番号です。
  4. whileループでは、繰り返しごとに新しいリングが追加されます。必要なリングの数を計算する代わりに、a + bを含めるのに十分な長さになるまでリストを作成し続け、その後、いずれかのリングを含めるのに十分な長さになるようにします。
  5. 「リングリスト」lは、len(i)とステップベクトルの6つのリストの連結であり、ステップベクトルは1j **(2/3)の累乗です。範囲は0からではなく、4から始まり、グリッド全体が回転します。これにより、次のことが可能になります。
  6. l[0]+=1 1行目から次のリングへのステップである6行目。
  7. L+=l 完全なリストとリングリストを連結します。
  8. リストLにはステップベクトルのみが含まれており、位置を取得するために合計する必要があります。ここでのすてきな機能は、距離を取得するために、最小の数値から最大の数値までスライス合計できることです!丸め誤差のため、結果は正確に1にはならず、したがって.9 <... <1.1です。興味深いことに、h(0,0)空のリストの合計がゼロであるため、ゼロケースまたはh(0,1)が暗黙的に処理されます。a<b、つまり引数が昇順になると確信できたら、に置き換えL[min(a,b):max(a,b)]てさらに14バイトを削ることができますL[a:b]が、悲しいかな!

PS:これが古い挑戦だとは知りませんでしたが、数日前にトップに現れました。


これは素晴らしい答えです!遅い回答を心配する必要はありません。PPCGについては、実際には問題はありません。
Rɪᴋᴇʀ

0

Mathematica、111 105 104バイト

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&

説明:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&r入力を受け取り#、セル0までの距離(セル数)を計算する関数を定義します。これは、各距離/リングの最後のセルのパターンを活用することでこれを行います:0 = 3(0 ^ 2 + 0)、6 = 3(1 ^ 2 + 1)、18 = 3(2 ^ 2 + 2)、36 = 3(3 ^ 2 + 3)、...およびそのパターンの式を反転します。セル0の場合、実際には(1/2)+ i *(sqrt(3)/ 6)のフロアを取得し、コンポーネントごとに計算して0 + 0 * i = 0 になることに注意してください。

r定義され、r@#セルのためのリングである#(他の関数の定義の中で)。#+3r@#-3(r@#)^2&はコードに正確に表示されませんが、セルの番号を取得し、次の内部リングのセルの最大番号を減算するため、「現在のリングのどのセルがこれですか?」という質問に対する答えが得られます。たとえば、セル9はリング2の3番目のセルなので、r[9]2を#+3r@#-3(r@#)^2&[9]出力し、3を出力します。

上記の関数でできることは、「セル0、セル17、セル58」光線から問題のセルへの反時計回りの角度である極角を見つけるために使用することです。すべてのリングの最後のセルは常に角度Pi / 6であり、Pi /(3 * ring_number)の増分でリングを一周します。したがって、理論的には、Pi / 6 +(which_cell_of_the_current_ring)* Pi /(3 * ring_number)のようなものを計算する必要があります。ただし、画像の回転は何にも影響しないため、Pi / 6部分を破棄できます(6バイトを節約するため)。これを前の式と組み合わせて単純化すると、次のようになりますPi(#/(3r@#)+1-r@#)&

残念ながら、リング番号が0であるため、これはセル0に対して未定義です。そのため、これを回避する必要があります。自然な解決策は次のようなものですt=If[#==0,0,Pi(#/(3r@#)+1-r@#)]&。しかし、セル0の角度は気にせずr@#、繰り返されるので、実際にここでバイトを保存できますt=Limit[Pi(#/(3x)+1-x),x->r@#]&

リング番号と角度がわかったので、セル(中央)の位置を見つけて、隣接関係をテストできます。リングは六角形であるため、実際の位置を見つけるのは面倒ですが、リングを完全な円であるかのように単純に指定して、リング番号をセル0の中心までの距離として扱うことができます。閉じる。複素数の極形式を使用して、単純な関数で複素平面のこの近似位置を表すことができます。p = r@#*Exp[I*t@#] &;

複素平面上の2つの複素数間の距離は、それらの差の絶対値によって与えられます。その後、結果を丸めて近似からのエラーを処理し、これが1に等しいかどうかを確認できます。この作品には名前がありませんが、ですRound@Abs[p@#-p@#2]==1&


次のようなコードを貼り付け、Gear->「Evaluate cell」をクリックするか、Shift + EnterまたはテンキーEnterを押すと、Wolfram Cloudサンドボックスでオンライン試すことができます。

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&[24,45]

または、すべてのテストケースについて:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&//MapThread[#,Transpose[{{0,1},{7,18},{8,22},{24,45},{40,64},{64,65},{6,57},{29,90},{21,38},{38,60},{40,63},{41,39},{40,40}}]]&
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.