A caminhada de uma rainha através de uma espiral

13

Em um reino distante, uma rainha do xadrez faz uma caminhada diária por um caminho em espiral, numerado de 1 a n, não se importando em seguir a própria espiral, mas simplesmente fazendo os movimentos da rainha como faria em um tabuleiro de xadrez. A rainha é amada por seus súditos, e eles anotam cada quadrado que ela visita em seu caminho. Dado que a rainha pode começar sua caminhada em qualquer praça e terminar em qualquer praça, qual é a caminhada mais curta da rainha que ela pode fazer?

O desafio

Dada uma espiral de números inteiros em uma grade retangular, escreva uma função que retorne um dos caminhos mais curtos possíveis (contados pelo número de células viajadas) entre dois números nessa grade espiral usando os movimentos de uma rainha do xadrez.

Por exemplo, de 16para 25:

25 10 11 12 13
24  9  2  3 14
23  8  1  4 15
22  7  6  5 16
21 20 19 18 17

Alguns caminhos possíveis incluem 16, 4, 2, 10, 25e 16, 5, 1, 9, 25.

Regras

  • A entrada será dois inteiros positivos.
  • A saída será um caminho de números inteiros (incluindo os dois pontos de extremidade) através da espiral, usando apenas movimentos ortogonais e diagonais.
  • O comprimento de um caminho é contado pelo número de células percorridas.
  • Sua resposta pode ser um programa ou uma função.
  • Isso é código de golfe, então o menor número de bytes vence.

Como sempre, se o problema não estiver claro, entre em contato. Boa sorte e bom golfe!

Casos de teste

>>> queen_spiral(4, 5)
4, 5
>>> queen_spiral(13, 20)
13, 3, 1, 7, 20
>>> queen_spiral(14, 14)
14
>>> queen_spiral(10, 3)
10, 11, 3
>>> queen_spiral(16, 25)
16, 4, 2, 10, 25
>>> queen_spiral(80, 1)
80, 48, 24, 8, 1
Sherlock9
fonte
Relacionado .
Freira vazada
5
Você pode mencionar que está procurando o caminho mais curto pelo número de células percorridas (em oposição à distância euclidiana, por exemplo).
Martin Ender
11
Isso não faria mais sentido como uma "caminhada do rei"?
Jo King
11
@ JoKing Ah, agora que você mencionou, deve ser uma caminhada do rei. Pode ser um pouco tarde para mudar o título, no entanto.
Sherlock9

Respostas:

5

APL (Dyalog Unicode) , 59 57 bytes SBCS

{v⍳+\v[⍺],↓⍉↑(|⍴¨×)⊃⍵⍺-.⊃⊂v9 11∘○¨+\0,0j1*{⍵/⍨⌈⍵÷2}⍳⍺⌈⍵}

Experimente online!

-2 bytes graças a @ngn.

Função anônima que aceita dois pontos de extremidade como argumentos esquerdo e direito.

Ungolfed & como funciona

A rainha se move diagonalmente primeiro, portanto é suficiente pré-calcular as coordenadas de cada número até max(start,end) .

O algoritmo de geração de coordenadas é inspirado em várias respostas sobre o desafio relacionado , mas é um pouco diferente de qualquer uma das respostas existentes:

  • Dado o limite necessário de 10
  • Gere o intervalo baseado em 1 r=1 2 3 4 5 6 7 8 9 10
  • Tome o teto de metade de cada número n=1 1 2 2 3 3 4 4 5 5
  • Replicar cada item de rpor n.1 2 3 3 4 4 5 5 5 6 6 6 7 7 7 7 8 8 8 8 9 9 9 9 9 10 10 10 10 10
  • Pegue a soma acumulada de potência da unidade imaginária, com ponto inicial de 0. (essa parte é comum a várias soluções Python para o desafio vinculado)

Então, quando o vetor de coordenadas vestiver pronto, podemos facilmente converter entre o índice espiral e as coordenadas usando v[i]ev⍳coord (localizando o primeiro índice de coordin v).

 Define a function; ⍺=start, ⍵=end
f←{
   Construct a vector of spiral coordinates v
  v9 11∘○¨+\0,0j1*{⍵/⍨⌈⍵÷2}⍳⍺⌈⍵
                             ⍺⌈⍵   max of start, end
                                  range of 1 to that number
                   {⍵/⍨⌈⍵÷2}   for each number n of above, copy itself ceil(n/2) times
               0j1*   raise imaginary unit to the power of above
           +\0,       prepend 0 and cumulative sum
                      (gives vector of coordinates as complex numbers)
    9 11∘○¨   convert each complex number into (real, imag) pair
  v          assign it to v

   Extract start and end coordinates
  a w←(⍺⊃v)(⍵⊃v)

   Compute the path the Queen will take
  v⍳+\(a),↓⍉↑(|⍴¨×)w-a
                    w-a   coordinate difference (end-start)
              (|⍴¨×)      generate abs(x) copies of signum(x) for both x- and y-coords
                          e.g. 4 -> (1 1 1 1), ¯3 -> 1 ¯1 ¯1)
           ↓⍉↑   promote to matrix (with 0 padding), transpose and split again
                 (gives list of steps the Queen will take)
    +\(a),      prepend the starting point and cumulative sum
                 (gives the path as coordinates)
  v   index into the spiral vector (gives the spiral numbers at those coordinates)
}
Bubbler
fonte
(⍵⊃v)-⍺⊃v->⊃⍵⍺-.⊃⊂
ngn 18/01
(⍺⌷v)->v[⍺]
ngn 18/01
3

Mathematica 615 530 bytes

Isso constrói uma grade numérica, a converte em um gráfico e localiza um caminho mais curto entre os dois números que foram inseridos.


UnGolfed

numberSpiralé da Mathworld Prime Spiral . Cria um n por n Ulam Spiral (sem destacar os números primos).

findPathconverte a grade numérica em um gráfico. As arestas são movimentos válidos da rainha na grade numérica.


numberSpiral[n_Integer?OddQ]:= 
  Module[{a,i=(n+1)/2,j=(n+1)/2,cnt=1,dir=0,len,parity,vec={{1,0},{0,-1},{-1,0},{0,1}}},a=Table[j+n(i-1),{i,n},{j,n}];Do[Do[Do[a[[j,i]]=cnt++;{i,j}+=vec[[dir+1]],{k,len}];dir=Mod[dir+1,4],{parity,0,1}],{len,n-1}];a];  

findPath[v1_, v2_] := 
  Module[{f, z, k},
    (*f  creates edges between each number and its neighboring squares *)
    f[sp_,n_]:=n<->#&/@(sp[[Sequence@@#]]&/@(Position[sp,n][[1]]/.{r_,c_}:>Cases[{{r-1,c},{r+1,c},{r,c-1},{r,c+1},{r-1,c-1},{r-1,c+1},{r+1,c+1}, {r+1,c-1}},{x_,y_}/; 0<x<k&&0<y<k]));k=If[EvenQ[
     z=\[LeftCeiling]Sqrt[Sort[{v1, v2}][[-1]]]\[RightCeiling]],z+1,z];
    FindShortestPath[Graph[Sort/@Flatten[f[ns=numberSpiral[k],#]&/@Range[k^2]] //Union],v1,v2]]

Exemplos

findPath[4,5]
findPath[13,22]
findPath[16,25]
numberSpiral[5]//Grid

{4,5}

{13,3,1,7,22}

{16,4,1,9,25}

rede


O caminho mais curto de 80 para 1 contém 5, não 6, vértices.

findPath[80,1]
numberSpiral[9]//Grid

{80, 48, 24, 8, 1}

oitenta e uma grade


Golfe

u=Module;
w@n_:=u[{a,i=(n+1)/2,j=(n+1)/2,c=1,d=0,l,p,v={{1,0},{0,-1},{-1,0},{0,1}}},
a=Table[j+n(i-1),{i,n},{j,n}];
Do[Do[Do[a[[j,i]]=c++;{i,j}+=v[[d+1]],{k,l}];d=Mod[d+1,4],{p,0,1}],{l,n-1}];a];
h[v1_,v2_]:=u[{f,z},
s_~f~n_:=n<->#&/@(s[[Sequence@@#]]&/@(Position[s,n][[1]]/.{r_,c_}:> 
Cases[{{r-1,c},{r+1,c},{r,c-1},{r,c+1},{r-1,c-1},{r-1,c+1},{r+1,c+1},{r+1,c-1}},{x_,y_}/;0<x<k&&0<y<k]));
k=If[EvenQ[z=\[LeftCeiling]Sqrt[Sort[{v1,v2}][[-1]]]\[RightCeiling]],z+1,z];
FindShortestPath[g=Graph[Sort/@Flatten[f[ns=w@k,#]&/@Union@Range[k^2]]],v1,v2]]
DavidC
fonte
2

Scala (830 bytes)

Constrói a espiral em uma matriz quadrada 2D usando quatro funções mutuamente recursivas. Outra pesquisa recursiva para criar a lista de caminhos.

def P(s:Int,e:Int):List[Int]={
import scala.math._
type G=Array[Array[Int]]
type I=Int
type T=(I,I)
def S(z:I)={def U(g:G,x:I,y:I,c:I,r:I):Unit={for(i<-0 to r.min(y)){g(y-i)(x)=c+i}
if(r<=y)R(g,x,y-r,c+r,r)}
def R(g:G,x:I,y:I,c:I,r:I)={for(i<-0 to r){g(y)(x+i)=c+i}
D(g,x+r,y,c+r,r+1)}
def D(g:G,x:I,y:I,c:I,r:I)={for(i<-0 to r){g(y+i)(x)=c+i}
L(g,x,y+r,c+r,r)}
def L(g:G,x:I,y:I,c:I,r:I)={for(i<-0 to r){g(y)(x-i)=c+i}
U(g,x-r,y,c+r,r+1)}
val g=Array.ofDim[I](z,z)
U(g,z/2,z/2,1,1)
g}
def C(n:I,g:G):T={var(x,y)=(0,0)
for(i<-g.indices){val j=g(i).indexOf(n)
if(j>=0){x=j
y=i}}
(x,y)}
def N(n:Int)=if(n==0)0 else if(n<0)-1 else 1
def Q(a:T,b:T):List[T]={val u=N(b._1-a._1)
val v=N(b._2-a._2)
if(u==0&&v==0)b::Nil else a::Q((a._1+u,a._2+v),b)}
val z=ceil(sqrt(max(s,e))).toInt|1
val p=S(z)
Q(C(s,p),C(e,p)).map{case(x,y)=>p(y)(x)}}

Ungolfed

  import scala.math._
  type Grid=Array[Array[Int]]
  def spiral(size: Int) = {
    def up(grid:Grid, x: Int, y: Int, c: Int, r: Int): Unit = {
      for (i <- 0 to r.min(y)) {
        grid(y-i)(x) = c + i
      }
      if (r <= y)
        right(grid,x,y-r,c+r,r)
    }
    def right(grid:Grid, x: Int, y: Int, c: Int, r: Int) = {
      for (i <- 0 to r) {
        grid(y)(x+i) = c + i
      }
      down(grid,x+r,y,c+r,r+1)
    }
    def down(grid:Grid, x: Int, y: Int, c: Int, r: Int) = {
      for (i <- 0 to r) {
        grid(y+i)(x) = c + i
      }
      left(grid,x,y+r,c+r,r)
    }
    def left(grid:Grid, x: Int, y: Int, c: Int, r: Int) = {
      for (i <- 0 to r) {
        grid(y)(x-i) = c + i
      }
      up(grid,x-r,y,c+r,r+1)
    }
    val grid = Array.ofDim[Int](size,size)
    up(grid,size/2,size/2,1,1)
    grid
  }
  def findPath(start: Int, end: Int): List[Int] = {
    def findCoords(n: Int, grid: Grid): (Int, Int) = {
      var (x,y)=(0,0)
      for (i <- grid.indices) {
        val j = grid(i).indexOf(n)
        if (j >= 0) {
          x = j
          y = i
        }
      }
      (x,y)
    }
    def sign(n: Int) = if (n == 0) 0 else if (n < 0) -1 else 1
    def path(stc: (Int, Int), enc: (Int, Int)) : List[(Int, Int)] = {
      val dx = sign(enc._1 - stc._1)
      val dy = sign(enc._2 - stc._2)
      if (dx == 0 && dy == 0) {
        enc :: Nil
      } else {
        stc :: path((stc._1 + dx, stc._2 + dy), enc)
      }
    }
    val size = ceil(sqrt(max(start, end))).toInt | 1
    val spir = spiral(size)
    path(findCoords(start, spir),findCoords(end, spir)).
      map { case (x, y) => spir(y)(x) }
  }
Tim Robbins
fonte
2

Ruby, 262 218 216 bytes

Esta é uma porta da minha resposta Python . Sugestões de golfe são bem-vindas.

Edit: 45 bytes, graças a Jordan e suas sugestões de d=[0]*n=m*m;*e=c=0;*t=a, .rect, 0<=>xe x,y=(e[a]-g=e[b]).rect; t<<d[(g.real+x)*m+g.imag+y]. Outro byte de (x+y*1i)para (x+y.i).

->a,b{m=([a,b].max**0.5).to_i+1;d=[0]*n=m*m;*e=c=0;*t=a
n.times{|k|d[c.real*m+c.imag]=k+1;e<<c;c+=1i**((4*k+1)**0.5-1).to_i}
x,y=(e[a]-g=e[b]).rect
(x+=0<=>x;y+=0<=>y;t<<d[(g.real+x)*m+g.imag+y])while(x+y.i).abs>0
t}

Ungolfed:

def q(a,b)
  m = ([a,b].max**0.5).to_i+1
  n = m*m
  d = [0]*n
  c = 0
  *e = c   # same as e=[0]
  *t = a   # same as t=[a]

  (1..n).each do |k|
    d[c.real * m + c.imag] = k+1
    e << c
    c += 1i**((4*k+1)**0.5-1).to_i
  end

  x, y = (e[a] - g=e[b]).rect

  while (x+y.i).abs > 0 do
    if x<0
      x += 1
    elsif x>0
      x += -1
    end

    if y<0
      y += 1
    elsif y>0
      y -= 1
    end

    t << d[(g.real+x)*m+g.imag+y]
  end

  return t
end
Sherlock9
fonte
Você deve remover o q=da sua resposta, pois não está contando os bytes. c=0;e=[c];t=[a]pode ser reduzido para *e=c=0;*t=a. Você pode substituir z=e[a]-e[b];x,y=z.real,z.imagpor x,y=(e[a]-e[b]).recte x+=x<0?1:x>0?-1:0com x+=0<=>x(o mesmo para y). Eu acho que reduz para 229 bytes.
Jordânia
Se você alternar para uma matriz unidimensional, poderá salvar mais 6 bytes. Substitua a inicialização de dpor d=[0]*m*me substitua d[c.real][c.imag]por d[c.real*m+c.imag]e d[e[b].real+x][e[b].imag+y]por d[(e[b].real+x)*m+e[b].imag+y].
Jordan
Uma melhoria de 2 bytes em relação ao meu comentário anterior: t<<d[(e[b].real+x)*m+e[b].imag+y]pode ser reduzida para u,v=e[b].rect;t<<d[(u+x)*m+v+y].
Jordan
Mais dois bytes alterando d=[0]*m*mpara d=[0]*n=m*me (m*m).timespara n.times. Isso é 219.
Jordânia
Você pode salvar dois bytes adicionais alterando x,y=(e[a]-e[b]).rectpara x,y=(e[a]-g=e[b]).rect, excluindo u,v=e[b].recte alterando t<<d[(u+x)*m+v+y]para t<<d[(g.real+x)*g.imag+v+y](basicamente revertendo meu penúltimo comentário).
Jordan
1

Python 3, 316 bytes

Essa resposta examina as coordenadas da espiral ae bda espiral (usando números complexos) e adiciona os movimentos diagonais primeiro, depois os movimentos ortogonais.

def q(a,b):
 m=int(max(a,b)**.5)+1;d=[];c=0;e=[c];t=[a]
 for i in range(m):d+=[[0]*m]
 for k in range(m*m):d[int(c.real)][int(c.imag)]=k+1;e+=[c];c+=1j**int((4*k+1)**.5-1)
 z=e[a]-e[b];x,y=int(z.real),int(z.imag)
 while abs(x+y*1j):x+=(x<0)^-(x>0);y+=(y<0)^-(y>0);t+=[d[int(e[b].real)+x][int(e[b].imag)+y]]
 return t

Ungolfed:

def queen_spiral(a,b):
    square_size = int(max(a,b)**.5)+1
    complex_to_spiral = []
    complex = 0
    spiral_to_complex = [c] # add 0 first, so that it's 1-indexed later
    result = [a]

    for i in range(square_size):
        complex_to_spiral.append([0]*square_size) # the rows of the spiral

    for k in range(square_size**2):
        row = int(complex.real)
        column = int(complex.imag)
        complex_to_spiral[row][column] = k+1 # 1-indexing

        spiral_to_complex.append(complex)

        quarter_turns = int((4*k+1)**.5-1)
        complex += 1j**quarter_turns

    z = spiral_to_complex[a] - spiral_to_complex[b]
    v = spiral_to_complex[b]
    x, y = int(z.real), int(z.imag)
    r, s = int(v.real), int(v.imag)

    while abs(x+y*1j):
        if x < 0:
            x += 1
        elif x > 0:
            x += -1
        # else x == 0, do nothing
        if y < 0:
            y += 1
        elif y > 0:
            y += -1

        vertex = complex_to_spiral[r+x][s+y]
        result.append(vertex)
    return result
Sherlock9
fonte