Looping dalam spiral


154

Seorang teman membutuhkan sebuah algoritma yang akan membiarkannya mengulangi elemen-elemen matriks NxM (N dan M aneh). Saya datang dengan solusi, tetapi saya ingin melihat apakah rekan-rekan SO saya dapat menemukan solusi yang lebih baik.

Saya memposting solusi saya sebagai jawaban untuk pertanyaan ini.

Contoh Output:

Untuk matriks 3x3, output harus:

(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (0, -1) (1, -1 )

3x3 matriks

Selanjutnya, algoritma harus mendukung matriks non-square, jadi misalnya untuk matriks 5x3, output harus:

(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (0, -1) (1, -1 ) (2, -1) (2, 0) (2, 1) (-2, 1) (-2, 0) (-2, -1)

5x3 matriks


Bisakah Anda menjelaskan apa yang Anda inginkan untuk matriks non-persegi? Solusi Anda memiliki "lompatan" dari (2,1) ke (-2,1) - apakah ini dimaksudkan? [Misalnya untuk matriks 7x3, ia akan memiliki dua "lompatan" lagi, dan untuk matriks (2k + 1) x3 akan memiliki lompatan 2k-3?]
ShreevatsaR

3
Ya, lompatannya disengaja. Saya telah memperbarui pertanyaan dengan gambar matriks 5x3. Seperti yang dapat Anda lihat dari gambar, kami melewatkan baris atas dan bawah.
Can Berk Güder

Oke, lalu kode Anda sendiri tampak terbersih. Dan meskipun ini offtopic: bagaimana Anda menghasilkan gambar-gambar itu? :)
ShreevatsaR

=)) Saya tidak menghasilkan mereka. Sebenarnya, cara saya menciptakan mereka sangat bodoh. Saya membuat tabel di OO.org Calc, mengambil tangkapan layar, dan mengedit tangkapan layar di GIMP. =))
Can Berk Güder

1
@Ying: Saya tidak benar-benar tahu mengapa teman saya membutuhkan ini, tetapi dia berkata dia ingin mendukung anggota matriks lebih dekat ke pusat dalam algoritma pencarian.
Can Berk Güder

Jawaban:


63

Inilah solusi saya (dengan Python):

def spiral(X, Y):
    x = y = 0
    dx = 0
    dy = -1
    for i in range(max(X, Y)**2):
        if (-X/2 < x <= X/2) and (-Y/2 < y <= Y/2):
            print (x, y)
            # DO STUFF...
        if x == y or (x < 0 and x == -y) or (x > 0 and x == 1-y):
            dx, dy = -dy, dx
        x, y = x+dx, y+dy

1
Ini adalah cara terbaik untuk menulisnya, sejauh yang saya bisa lihat. Satu-satunya perbaikan yang mungkin dilakukan adalah menjadikannya O (MN) alih-alih O (maks (M, N) ^ 2) dengan secara langsung melewati mereka (x, y) yang tidak akan dicetak, tetapi itu akan membuat kode sedikit lebih jelek.
ShreevatsaR

Saya mengoptimalkan solusi saya dan cukup dekat dengan apa yang sudah Anda dapatkan. Ini adalah solusi yang cukup bagus menurut saya. Selain saran ShreevatsaR, dan hal-hal seperti tidak menghitung x / 2 dan y / 2 setiap iterasi, tidak ada terlalu banyak untuk diperbaiki kecuali gaya.
Triptych

Ada solusi untuk matlab ?!
Sam

Apakah ini memberikan koherensi cache yang baik untuk mengakses data buffer gambar? (Ada begitu banyak jawaban di sini, tetapi tidak banyak info mengenai yang bekerja paling baik untuk operasi gambar berkinerja tinggi)
ideasman42

@ ideasman42 - itu tidak berlaku, karena hasilnya selalu pola koordinat spiral yang sama. Apakah pola spiral koheren cache saya kira tergantung pada implementasi buffer gambar. (Dugaan saya adalah itu akan merosot cache lebih dari cara lain berjalan gambar, seperti akan baris demi baris secara berurutan). Tetapi pilihan algoritma untuk menghasilkan koordinat ini mungkin tidak akan memengaruhi cache.
Raptormeat

31

C ++ siapa saja? Terjemahan cepat dari python, diposting untuk kelengkapan

void Spiral( int X, int Y){
    int x,y,dx,dy;
    x = y = dx =0;
    dy = -1;
    int t = std::max(X,Y);
    int maxI = t*t;
    for(int i =0; i < maxI; i++){
        if ((-X/2 <= x) && (x <= X/2) && (-Y/2 <= y) && (y <= Y/2)){
            // DO STUFF...
        }
        if( (x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y))){
            t = dx;
            dx = -dy;
            dy = t;
        }
        x += dx;
        y += dy;
    }
}

Anda juga dapat menggunakan s dan ds seperti yang saya lakukan untuk mendeteksi sudut-sudut yang menghilangkan kondisi besar jika
John La Rooy

1
Sunting untuk posting ini disarankan di sini . Meskipun hasil edit ditolak karena itu mengubah makna posting Anda, Anda mungkin ingin mempertimbangkan untuk memasukkan perubahan yang disarankan jika masuk akal untuk melakukannya.
Robert Harvey

19
let x = 0
let y = 0
let d = 1
let m = 1

while true
  while 2 * x * d < m
    print(x, y)
    x = x + d
  while 2 * y * d < m
    print(x, y)
    y = y + d
  d = -1 * d
  m = m + 1

Ada banyak solusi yang diusulkan untuk masalah ini yang ditulis dalam berbagai bahasa pemrograman namun mereka semua tampaknya berasal dari pendekatan yang berbelit-belit. Saya akan mempertimbangkan masalah yang lebih umum dari komputasi spiral yang dapat diekspresikan secara ringkas menggunakan induksi.

Kasing dasar: Mulai dari (0, 0), bergerak maju 1 kotak, belok kiri, maju 1 kotak, belok kiri. Langkah induktif: Bergerak maju n + 1 kotak, belok kiri, bergerak maju n + 1 kotak, belok kiri.

Keanggunan matematis untuk mengungkapkan masalah ini sangat menyarankan harus ada algoritma sederhana untuk menghitung solusinya. Dengan mengingat abstraksi, saya memilih untuk tidak mengimplementasikan algoritma dalam bahasa pemrograman tertentu tetapi lebih sebagai pseudo-code.

Pertama saya akan mempertimbangkan algoritma untuk menghitung hanya 2 iterasi spiral menggunakan 4 pasang while loop. Struktur masing-masing pasangan serupa, namun berbeda dalam dirinya sendiri. Ini mungkin tampak gila pada awalnya (beberapa loop hanya dieksekusi sekali) tetapi langkah demi langkah saya akan melakukan transformasi sampai kita tiba di 4 pasang loop yang identik dan karenanya dapat diganti dengan satu pasang ditempatkan di dalam loop lain. Ini akan memberi kami solusi umum komputasi dan iterasi tanpa menggunakan persyaratan apa pun.

let x = 0
let y = 0

//RIGHT, UP
while x < 1
  print(x, y)
  x = x + 1
while y < 1
  print(x, y)
  y = y + 1

//LEFT, LEFT, DOWN, DOWN
while x > -1
  print(x, y)
  x = x - 1
while y > -1
  print(x, y)
  y = y - 1

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x < 2
  print(x, y)
  x = x + 1
while y < 2
  print(x, y)
  y = y + 1

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x > -2
  print(x, y)
  x = x - 1
while y > -2
  print(x, y)
  y = y - 1

Transformasi pertama yang akan kita buat adalah pengenalan variabel baru d, untuk arah, yang memiliki nilai +1 atau -1. Arah beralih setelah setiap pasangan loop. Karena kita mengetahui nilai d pada semua titik, kita dapat mengalikan setiap sisi dari setiap ketidaksetaraan dengan itu, menyesuaikan arah ketidaksetaraan sesuai dan menyederhanakan setiap perkalian dari d dengan konstanta ke konstanta lain. Ini meninggalkan kita dengan yang berikut ini.

let x = 0
let y = 0
let d = 1

//RIGHT, UP
while x * d < 1
  print(x, y)
  x = x + d
while y * d < 1
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, DOWN, DOWN
while x * d < 1
  print(x, y)
  x = x + d
while y * d < 1
  print(x, y)
  y = y + d
d = -1 * d

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x * d < 2
  print(x, y)
  x = x + d
while y * d < 2
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x * d < 2
  print(x, y)
  x = x + d
while y * d < 2
  print(x, y)
  y = y + d

Sekarang kita perhatikan bahwa x * d dan RHS adalah bilangan bulat sehingga kita dapat mengurangi nilai riil antara 0 dan 1 dari RHS tanpa memengaruhi hasil ketidaksetaraan. Kami memilih untuk mengurangi 0,5 dari ketidaksetaraan setiap pasangan sementara loop untuk membangun lebih banyak pola.

let x = 0
let y = 0
let d = 1

//RIGHT, UP
while x * d < 0.5
  print(x, y)
  x = x + d
while y * d < 0.5
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, DOWN, DOWN
while x * d < 1
  print(x, y)
  x = x + d
while y * d < 1
  print(x, y)
  y = y + d
d = -1 * d

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x * d < 1.5
  print(x, y)
  x = x + d
while y * d < 1.5
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x * d < 2
  print(x, y)
  x = x + d
while y * d < 2
  print(x, y)
  y = y + d

Kita sekarang dapat memperkenalkan variabel lain m untuk jumlah langkah yang kita ambil pada setiap pasangan while loop.

let x = 0
let y = 0
let d = 1
let m = 0.5

//RIGHT, UP
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d
d = -1 * d
m = m + 0.5

//LEFT, LEFT, DOWN, DOWN
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d
d = -1 * d
m = m + 0.5

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d
d = -1 * d
m = m + 0.5

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d

Akhirnya, kita melihat bahwa struktur setiap pasangan while loop identik dan dapat direduksi menjadi satu loop yang ditempatkan di dalam loop lain. Juga, untuk menghindari penggunaan bilangan bernilai nyata, saya telah mengalikan nilai awal m; nilai m bertambah dengan; dan kedua sisi masing-masing ketidaksetaraan oleh 2.

Ini mengarah ke solusi yang ditunjukkan di awal jawaban ini.


1
Dalam kondisi apa solusi akhir Anda akan berakhir?
Merlyn Morgan-Graham

1
Apa aplikasi pencetakan pola semacam itu?
Ashish Shukla

1
@ MerlynMorgan-Graham Ini berakhir saat komputer kehabisan memori atau daya.
Mike

Tampaknya keanggunan solusi itu berasal dari mengabaikan kendala waktu dan memori. Saya merekomendasikan penambahan kondisi penghentian secara elegan (jika mungkin). Saya juga merekomendasikan untuk memindahkannya ke atas jawaban, dan menunjukkan derivasi di bawahnya.
Merlyn Morgan-Graham

1
Sementara pertanyaan aslinya adalah tentang matriks NxM, ini sebenarnya jawaban yang sangat berguna jika Anda perlu keluar spiral terus-menerus sampai Anda menemukan sesuatu (yaitu kemudian istirahat atau kembali). Tentu saja, seperti komentar lain yang disebutkan, Anda perlu mendefinisikan kondisi pemutusan itu atau akan berjalan selamanya.
cclogg

16

Inilah solusi O (1) untuk menemukan posisi dalam spiral kuadrat: Fiddle

function spiral(n) {
    // given n an index in the squared spiral
    // p the sum of point in inner square
    // a the position on the current square
    // n = p + a

    var r = Math.floor((Math.sqrt(n + 1) - 1) / 2) + 1;

    // compute radius : inverse arithmetic sum of 8+16+24+...=
    var p = (8 * r * (r - 1)) / 2;
    // compute total point on radius -1 : arithmetic sum of 8+16+24+...

    var en = r * 2;
    // points by face

    var a = (1 + n - p) % (r * 8);
    // compute de position and shift it so the first is (-r,-r) but (-r+1,-r)
    // so square can connect

    var pos = [0, 0, r];
    switch (Math.floor(a / (r * 2))) {
        // find the face : 0 top, 1 right, 2, bottom, 3 left
        case 0:
            {
                pos[0] = a - r;
                pos[1] = -r;
            }
            break;
        case 1:
            {
                pos[0] = r;
                pos[1] = (a % en) - r;

            }
            break;
        case 2:
            {
                pos[0] = r - (a % en);
                pos[1] = r;
            }
            break;
        case 3:
            {
                pos[0] = -r;
                pos[1] = r - (a % en);
            }
            break;
    }
    console.log("n : ", n, " r : ", r, " p : ", p, " a : ", a, "  -->  ", pos);
    return pos;
}

3
Untuk memulai dari tengah tambahkan dua baris. if (n === 0) return [0, 0, r]; --n;Lihat Fiddle: jsfiddle.net/Wishmesh/nwd9gt1s/2
Maris B.

15

Saya suka generator python.

def spiral(N, M):
    x,y = 0,0   
    dx, dy = 0, -1

    for dumb in xrange(N*M):
        if abs(x) == abs(y) and [dx,dy] != [1,0] or x>0 and y == 1-x:  
            dx, dy = -dy, dx            # corner, change direction

        if abs(x)>N/2 or abs(y)>M/2:    # non-square
            dx, dy = -dy, dx            # change direction
            x, y = -y+dx, x+dy          # jump

        yield x, y
        x, y = x+dx, y+dy

Pengujian dengan:

print 'Spiral 3x3:'
for a,b in spiral(3,3):
    print (a,b),

print '\n\nSpiral 5x3:'
for a,b in spiral(5,3):
    print (a,b),

Anda mendapatkan:

Spiral 3x3:
(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1) 

Spiral 5x3:
(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1) (2, -1) (2, 0) (2, 1) (-2, 1) (-2, 0) (-2, -1)

8

Upaya "spiral golf Code" Java, berdasarkan pada varian C ++.

public static void Spiral(int X, int Y) {
    int x=0, y=0, dx = 0, dy = -1;
    int t = Math.max(X,Y);
    int maxI = t*t;

    for (int i=0; i < maxI; i++){
        if ((-X/2 <= x) && (x <= X/2) && (-Y/2 <= y) && (y <= Y/2)) {
            System.out.println(x+","+y);
            //DO STUFF
        }

        if( (x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y))) {
            t=dx; dx=-dy; dy=t;
        }   
        x+=dx; y+=dy;
    }
}

7

Berikut adalah solusi C ++ yang menunjukkan bahwa Anda dapat menghitung koordinat berikutnya (x, y) secara langsung dan mudah dari yang sebelumnya - tidak perlu untuk melacak arah, jari-jari saat ini, atau yang lainnya:

void spiral(const int M, const int N)
{
    // Generate an Ulam spiral centered at (0, 0).
    int x = 0;
    int y = 0;

    int end = max(N, M) * max(N, M);
    for(int i = 0; i < end; ++i)
    {
        // Translate coordinates and mask them out.
        int xp = x + N / 2;
        int yp = y + M / 2;
        if(xp >= 0 && xp < N && yp >= 0 && yp < M)
            cout << xp << '\t' << yp << '\n';

        // No need to track (dx, dy) as the other examples do:
        if(abs(x) <= abs(y) && (x != y || x >= 0))
            x += ((y >= 0) ? 1 : -1);
        else
            y += ((x >= 0) ? -1 : 1);
    }
}

Jika semua yang Anda coba lakukan adalah menghasilkan titik N pertama di spiral (tanpa kendala masalah asli untuk menutupi wilayah N x M), kode menjadi sangat sederhana:

void spiral(const int N)
{
    int x = 0;
    int y = 0;
    for(int i = 0; i < N; ++i)
    {
        cout << x << '\t' << y << '\n';
        if(abs(x) <= abs(y) && (x != y || x >= 0))
            x += ((y >= 0) ? 1 : -1);
        else
            y += ((x >= 0) ? -1 : 1);
    }
}

Triknya adalah Anda dapat membandingkan x dan y untuk menentukan di sisi mana Anda berada, dan itu memberi tahu Anda arah bergerak yang mana.


5

TDD, di Jawa.

SpiralTest.java:

import java.awt.Point;
import java.util.List;

import junit.framework.TestCase;

public class SpiralTest extends TestCase {

    public void test3x3() throws Exception {
        assertEquals("(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1)", strung(new Spiral(3, 3).spiral()));
    }

    public void test5x3() throws Exception {
        assertEquals("(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1) (2, -1) (2, 0) (2, 1) (-2, 1) (-2, 0) (-2, -1)",
                strung(new Spiral(5, 3).spiral()));
    }

    private String strung(List<Point> points) {
        StringBuffer sb = new StringBuffer();
        for (Point point : points)
            sb.append(strung(point));
        return sb.toString().trim();
    }

    private String strung(Point point) {
        return String.format("(%s, %s) ", point.x, point.y);
    }

}

Spiral.java:

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class Spiral {
    private enum Direction {
    E(1, 0) {Direction next() {return N;}},
    N(0, 1) {Direction next() {return W;}},
    W(-1, 0) {Direction next() {return S;}},
    S(0, -1) {Direction next() {return E;}},;

        private int dx;
        private int dy;

        Point advance(Point point) {
            return new Point(point.x + dx, point.y + dy);
        }

        abstract Direction next();

        Direction(int dx, int dy) {
            this.dx = dx;
            this.dy = dy;
        }
    };
    private final static Point ORIGIN = new Point(0, 0);
    private final int   width;
    private final int   height;
    private Point       point;
    private Direction   direction   = Direction.E;
    private List<Point> list = new ArrayList<Point>();

    public Spiral(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public List<Point> spiral() {
        point = ORIGIN;
        int steps = 1;
        while (list.size() < width * height) {
            advance(steps);
            advance(steps);
            steps++;
        }
        return list;
    }

    private void advance(int n) {
        for (int i = 0; i < n; ++i) {
            if (inBounds(point))
                list.add(point);
            point = direction.advance(point);
        }
        direction = direction.next();
    }

    private boolean inBounds(Point p) {
        return between(-width / 2, width / 2, p.x) && between(-height / 2, height / 2, p.y);
    }

    private static boolean between(int low, int high, int n) {
        return low <= n && n <= high;
    }
}

@ leppie: Mungkin tidak - tentu saja tidak cukup pendek - tapi saya pikir ini adalah demonstrasi TDD yang bagus, dan kode yang benar-benar bersih, mudah dipahami, dan benar. Saya akan meninggalkannya.
Carl Manaster

4

Ini solusi saya (dalam Ruby)

def spiral(xDim, yDim)
   sx = xDim / 2
   sy = yDim / 2

   cx = cy = 0
   direction = distance = 1

   yield(cx,cy)
   while(cx.abs <= sx || cy.abs <= sy)
      distance.times { cx += direction; yield(cx,cy) if(cx.abs <= sx && cy.abs <= sy); } 
      distance.times { cy += direction; yield(cx,cy) if(cx.abs <= sx && cy.abs <= sy); } 
      distance += 1
      direction *= -1
   end
end

spiral(5,3) { |x,y|
   print "(#{x},#{y}),"
}

Masih O (maks (n, m) ^ 2), tapi gaya yang bagus.
Triptych

1
direction = -direction bukan direction * = - 1? jika Anda bermain golf d = -d lebih pendek dari d * = - 1 juga
John La Rooy

3

Haskell, pilihlah:

spiral x y = (0, 0) : concatMap ring [1 .. max x' y'] where
    ring n | n > x' = left x' n  ++ right x' (-n)
    ring n | n > y' = up   n  y' ++ down (-n) y'
    ring n          = up n n ++ left n n ++ down n n ++ right n n
    up    x y = [(x, n) | n <- [1-y .. y]]; down = (.) reverse . up
    right x y = [(n, y) | n <- [1-x .. x]]; left = (.) reverse . right
    (x', y') = (x `div` 2, y `div` 2)

spiral x y = filter (\(x',y') -> 2*abs x' <= x && 2*abs y' <= y) .
             scanl (\(a,b) (c,d) -> (a+c,b+d)) (0,0) $
             concat [ (:) (1,0) . tail 
                    $ concatMap (replicate n) [(0,1),(-1,0),(0,-1),(1,0)]
                    | n <- [2,4..max x y] ]

22
Tolong jangan menganggap ini sebagai kata-kata kasar atau komentar troll, tapi TUHAN jelek sekali!
Petruza

1
Saya sangat setuju dengan komentar di atas.
Sneakyness

Haskell ini terlihat sangat trendi bagiku.

1
Ya, tapi perhatikan betapa ekspresifnya itu. Bandingkan panjangnya dengan beberapa contoh lain yang diposting di sini.
Robert Harvey

@Petruza Sebenarnya, ini bukan solusi terbaik di Haskell. Lihatlah di sini: rosettacode.org/wiki/Spiral_matrix#Haskell
polkovnikov.ph

2

Ini dalam C.

Saya kebetulan memilih nama variabel yang buruk. Dalam nama T == atas, L == kiri, B == bawah, R == kanan. Jadi, tli di kiri atas saya dan brj di kanan bawah j.

#include<stdio.h>

typedef enum {
   TLTOR = 0,
   RTTOB,
   BRTOL,
   LBTOT
} Direction;

int main() {
   int arr[][3] = {{1,2,3},{4,5,6}, {7,8,9}, {10,11,12}};
   int tli = 0, tlj = 0, bri = 3, brj = 2;
   int i;
   Direction d = TLTOR;

   while (tli < bri || tlj < brj) {
     switch (d) {
     case TLTOR:
    for (i = tlj; i <= brj; i++) {
       printf("%d ", arr[tli][i]);
    }
    tli ++;
    d = RTTOB;
    break;
     case RTTOB:
    for (i = tli; i <= bri; i++) {
       printf("%d ", arr[i][brj]);
    }
    brj --;
    d = BRTOL;
    break;
     case BRTOL:
    for (i = brj; i >= tlj; i--) {
       printf("%d ", arr[bri][i]);
    }
    bri --;
        d = LBTOT;
    break;
     case LBTOT:
    for (i = bri; i >= tli; i--) {
       printf("%d ", arr[i][tlj]);
    }
    tlj ++;
        d = TLTOR;
    break;
 }
   }
   if (tli == bri == tlj == brj) {
      printf("%d\n", arr[tli][tlj]);
   }
}

2

Saya memiliki pustaka sumber terbuka, pixelcan , yaitu pustaka python yang menyediakan fungsi untuk memindai piksel pada kisi dalam berbagai pola spasial. Pola spasial yang dimasukkan adalah lingkaran, cincin, kisi-kisi, ular, dan jalan acak. Ada juga berbagai transformasi (misalnya, klip, tukar, putar, terjemahkan). Masalah OP asli dapat diselesaikan sebagai berikut

for x, y in clip(swap(ringscan(0, 0, 0, 2)), miny=-1, maxy=1):
    print x, y

yang menghasilkan poin

(0,0) (1,0) (1,1) (0,1) (-1,1) (-1,0) (-1,-1) (0,-1) (1,-1) (2,0) (2,1) (-2,1) (-2,0)
(-2,-1) (2,-1)

Generator perpustakaan dan transformasi dapat dirantai untuk mengubah titik dalam berbagai urutan dan pola spasial.


2

Berikut adalah solusi dalam Python 3 untuk mencetak bilangan bulat berturut-turut dalam spiral searah jarum jam dan berlawanan arah jarum jam.

import math

def sp(n): # spiral clockwise
    a=[[0 for x in range(n)] for y in range(n)]
    last=1
    for k in range(n//2+1):
      for j in range(k,n-k):
          a[k][j]=last
          last+=1
      for i in range(k+1,n-k):
          a[i][j]=last
          last+=1
      for j in range(n-k-2,k-1,-1):
          a[i][j]=last
          last+=1
      for i in range(n-k-2,k,-1):
          a[i][j]=last
          last+=1

    s=int(math.log(n*n,10))+2 # compute size of cell for printing
    form="{:"+str(s)+"}"
    for i in range(n):
        for j in range(n):
            print(form.format(a[i][j]),end="")
        print("")

sp(3)
# 1 2 3
# 8 9 4
# 7 6 5

sp(4)
#  1  2  3  4
# 12 13 14  5
# 11 16 15  6
# 10  9  8  7

def sp_cc(n): # counterclockwise
    a=[[0 for x in range(n)] for y in range(n)]
    last=1
    for k in range(n//2+1):
      for j in range(n-k-1,k-1,-1):
          a[n-k-1][j]=last
          last+=1
      for i in range(n-k-2,k-1,-1):
          a[i][j]=last
          last+=1
      for j in range(k+1,n-k):
          a[i][j]=last
          last+=1
      for i in range(k+1,n-k-1):
          a[i][j]=last
          last+=1

    s=int(math.log(n*n,10))+2 # compute size of cell for printing
    form="{:"+str(s)+"}"
    for i in range(n):
        for j in range(n):
            print(form.format(a[i][j]),end="")
        print("")

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

Penjelasan

Sebuah spiral terbuat dari kotak konsentris, misalnya persegi 5x5 dengan rotasi searah jarum jam terlihat seperti ini:

 5x5        3x3      1x1

>>>>>
^   v       >>>
^   v   +   ^ v   +   >
^   v       <<<
<<<<v

( >>>>>berarti "jalan 5 kali benar" atau naikkan indeks kolom 5 kali, vberarti turun atau naikkan indeks baris, dll.)

Semua kotak sama dengan ukurannya, saya memutar kotak konsentris.

Untuk setiap kotak kode memiliki empat loop (satu untuk setiap sisi), dalam setiap loop kami menambah atau mengurangi kolom atau indeks baris. Jika iadalah indeks baris dan indeks jkolom maka kotak 5x5 dapat dibangun dengan: - bertambah jdari 0 menjadi 4 (5 kali) - bertambah idari 1 menjadi 4 (4 kali) - penurunan jdari 3 menjadi 0 (4 kali) - pengurangan idari 3 hingga 1 (3 kali)

Untuk kotak berikutnya (3x3 dan 1x1) kami melakukan hal yang sama tetapi menggeser indeks awal dan akhir dengan tepat. Saya menggunakan indeksk untuk setiap kotak konsentris, ada n // 2 + 1 kotak konsentris.

Akhirnya, beberapa matematika untuk pencetakan cantik.

Untuk mencetak indeks:

def spi_cc(n): # counter-clockwise
    a=[[0 for x in range(n)] for y in range(n)]
    ind=[]
    last=n*n
    for k in range(n//2+1):
      for j in range(n-k-1,k-1,-1):
          ind.append((n-k-1,j))
      for i in range(n-k-2,k-1,-1):
          ind.append((i,j))
      for j in range(k+1,n-k):
          ind.append((i,j))
      for i in range(k+1,n-k-1):
          ind.append((i,j))

    print(ind)

spi_cc(5)

1

Inilah c #, linq'ish.

public static class SpiralCoords
{
  public static IEnumerable<Tuple<int, int>> GenerateOutTo(int radius)
  {
    //TODO trap negative radius.  0 is ok.

    foreach(int r in Enumerable.Range(0, radius + 1))
    {
      foreach(Tuple<int, int> coord in GenerateRing(r))
      {
        yield return coord;
      }
    }
  }

  public static IEnumerable<Tuple<int, int>> GenerateRing(int radius)
  {
    //TODO trap negative radius.  0 is ok.

    Tuple<int, int> currentPoint = Tuple.Create(radius, 0);
    yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);

    //move up while we can
    while (currentPoint.Item2 < radius)
    {
      currentPoint.Item2 += 1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);
    }
    //move left while we can
    while (-radius < currentPoint.Item1)
    {
      currentPoint.Item1 -=1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);    
    }
    //move down while we can
    while (-radius < currentPoint.Item2)
    {
      currentPoint.Item2 -= 1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);
    }
    //move right while we can
    while (currentPoint.Item1 < radius)
    {
      currentPoint.Item1 +=1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);    
    }
    //move up while we can
    while (currentPoint.Item2 < -1)
    {
      currentPoint.Item2 += 1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);
    }
  }

}

Contoh pertama pertanyaan (3x3) adalah:

var coords = SpiralCoords.GenerateOutTo(1);

Contoh kedua pertanyaan (5x3) adalah:

var coords = SpiralCoords.GenerateOutTo(2).Where(x => abs(x.Item2) < 2);

1

Ini adalah versi yang sedikit berbeda - mencoba menggunakan recursiondan iteratorsdalam LUA. Pada setiap langkah program turun lebih jauh ke dalam matriks dan loop. Saya juga menambahkan bendera ekstra untuk spiral clockwiseatau anticlockwise. Output dimulai dari sudut kanan bawah dan loop secara rekursif menuju pusat.

local row, col, clockwise

local SpiralGen
SpiralGen = function(loop)  -- Generator of elements in one loop
    local startpos = { x = col - loop, y = row - loop }
    local IteratePosImpl = function() -- This function calculates returns the cur, next position in a loop. If called without check, it loops infinitely

        local nextpos = {x = startpos.x, y = startpos.y}        
        local step = clockwise and {x = 0, y = -1} or { x = -1, y = 0 }

        return function()

            curpos = {x = nextpos.x, y = nextpos.y}
            nextpos.x = nextpos.x + step.x
            nextpos.y = nextpos.y + step.y
            if (((nextpos.x == loop or nextpos.x == col - loop + 1) and step.y == 0) or 
                ((nextpos.y == loop or nextpos.y == row - loop + 1) and step.x == 0)) then --Hit a corner in the loop

                local tempstep = {x = step.x, y = step.y}
                step.x = clockwise and tempstep.y or -tempstep.y
                step.y = clockwise and -tempstep.x or tempstep.x
                -- retract next step with new step
                nextpos.x = curpos.x + step.x 
                nextpos.y = curpos.y + step.y

            end         
            return curpos, nextpos
        end
    end
    local IteratePos = IteratePosImpl() -- make an instance
    local curpos, nextpos = IteratePos()
    while (true) do
        if(nextpos.x == startpos.x and nextpos.y == startpos.y) then            
            coroutine.yield(curpos)
            SpiralGen(loop+1) -- Go one step inner, since we're done with this loop
            break -- done with inner loop, get out
        else
            if(curpos.x < loop + 1 or curpos.x > col - loop or curpos.y < loop + 1 or curpos.y > row - loop) then
                break -- done with all elemnts, no place to loop further, break out of recursion
            else
                local curposL = {x = curpos.x, y = curpos.y}
                curpos, nextpos = IteratePos()
                coroutine.yield(curposL)
            end
        end     
    end 
end


local Spiral = function(rowP, colP, clockwiseP)
    row = rowP
    col = colP
    clockwise = clockwiseP
    return coroutine.wrap(function() SpiralGen(0) end) -- make a coroutine that returns all the values as an iterator
end


--test
for pos in Spiral(10,2,true) do
    print (pos.y, pos.x)
end

for pos in Spiral(10,9,false) do
    print (pos.y, pos.x)
end

1

// Implementasi PHP

function spiral($n) {

    $r = intval((sqrt($n + 1) - 1) / 2) + 1;

    // compute radius : inverse arithmetic sum of 8+16+24+...=
    $p = (8 * $r * ($r - 1)) / 2;
    // compute total point on radius -1 : arithmetic sum of 8+16+24+...

    $en = $r * 2;
    // points by face

    $a = (1 + $n - $p) % ($r * 8);
    // compute de position and shift it so the first is (-r,-r) but (-r+1,-r)
    // so square can connect

    $pos = array(0, 0, $r);
    switch (intval($a / ($r * 2))) {
        // find the face : 0 top, 1 right, 2, bottom, 3 left
        case 0:
            $pos[0] = $a - $r;
            $pos[1] = -$r;
            break;
        case 1:
            $pos[0] = $r;
            $pos[1] = ($a % $en) - $r;
            break;
        case 2:
            $pos[0] = $r - ($a % $en);
            $pos[1] = $r;
            break;
        case 3:
            $pos[0] = -$r;
            $pos[1] = $r - ($a % $en);
            break;
    }
    return $pos;
}

for ($i = 0; $i < 168; $i++) {

    echo '<pre>';
    print_r(spiral($i));
    echo '</pre>';
}

1

Berikut ini adalah solusi berulang JavaScript (ES6) untuk masalah ini:

let spiralMatrix = (x, y, step, count) => {
    let distance = 0;
    let range = 1;
    let direction = 'up';

    for ( let i = 0; i < count; i++ ) {
        console.log('x: '+x+', y: '+y);
        distance++;
        switch ( direction ) {
            case 'up':
                y += step;
                if ( distance >= range ) {
                    direction = 'right';
                    distance = 0;
                }
                break;
            case 'right':
                x += step;
                if ( distance >= range ) {
                    direction = 'bottom';
                    distance = 0;
                    range += 1;
                }
                break;
            case 'bottom':
                y -= step;
                if ( distance >= range ) {
                    direction = 'left';
                    distance = 0;
                }
                break;
            case 'left':
                x -= step;
                if ( distance >= range ) {
                    direction = 'up';
                    distance = 0;
                    range += 1;
                }
                break;
            default:
                break;
        }
    }
}

Berikut cara menggunakannya:

spiralMatrix(0, 0, 1, 100);

Ini akan membuat spiral luar, mulai dari koordinat (x = 0, y = 0) dengan langkah 1 dan jumlah total item sama dengan 100. Implementasi selalu memulai gerakan dalam urutan berikut - naik, kanan, bawah, kiri.

Harap perhatikan bahwa implementasi ini membuat matriks persegi.


1

Inilah jawaban dalam Julia: pendekatan saya adalah menetapkan titik-titik dalam kotak konsentris ('spiral') di sekitar titik asal (0,0), di mana setiap kotak memiliki panjang sisi m = 2n + 1, untuk menghasilkan kamus berurutan dengan nomor lokasi (mulai dari 1 untuk asal) sebagai kunci dan koordinat yang sesuai sebagai nilai.

Karena lokasi maksimum per spiral adalah pada (n,-n), sisa poin dapat ditemukan dengan hanya bekerja mundur dari titik ini, yaitu dari sudut kanan bawah dengan m-1unit, kemudian mengulangi untuk 3 segmen m-1unit yang tegak lurus .

Proses ini ditulis dalam urutan terbalik di bawah, sesuai dengan bagaimana proses spiral daripada proses penghitungan terbalik ini, yaitu rasegmen [naik kanan] dikurangi dengan 3(m+1), kemudian la[naik kiri] oleh 2(m+1), dan seterusnya - mudah-mudahan ini cukup jelas. .

import DataStructures: OrderedDict, merge

function spiral(loc::Int)
    s = sqrt(loc-1) |> floor |> Int
    if s % 2 == 0
        s -= 1
    end
    s = (s+1)/2 |> Int
    return s
end

function perimeter(n::Int)
    n > 0 || return OrderedDict([1,[0,0]])
    m = 2n + 1 # width/height of the spiral [square] indexed by n
    # loc_max = m^2
    # loc_min = (2n-1)^2 + 1
    ra = [[m^2-(y+3m-3), [n,n-y]] for y in (m-2):-1:0]
    la = [[m^2-(y+2m-2), [y-n,n]] for y in (m-2):-1:0]
    ld = [[m^2-(y+m-1), [-n,y-n]] for y in (m-2):-1:0]
    rd = [[m^2-y, [n-y,-n]] for y in (m-2):-1:0]
    return OrderedDict(vcat(ra,la,ld,rd))
end

function walk(n)
    cds = OrderedDict(1 => [0,0])
    n > 0 || return cds
    for i in 1:n
        cds = merge(cds, perimeter(i))
    end
    return cds
end

Jadi untuk contoh pertama Anda, menyambungkan m = 3ke persamaan untuk menemukan n memberi n = (5-1)/2 = 2, dan walk(2)memberikan kamus lokasi untuk koordinat, yang dapat Anda ubah menjadi hanya array koordinat dengan mengakses bidang kamus vals:

walk(2)
DataStructures.OrderedDict{Any,Any} with 25 entries:
  1  => [0,0]
  2  => [1,0]
  3  => [1,1]
  4  => [0,1]
    => 

[(co[1],co[2]) for co in walk(2).vals]
25-element Array{Tuple{Int64,Int64},1}:
 (0,0)  
 (1,0)  
        
 (1,-2) 
 (2,-2)

Perhatikan bahwa untuk beberapa fungsi [misalnya norm], lebih baik membiarkan koordinat dalam array daripada Tuple{Int,Int}, tetapi di sini saya mengubahnya menjadi tupel— (x,y)—sebagaimana diminta, menggunakan daftar pemahaman.

Konteks untuk "mendukung" matriks non-kuadrat tidak ditentukan (perhatikan bahwa solusi ini masih menghitung nilai off-grid), tetapi jika Anda ingin memfilter hanya rentang xdengan y(di sini untuk x=5, y=3) setelah menghitung spiral penuh maka intersectmatriks ini terhadap nilai-nilai dari walk.

grid = [[x,y] for x in -2:2, y in -1:1]
5×3 Array{Array{Int64,1},2}:
 [-2,-1]  [-2,0]  [-2,1]
                  
 [2,-1]   [2,0]   [2,1]

[(co[1],co[2]) for co in intersect(walk(2).vals, grid)]
15-element Array{Tuple{Int64,Int64},1}:
 (0,0)  
 (1,0)  
  
 (-2,0) 
 (-2,-1)

1

Pertanyaan Anda terlihat seperti pertanyaan yang disebut memori spiral. Dalam masalah itu, setiap kotak pada grid dialokasikan dalam pola spiral mulai dari nomor 1 yang terletak di titik asal. Dan kemudian menghitung sambil berputar keluar. Sebagai contoh:

17  16  15  14  13

18   5   4   3  12

19   6   1   2  11

20   7   8   9  10

21  22  23  ---->

Solusi saya untuk menghitung koordinat setiap angka mengikuti pola spiral ini diposting di bawah ini:

def spiral_pattern(num):
    x = y = 0
    for _ in range(num-1):
        x, y = find_next(x, y)
    yield (x, y)


def find_next(x, y):
    """find the coordinates of the next number"""
    if x == 0 and y == 0:
        return 1, 0

    if abs(x) == abs(y):
        if x > 0 and y > 0:
            x, y = left(x, y)
        elif x < 0 and y > 0:
            x, y = down(x, y)
        elif x < 0 and y < 0:
            x, y = right(x, y)
        elif x > 0 and y < 0:
            x, y = x+1, y
    else:
        if x > y and abs(x) > abs(y):
            x, y = up(x, y)
        elif x < y and abs(x) < abs(y):
            x, y = left(x, y)
        elif x < y and abs(x) > abs(y):
            x, y = down(x, y)
        elif x > y and abs(x) < abs(y):
            x, y = right(x, y)

    return x, y

def up(x, y):
    return x, y+1


def down(x, y):
    return x, y-1


def left(x, y):
    return x-1, y


def right(x, y):
    return x+1, y

0

Ini didasarkan pada solusi Anda sendiri, tetapi kita bisa lebih pintar menemukan sudut. Ini membuatnya lebih mudah untuk melihat bagaimana Anda bisa melewati area di luar jika M dan N sangat berbeda.

def spiral(X, Y):
    x = y = 0
    dx = 0
    dy = -1
    s=0
    ds=2
    for i in range(max(X, Y)**2):
            if abs(x) <= X and abs(y) <= Y/2:
                    print (x, y)
                    # DO STUFF...
            if i==s:
                    dx, dy = -dy, dx
                    s, ds = s+ds/2, ds+1
            x, y = x+dx, y+dy

dan solusi berbasis generator yang lebih baik daripada O (maks (n, m) ^ 2), itu adalah O (nm + abs (nm) ^ 2) karena melompati seluruh strip jika mereka bukan bagian dari solusi.

def spiral(X,Y):
X = X+1>>1
Y = Y+1>>1
x = y = 0
d = side = 1
while x<X or y<Y:
    if abs(y)<Y:
        for x in range(x, x+side, d):
            if abs(x)<X: yield x,y
        x += d
    else:
        x += side
    if abs(x)<X:
        for y in range(y, y+side, d):
            if abs(y)<Y: yield x,y
        y += d
    else:
        y += side
    d =-d
    side = d-side

0
Here is my attempt for simple C solution. First print the outer spiral and move one block inside..and repeat.

#define ROWS        5
#define COLS        5
//int A[ROWS][COLS] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {11, 12, 13, 14}, {15, 16, 17, 18} };
//int A[ROWS][COLS] = { {1, 2, 3}, {6, 7, 8}, { 12, 13, 14} };
//int A[ROWS][COLS] = { {1, 2}, {3, 4}};

int A[ROWS][COLS] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} , {16, 17, 18, 19, 20}, {21, 22, 23, 24, 25} };


void print_spiral(int rows, int cols)
{
    int row = 0;
    int offset = 0;

    while (offset < (ROWS - 1)) {
        /* print one outer loop at a time. */
        for (int col = offset; col <= cols; col++) {
            printf("%d ", A[offset][col]);
        }

        for (row = offset + 1; row <= rows; row++) {
            printf("%d ", A[row][cols]);
        }

        for (int col = cols - 1; col >= offset; col--) {
            printf("%d ", A[rows][col]);
        }

        for (row = rows - 1; row >= offset + 1; row--) {
            printf("%d ", A[row][offset]);
        }

       /* Move one block inside */
        offset++;
        rows--;
        cols--;
    }
    printf("\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
    print_spiral(ROWS-1, COLS-1);
    return 0;
}

0

Ini adalah solusi saya yang sangat buruk, dibuat dari pengetahuan minimum Jawa. Di sini saya harus menempatkan unit pada bidang dalam spiral. Unit tidak dapat ditempatkan di atas unit lain atau di gunung atau di laut.

Agar jelas. Ini bukan solusi yang baik. Ini adalah solusi yang sangat buruk ditambahkan untuk kesenangan orang lain untuk menertawakan betapa buruknya hal itu dapat dilakukan

private void unitPlacementAlgorithm(Position p, Unit u){
    int i = p.getRow();
    int j = p.getColumn();

    int iCounter = 1;
    int jCounter = 0;

    if (getUnitAt(p) == null) {
            unitMap.put(p, u);
    } else {
        iWhileLoop(i, j, iCounter, jCounter, -1, u);
    }

}

private void iWhileLoop(int i, int j, int iCounter, int jCounter, int fortegn, Unit u){
    if(iCounter == 3) {
        for(int k = 0; k < 3; k++) {
            if(k == 2) { //This was added to make the looping stop after 9 units
                System.out.println("There is no more room around the city");
                return; 
            }
            i--;

            if (getUnitAt(new Position(i, j)) == null 
                && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.OCEANS)) 
                && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.MOUNTAINS))) {
                    unitMap.put(new Position(i, j), u);
                    return;
            }
            iCounter--;
        }
    }

    while (iCounter > 0) {
        if (fortegn > 0) {
            i++;
        } else {
            i--;
        }

        if (getUnitAt(new Position(i, j)) == null 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.OCEANS)) 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.MOUNTAINS))) {
                unitMap.put(new Position(i, j), u);
                return;
        }
        iCounter--;
        jCounter++;
    }
    fortegn *= -1;
    jWhileLoop(i, j, iCounter, jCounter, fortegn, u);
}

private void jWhileLoop(int i, int j, int iCounter, int jCounter,
        int fortegn, Unit u) {
    while (jCounter > 0) {
        if (fortegn > 0) {
            j++;
        } else {
            j--;
        }

        if (getUnitAt(new Position(i, j)) == null 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.OCEANS)) 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.MOUNTAINS))) {
                unitMap.put(new Position(i, j), u);
                return;

        }
        jCounter--;
        iCounter++;
        if (jCounter == 0) {
            iCounter++;
        }

    }
    iWhileLoop(i, j, iCounter, jCounter, fortegn, u);
}

Cudos kepada siapa saja yang benar-benar dapat membaca ini

Pertanyaan bonus: Berapa waktu berjalan dari "algoritma" ini? : P


1
1 karena " Ini adalah solusi yang sangat buruk ditambahkan untuk bersenang-senang orang lain menertawakan betapa buruknya hal itu dapat dilakukan ".
Oriol

0

Solusi untuk AutoIt

#include <Math.au3>
#include <Array.au3>

Func SpiralSearch($xMax,$yMax)
    $x = 0
    $y = 0
    $dx = 0
    $dy = -1
    for $i=0 To _max($xMax, $yMax)^2-1 Step 1
        if -$xMax/2 < $x and $x <= $xMax/2 And -$yMax/2 < $y And $y <= $yMax/2 Then
            MsgBox(0, "We are here ", $x & " " & $y)
        EndIf
        if $x == $y or ($x < 0 and $x == -$y) or ($x > 0 and $x == 1-$y) Then
            _ArraySwap ($dx, $dy)
            $dx=-$dx
        EndIf
        $x += $dx
        $y += $dy
    Next
EndFunc

0

Saya baru-baru ini memiliki tantangan serupa di mana saya harus membuat array 2D dan menggunakan algoritma matriks spiral untuk mengurutkan dan mencetak hasilnya. Kode C # ini akan bekerja dengan array N, N 2D. Ini sangat jelas untuk kejelasan dan kemungkinan dapat diperhitungkan ulang sesuai dengan kebutuhan Anda.

//CREATE A NEW MATRIX OF SIZE 4 ROWS BY 4 COLUMNS - SCALE MATRIX SIZE HERE
SpiralMatrix SM = new SpiralMatrix(4, 4);
string myData = SM.Read();


public class SpiralMatrix
{
    //LETS BUILD A NEW MATRIX EVERY TIME WE INSTANTIATE OUR CLASS
    public SpiralMatrix(int Rows, int Cols)
    {
        Matrix = new String[Rows, Cols];

        int pos = 1;
        for(int r = 0; r<Rows; r++){
            for (int c = 0; c < Cols; c++)
            {
                //POPULATE THE MATRIX WITH THE CORRECT ROW,COL COORDINATE
                Matrix[r, c] = pos.ToString();
                pos++;
            }
        }
    }

    //READ MATRIX
    public string Read()
    {
        int Row = 0;
        int Col = 0;

        string S = "";
        bool isDone = false;

        //CHECK tO SEE IF POSITION ZERO IS AVAILABLE
        if(PosAvailable(Row, Col)){
            S = ConsumePos(Row, Col);
        }


        //START READING SPIRAL
        //THIS BLOCK READS A FULL CYCLE OF RIGHT,DOWN,LEFT,UP EVERY ITERATION
        while(!isDone)
        {
            bool goNext = false;

            //READ ALL RIGHT SPACES ON THIS PATH PROGRESSION
            while (PosAvailable(Row, Col+1))
            {
                //Is ReadRight Avail
                Col++;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            //READ ALL DOWN SPACES ON THIS PATH PROGRESSION
            while(PosAvailable(Row+1, Col)){
                //Is ReadDown Avail
                Row++;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            //READ ALL LEFT SPACES ON THIS PATH PROGRESSION
            while(PosAvailable(Row, Col-1)){
                //Is ReadLeft Avail
                Col--;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            //READ ALL UP SPACES ON THIS PATH PROGRESSION
            while(PosAvailable(Row-1, Col)){
                //Is ReadUp Avail
                Row--;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            if(!goNext){
                //DONE - SET EXIT LOOP FLAG
                isDone = true;
            }
        }

        return S;
    }

    //DETERMINE IF THE POSITION IS AVAILABLE
    public bool PosAvailable(int Row, int Col)
    {
        //MAKE SURE WE ARE WITHIN THE BOUNDS OF THE ARRAY
        if (Row < Matrix.GetLength(0) && Row >= 0
            && Col < Matrix.GetLength(1) && Col >= 0)
        {
            //CHECK COORDINATE VALUE
            if (Matrix[Row, Col] != ConsumeChar)
                return true;
            else
                return false;
        }
        else
        {
            //WE ARE OUT OF BOUNDS
            return false;
        }
    }

    public string ConsumePos(int Row, int Col)
    {
        string n = Matrix[Row, Col];
        Matrix[Row, Col] = ConsumeChar;
        return n;
    }

    public string ConsumeChar = "X";
    public string[,] Matrix;
}

0

Saya membuat ini dengan seorang teman yang menyesuaikan rasio aspek spiral ke kanvas pada Javascript. Solusi terbaik yang saya dapatkan untuk piksel evolusi gambar demi piksel, mengisi seluruh gambar.

Semoga ini bisa membantu seseorang.

var width = 150;
var height = 50;

var x = -(width - height)/2;
var y = 0;
var dx = 1;
var dy = 0;
var x_limit = (width - height)/2;
var y_limit = 0;
var counter = 0;

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');

setInterval(function(){
   if ((-width/2 < x && x <= width/2)  && (-height/2 < y && y <= height/2)) {
       console.log("[ " + x + " , " +  y + " ]");
       ctx.fillStyle = "#FF0000";
       ctx.fillRect(width/2 + x, height/2 - y,1,1);
   }
   if( dx > 0 ){//Dir right
       if(x > x_limit){
           dx = 0;
           dy = 1;
       }
   }
   else if( dy > 0 ){ //Dir up
       if(y > y_limit){
           dx = -1;
           dy = 0;
       }
   }
   else if(dx < 0){ //Dir left
       if(x < (-1 * x_limit)){
           dx = 0;
           dy = -1;
       }
   }
   else if(dy < 0) { //Dir down
       if(y < (-1 * y_limit)){
           dx = 1;
           dy = 0;
           x_limit += 1;
           y_limit += 1;
       }
   }
   counter += 1;
   //alert (counter);
   x += dx;
   y += dy;      
}, 1);

Anda dapat melihatnya bekerja di http://jsfiddle.net/hitbyatruck/c4Kd6/ . Pastikan untuk mengubah lebar dan tinggi kanvas pada vars javascript dan atribut pada HTML.


0

Hanya untuk bersenang-senang di Javascript:

function spiral(x, y) {
  var iy = ix = 0
    , hr = (x - 1) / 2
    , vr = (y - 1) / 2
    , tt = x * y
    , matrix = []
    , step = 1
    , dx = 1
    , dy = 0;

  while(matrix.length < tt) {

    if((ix <= hr && ix >= (hr * -1)) && (iy <= vr && (iy >= (vr * -1)))) {
      console.log(ix, iy);
      matrix.push([ix, iy]);
    }

    ix += dx;
    iy += dy;

    // check direction
    if(dx !== 0) {
      // increase step
      if(ix === step && iy === (step * -1)) step++;

      // horizontal range reached
      if(ix === step || (ix === step * -1)) {
        dy = (ix === iy)? (dx * -1) : dx;
        dx = 0;  
      }
    } else {
      // vertical range reached
      if(iy === step || (iy === step * -1)) {
        dx = (ix === iy)? (dy * -1) : dy;
        dy = 0;
      }
    }
  }

  return matrix;
}

var sp = spiral(5, 3);

0

Versi C #, menangani ukuran non-persegi juga.

private static Point[] TraverseSpiral(int width, int height) {
    int numElements = width * height + 1;
    Point[] points = new Point[numElements];

    int x = 0;
    int y = 0;
    int dx = 1;
    int dy = 0;
    int xLimit = width - 0;
    int yLimit = height - 1;
    int counter = 0;

    int currentLength = 1;
    while (counter < numElements) {
        points[counter] = new Point(x, y);

        x += dx;
        y += dy;

        currentLength++;
        if (dx > 0) {
            if (currentLength >= xLimit) {
                dx = 0;
                dy = 1;
                xLimit--;
                currentLength = 0;
            }
        } else if (dy > 0) {
            if (currentLength >= yLimit) {
                dx = -1;
                dy = 0;
                yLimit--;
                currentLength = 0;
            }
        } else if (dx < 0) {
            if (currentLength >= xLimit) {
                dx = 0;
                dy = -1;
                xLimit--;
                currentLength = 0;
            }
        } else if (dy < 0) {
            if (currentLength >= yLimit) {
                dx = 1;
                dy = 0;
                yLimit--;
                currentLength = 0;
            }
        }

        counter++;
    }

    Array.Reverse(points);
    return points;
}

0

Saya membagikan kode ini yang saya rancang untuk tujuan yang berbeda; ini adalah tentang menemukan nomor Kolom "X", dan nomor baris "Y" elemen array @ indeks spiral "indeks". Fungsi ini mengambil lebar "w" dan tinggi "h" dari matriks, dan "indeks" yang diperlukan. Tentu saja, fungsi ini dapat digunakan untuk menghasilkan output yang dibutuhkan sama. Saya pikir ini adalah metode tercepat yang mungkin (karena melompati sel daripada memindai mereka).

    rec BuildSpiralIndex(long w, long h, long index = -1)
    {  
        long count = 0 , x = -1,  y = -1, dir = 1, phase=0, pos = 0,                            length = 0, totallength = 0;
        bool isVertical = false;
        if(index>=(w*h)) return null;

        do 
        {                
            isVertical = (count % 2) != 0;
            length = (isVertical ? h : w) - count/2 - count%2 ;
            totallength += length;
            count++;
        } while(totallength<index);

        count--; w--; h--;
        phase = (count / 4); pos = (count%4);
        x = (pos > 1 ? phase : w - phase);
        y = ((pos == 1 || pos == 2) ? h - phase : phase) + (1 * (pos == 3 ? 1 : 0));
        dir = pos > 1 ? -1 : 1;
        if (isVertical) y -= (totallength - index - 1) * dir;
        else x -= (totallength - index -1) * dir;
        return new rec { X = x, Y = y };
    }

0

Python looping kode spiral searah jarum jam menggunakan Can Berk Güder menjawab .

def spiral(X, Y):
    x = y = 0
    dx = 0
    dy = 1
    for i in range(max(X, Y)**2):
        if (-X/2 < x <= X/2) and (-Y/2 < y <= Y/2):
            print (x, y)
            # DO STUFF...
        if x == -y or (x < 0 and x == y) or (x > 0 and x-1 == y):
            dx, dy = dy, -dx
        x, y = x+dx, y+dy

1
Searah jarum jam 🔃 dan saya mengutip Can Berk Güder. Pertanyaan asli adalah untuk berlawanan arah jarum jam 🔄. Saya membutuhkan fungsi searah jarum jam jadi saya merasa akan bermanfaat untuk meninggalkannya di sana.
adrianmelic

0

Solusi terbaik Davidont di VB.Net

    Public Function Spiral(n As Integer) As RowCol
    ' given n an index in the squared spiral
    ' p the sum of point in inner square
    ' a the position on the current square
    ' n = p + a
    ' starts with row 0 col -1
    Dim r As Integer = CInt(Math.Floor((Math.Sqrt(n + 1) - 1) / 2) + 1)

    ' compute radius : inverse arithmetic sum of 8+16+24+...=
    Dim p As Integer = (8 * r * (r - 1)) \ 2
    ' compute total point on radius -1 : arithmetic sum of 8+16+24+...

    Dim en As Integer = r * 2
    ' points by face

    Dim a As Integer = (1 + n - p) Mod (r * 8)
    ' compute the position and shift it so the first is (-r,-r) but (-r+1,-r)
    ' so square can connect

    Dim row As Integer
    Dim col As Integer

    Select Case Math.Floor(a \ (r * 2))
        ' find the face : 0 top, 1 right, 2, bottom, 3 left
        Case 0
            row = a - r
            col = -r
        Case 1
            row = r
            col = (a Mod en) - r
        Case 2
            row = r - (a Mod en)
            col = r
        Case 3
            row = -r
            col = r - (a Mod en)
    End Select

    Return New RowCol(row, col)
End Function
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.