La marche d'une reine à travers une spirale

13

Dans un royaume lointain, une reine d'échecs fait une promenade quotidienne à travers un chemin en spirale, numéroté de 1 à n, ne se souciant pas de suivre la spirale elle-même, mais faisant simplement les mouvements de la reine comme elle le ferait sur un échiquier. La reine est aimée de ses sujets, et ils notent chaque case qu'elle visite sur son chemin. Étant donné que la reine peut commencer sa marche sur n'importe quelle case et la terminer sur n'importe quelle case, quelle est la marche de la reine la plus courte qu'elle peut faire?

Le défi

Étant donné une spirale d'entiers sur une grille rectangulaire, écrivez une fonction qui renvoie l'une des plus courtes chemins possibles (compté par le nombre de cellules parcourues) entre deux nombres sur cette grille en spirale en utilisant les mouvements d'une reine d'échecs.

Par exemple, de 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

Certains chemins possibles incluent 16, 4, 2, 10, 25et 16, 5, 1, 9, 25.

Règles

  • L'entrée sera constituée de deux entiers positifs.
  • La sortie sera un chemin d'entiers (y compris les deux extrémités) à travers la spirale en utilisant uniquement des mouvements orthogonaux et diagonaux.
  • La longueur d'un chemin est comptée par le nombre de cellules parcourues.
  • Votre réponse peut être un programme ou une fonction.
  • C'est le golf de code, donc le plus petit nombre d'octets gagne.

Comme toujours, si le problème n'est pas clair, faites-le moi savoir. Bonne chance et bon golf!

Cas de test

>>> 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
la source
Connexes .
Leaky Nun
5
Vous voudrez peut-être mentionner que vous recherchez le chemin le plus court en fonction du nombre de cellules parcourues (par opposition à la distance euclidienne, par exemple).
Martin Ender
1
Cela ne serait-il pas plus sensé comme une «marche du roi»?
Jo King
1
@JoKing Ah, maintenant que vous en parlez, ce devrait être la marche d'un roi. Il pourrait cependant être un peu tard pour changer le titre.
Sherlock9

Réponses:

5

APL (Dyalog Unicode) , 59 57 octets SBCS

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

Essayez-le en ligne!

-2 octets grâce à @ngn.

Fonction anonyme qui accepte deux points de terminaison comme arguments gauche et droit.

Non golfé et comment cela fonctionne

La reine se déplace en premier en diagonale, il suffit donc de pré-calculer les coordonnées de chaque nombre jusqu'à max(start,end).

L'algorithme générateur de coordonnées est inspiré de plusieurs réponses sur le défi associé , mais est légèrement différent de l'une des réponses existantes:

  • Étant donné la limite nécessaire de 10
  • Générer la plage 1 r=1 2 3 4 5 6 7 8 9 10
  • Prenez le plafond de la moitié de chaque nombre n=1 1 2 2 3 3 4 4 5 5
  • Répliquez chaque élément de rpar 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
  • Prenez la somme cumulée de la puissance de l'unité imaginaire, avec un point de départ de 0. (cette partie est commune à diverses solutions Python au défi lié)

Ensuite, une fois que le vecteur de coordonnées vest prêt, nous pouvons facilement convertir entre l'index en spirale et les coordonnées à l'aide de v[i]et v⍳coord(trouver le premier indice 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
la source
(⍵⊃v)-⍺⊃v->⊃⍵⍺-.⊃⊂
ngn
(⍺⌷v)->v[⍺]
ngn
3

Mathematica 615 530 octets

Cela construit une grille numérique, la convertit en graphique, puis trouve le chemin le plus court entre les deux nombres qui ont été saisis.


UnGolfed

numberSpiralest de Mathworld Prime Spiral . Il crée une spirale Ulam n par n (sans mettre en évidence les nombres premiers).

findPathconvertit la grille numérique en un graphique. Les bords sont des mouvements de reine valides sur la grille numérique.


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

Exemples

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

{4,5}

{13,3,1,7,22}

{16,4,1,9,25}

la grille


Le chemin le plus court de 80 à 1 contient 5, et non 6, sommets.

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

{80, 48, 24, 8, 1}

quatre-vingt une grille


Golfé

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
la source
2

Scala (830 octets)

Construit la spirale dans un tableau 2D carré en utilisant quatre fonctions mutuellement récursives. Une autre recherche récursive pour construire la liste des chemins.

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

Non golfé

  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
la source
2

Ruby, 262 218 216 octets

Ceci est un port de ma réponse Python . Suggestions de golf bienvenues.

Edit: 45 octets grâce à la Jordanie et leurs suggestions de d=[0]*n=m*m;*e=c=0;*t=a, .rect, 0<=>xet x,y=(e[a]-g=e[b]).rect; t<<d[(g.real+x)*m+g.imag+y]. Un autre octet de (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}

Non golfé:

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
la source
Vous devez supprimer le q=de votre réponse car vous ne comptez pas ses octets. c=0;e=[c];t=[a]peut être raccourci *e=c=0;*t=a. Vous pouvez remplacer z=e[a]-e[b];x,y=z.real,z.imagpar x,y=(e[a]-e[b]).rectet x+=x<0?1:x>0?-1:0par x+=0<=>x(idem pour y). Je pense que cela ramène à 229 octets.
Jordan
Si vous passez à un tableau unidimensionnel, vous pouvez économiser 6 octets supplémentaires. Remplacez l'initialisation de dpar d=[0]*m*m, puis remplacez d[c.real][c.imag]par d[c.real*m+c.imag]et d[e[b].real+x][e[b].imag+y]par d[(e[b].real+x)*m+e[b].imag+y].
Jordan
Une amélioration de 2 octets par rapport à mon commentaire précédent: t<<d[(e[b].real+x)*m+e[b].imag+y]peut être raccourcie u,v=e[b].rect;t<<d[(u+x)*m+v+y].
Jordan
Deux octets supplémentaires en changeant d=[0]*m*mvers d=[0]*n=m*met (m*m).timesvers n.times. C'est 219.
Jordan
Vous pouvez enregistrer deux octets supplémentaires en changeant x,y=(e[a]-e[b]).recten x,y=(e[a]-g=e[b]).rect, en supprimant u,v=e[b].rectet en changeant t<<d[(u+x)*m+v+y]en t<<d[(g.real+x)*g.imag+v+y](en revenant essentiellement à mon avant-dernier commentaire).
Jordan
1

Python 3, 316 octets

Cette réponse examine les coordonnées de aet bsur la spirale (en utilisant des nombres complexes) et ajoute d'abord les mouvements diagonaux, puis les mouvements orthogonaux.

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

Non golfé:

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
la source