Прогулка королевы по спирали

13

В далеком королевстве шахматная королева совершает ежедневную прогулку по спиральной дорожке, пронумерованной от 1 до n, не заботясь о том, чтобы следовать самой спирали, а просто делает движения королевы, как она делает это на шахматной доске. Королева любима своими подданными, и они записывают каждый квадрат, который она посещает на своем пути. Учитывая, что королева может начать свою прогулку на любом квадрате и заканчивать его на любом квадрате, какой самый короткий путь королевы она может пройти?

Соревнование

Получив спираль целых чисел на прямоугольной сетке, напишите функцию, которая возвращает один из кратчайших возможных путей (подсчитанных по количеству пройденных ячеек) между двумя числами в этой спиральной сетке, используя ходы шахматной королевы.

Например, от 16до 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

Некоторые возможные пути включают в себя 16, 4, 2, 10, 25и 16, 5, 1, 9, 25.

правила

  • На входе будут любые два натуральных числа.
  • Выходными данными будет путь целых чисел (включая обе конечные точки) по спирали с использованием только ортогональных и диагональных перемещений.
  • Длина пути подсчитывается по количеству пройденных клеток.
  • Ваш ответ может быть программой или функцией.
  • Это код гольф, поэтому выигрывает наименьшее количество байтов.

Как всегда, если проблема неясна, пожалуйста, дайте мне знать. Удачи и хорошего гольфа!

Контрольные примеры

>>> 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
источник
Относящиеся .
Утренняя монахиня
5
Вы можете упомянуть, что вы ищете кратчайший путь по количеству пройденных клеток (скажем, в отличие от евклидова расстояния).
Мартин Эндер
1
Разве это не имеет больше смысла как "прогулка короля"?
Джо Кинг
1
@JoKing Ах, теперь, когда вы упомянули об этом, это должна быть прогулка короля. Однако, возможно, будет немного поздно менять название.
Шерлок9

Ответы:

5

APL (Dyalog Unicode) , 59 57 байтов SBCS

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

Попробуйте онлайн!

-2 байта благодаря @ngn.

Анонимная функция, которая принимает две конечные точки в качестве левого и правого аргументов.

Ungolfed & как это работает

Королева движется по диагонали в первую очередь, поэтому достаточно предварительно рассчитать координаты каждого числа до max(start,end).

Алгоритм генерации координат основан на нескольких ответах на связанный вызов , но немного отличается от любого из существующих ответов:

  • Учитывая необходимую границу 10
  • Создайте диапазон на основе 1 r=1 2 3 4 5 6 7 8 9 10
  • Возьмите потолок половины каждого числа n=1 1 2 2 3 3 4 4 5 5
  • Репликация каждый элемент rпо 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
  • Возьмите кумулятивную сумму мощности воображаемой единицы с начальной точкой 0. (эта часть является общей для различных решений Python для связанной задачи)

Затем, как только вектор координат vбудет готов, мы можем легко конвертировать между спиральным индексом и координатами, используя v[i]и v⍳coord(находя первый индекс coordв 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)
}
фонтанчик для питья
источник
(⍵⊃v)-⍺⊃v->⊃⍵⍺-.⊃⊂
NGN
(⍺⌷v)->v[⍺]
NGN
3

Mathematica 615 530 байт

Это создает сетку чисел, преобразует ее в график, а затем находит кратчайший путь между двумя числами, которые были введены.


UnGolfed

numberSpiralэто из Mathworld Prime Spiral . Создает спираль Улама (без выделения простых чисел).

findPathпреобразует сетку чисел в график. Края являются действительными ходами ферзя на числовой сетке.


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]]

Примеры

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

{4,5}

{} 13,3,1,7,22

{} 16,4,1,9,25

сетка


Кратчайший путь из 80 в 1 содержит 5, а не 6 вершин.

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

{80, 48, 24, 8, 1}

восемьдесят одна сетка


Golfed

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
источник
2

Scala (830 байт)

Строит спираль в квадратном двумерном массиве, используя четыре взаимно рекурсивных функции. Еще один рекурсивный поиск для построения списка путей.

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) }
  }
Тим Роббинс
источник
2

Рубин, 262 218 216 байт

Это порт моего ответа Python . Предложения по игре в гольф приветствуются.

Edit: 45 байт благодаря Иордану и их предложениям d=[0]*n=m*m;*e=c=0;*t=a, .rect, 0<=>xи x,y=(e[a]-g=e[b]).rect; t<<d[(g.real+x)*m+g.imag+y]. Еще один байт от (x+y*1i)до (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
источник
Вы должны удалить q=из своего ответа, так как вы не учитываете его байты. c=0;e=[c];t=[a]можно сократить до *e=c=0;*t=a. Вы можете заменить z=e[a]-e[b];x,y=z.real,z.imagна x,y=(e[a]-e[b]).rectи x+=x<0?1:x>0?-1:0с x+=0<=>x(то же самое для y). Я думаю, что это уменьшается до 229 байтов.
Джордан
Если вы переключитесь на одномерный массив, вы можете сохранить еще 6 байтов. Замените инициализацию dс d=[0]*m*m, затем замените d[c.real][c.imag]на d[c.real*m+c.imag]и d[e[b].real+x][e[b].imag+y]с d[(e[b].real+x)*m+e[b].imag+y].
Джордан
2-байтовое улучшение по сравнению с моим предыдущим комментарием: t<<d[(e[b].real+x)*m+e[b].imag+y]можно сократить до u,v=e[b].rect;t<<d[(u+x)*m+v+y].
Джордан
Еще два байта, изменяя d=[0]*m*mв d=[0]*n=m*mи (m*m).timesк n.times. Это 219.
Джордан
Вы можете сохранить два дополнительных байта, изменив x,y=(e[a]-e[b]).rectна x,y=(e[a]-g=e[b]).rect, удалив u,v=e[b].rectи изменив t<<d[(u+x)*m+v+y]на t<<d[(g.real+x)*g.imag+v+y](в основном возвращая мой второй к последнему комментарию).
Джордан
1

Python 3, 316 байт

Этот ответ смотрит на координаты aи bна спирали (используя комплексные числа) и добавляет сначала диагональные движения, затем ортогональные движения.

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
источник