Vòng lặp theo hình xoắn ốc


154

Một người bạn đang cần một thuật toán cho phép anh ta lặp qua các phần tử của ma trận NxM (N và M là số lẻ). Tôi đã đưa ra một giải pháp, nhưng tôi muốn xem liệu các đồng nghiệp của mình có thể đưa ra một giải pháp tốt hơn không.

Tôi đang đăng giải pháp của mình như một câu trả lời cho câu hỏi này.

Kết quả ví dụ:

Đối với ma trận 3x3, đầu ra phải là:

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

Ma trận 3x3

Hơn nữa, thuật toán sẽ hỗ trợ các ma trận không vuông, vì vậy, ví dụ cho ma trận 5x3, đầu ra phải là:

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

Ma trận 5x3


Bạn có thể giải thích những gì bạn muốn cho ma trận không vuông? Giải pháp của bạn có một "bước nhảy" từ (2.1) đến (-2,1) - đây có phải là dự định không? [Ví dụ: đối với ma trận 7x3, nó sẽ có thêm hai lần "nhảy" và đối với ma trận (2k + 1) x3, nó sẽ có các lần nhảy 2k-3?]
ShreevatsaR

3
Vâng, các bước nhảy là có chủ ý. Tôi đã cập nhật câu hỏi với hình ảnh ma trận 5x3. Như bạn có thể thấy từ hình ảnh, chúng tôi bỏ qua các hàng trên cùng và dưới cùng.
Can Berk Güder

Ok, sau đó mã của riêng bạn có vẻ sạch nhất. Và mặc dù điều này là không chính thống: làm thế nào bạn tạo ra những hình ảnh đó? :)
ShreevatsaR

=)) Tôi đã không tạo ra chúng. Trên thực tế, cách tôi tạo ra chúng khá ngu ngốc. Tôi đã tạo các bảng trong OO.org Calc, chụp ảnh màn hình và chỉnh sửa ảnh chụp màn hình trong GIMP. =))
Có thể Berk Güder

1
@Ying: Tôi không thực sự biết tại sao bạn tôi cần điều này, nhưng anh ấy nói rằng anh ấy muốn ủng hộ các thành viên của ma trận gần trung tâm hơn trong thuật toán tìm kiếm.
Có thể Berk Güder

Câu trả lời:


63

Đây là giải pháp của tôi (bằng 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
Đây là cách tốt nhất để viết nó, theo như tôi có thể thấy. Cải tiến duy nhất có thể là làm cho nó O (MN) thay vì O (max (M, N) ^ 2) bằng cách bỏ qua trực tiếp những (x, y) sẽ không được in, nhưng điều đó sẽ tạo ra mã xấu hơn một chút
ShreevatsaR

Tôi đang tối ưu hóa giải pháp của mình và nó khá gần với những gì bạn đã có. Đây là một giải pháp khá tốt tôi nghĩ. Bên cạnh gợi ý của ShreevatsaR, và những thứ như không tính x / 2 và y / 2 mỗi lần lặp, không có quá nhiều thứ để cải thiện ngoại trừ kiểu dáng.
Triptych

Bất kỳ giải pháp cho MATLAB?!
Sam

Điều này có cho sự kết hợp bộ nhớ cache tốt để truy cập dữ liệu bộ đệm hình ảnh không? (Có rất nhiều câu trả lời ở đây, nhưng không có nhiều thông tin liên quan đến hoạt động tốt nhất cho hoạt động hình ảnh hiệu suất cao)
ideaman42

@ ideaman42 - điều đó không xảy ra, bởi vì kết quả luôn luôn là mô hình tọa độ xoắn ốc giống nhau. Cho dù mô hình xoắn ốc là bộ nhớ cache mạch lạc, tôi đoán là phụ thuộc vào việc thực hiện bộ đệm hình ảnh. (tôi đoán là nó sẽ đập bộ nhớ cache nhiều hơn các cách khác để di chuyển hình ảnh, giống như đi từng dòng theo thứ tự). Nhưng việc lựa chọn thuật toán để tạo ra các tọa độ này có thể sẽ không ảnh hưởng đến bộ đệm.
Raptormeat

31

C ++ có ai không? Dịch nhanh từ python, được đăng cho đầy đủ

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

bạn cũng có thể sử dụng s và DS như tôi để phát hiện các góc bị loại bỏ nếu có điều kiện
John La Rooy

1
Một chỉnh sửa cho bài viết này đã được đề xuất ở đây . Mặc dù chỉnh sửa đã bị từ chối vì nó thay đổi ý nghĩa của bài đăng của bạn, bạn có thể muốn xem xét kết hợp các thay đổi được đề xuất nếu nó hợp lý để làm như vậy.
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

Đã có nhiều giải pháp được đề xuất cho vấn đề này được viết bằng nhiều ngôn ngữ lập trình khác nhau, tuy nhiên tất cả chúng đều xuất phát từ cùng một cách tiếp cận phức tạp. Tôi sẽ xem xét vấn đề chung hơn của việc tính toán một vòng xoắn ốc có thể được thể hiện chính xác bằng cách sử dụng cảm ứng.

Trường hợp cơ sở: Bắt đầu tại (0, 0), di chuyển về phía trước 1 hình vuông, rẽ trái, di chuyển về phía trước 1 hình vuông, rẽ trái. Bước quy nạp: Di chuyển về phía trước n + 1 hình vuông, rẽ trái, di chuyển về phía trước n + 1 hình vuông, rẽ trái.

Sự tao nhã toán học của việc thể hiện vấn đề này mạnh mẽ cho thấy cần có một thuật toán đơn giản để tính toán giải pháp. Giữ sự trừu tượng trong tâm trí, tôi đã chọn không thực hiện thuật toán bằng ngôn ngữ lập trình cụ thể mà là mã giả.

Đầu tiên tôi sẽ xem xét một thuật toán để tính toán chỉ 2 lần lặp của vòng xoắn ốc bằng cách sử dụng 4 cặp vòng lặp while. Cấu trúc của mỗi cặp tương tự nhau, nhưng khác biệt theo đúng nghĩa của nó. Điều này thoạt nghe có vẻ điên rồ (một số vòng chỉ được thực hiện một lần) nhưng từng bước tôi sẽ thực hiện các phép biến đổi cho đến khi chúng ta đến 4 cặp vòng giống hệt nhau và do đó có thể được thay thế bằng một cặp duy nhất được đặt bên trong một vòng lặp khác. Điều này sẽ cung cấp cho chúng tôi một giải pháp chung về tính toán lặp đi lặp lại mà không sử dụng bất kỳ điều kiện nào.

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

Biến đổi đầu tiên chúng ta sẽ thực hiện là giới thiệu một biến mới d, theo hướng, giữ giá trị +1 hoặc -1. Hướng chuyển đổi sau mỗi cặp vòng. Vì chúng ta biết giá trị của d tại tất cả các điểm, chúng ta có thể nhân mỗi bên của mỗi bất đẳng thức với nó, điều chỉnh hướng của bất đẳng thức cho phù hợp và đơn giản hóa bất kỳ phép nhân nào của d với hằng số khác. Điều này để lại cho chúng tôi với những điều sau đây.

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

Bây giờ chúng tôi lưu ý rằng cả x * d và RHS đều là số nguyên để chúng tôi có thể trừ bất kỳ giá trị thực nào từ 0 đến 1 khỏi RHS mà không ảnh hưởng đến kết quả của bất đẳng thức. Chúng tôi chọn trừ 0,5 từ bất đẳng thức của mỗi cặp vòng lặp khác để thiết lập nhiều mẫu hơn.

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

Bây giờ chúng ta có thể giới thiệu một biến m khác cho số bước chúng ta thực hiện ở mỗi cặp vòng lặp while.

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

Cuối cùng, chúng ta thấy rằng cấu trúc của mỗi cặp vòng lặp giống hệt nhau và có thể được giảm xuống thành một vòng lặp được đặt bên trong một vòng lặp khác. Ngoài ra, để tránh sử dụng các số có giá trị thực, tôi đã nhân giá trị ban đầu của m; giá trị m được tăng lên theo; và cả hai mặt của mỗi bất đẳng thức bằng 2.

Điều này dẫn đến giải pháp hiển thị ở đầu câu trả lời này.


1
Trong những điều kiện giải pháp cuối cùng của bạn sẽ chấm dứt?
Merlyn Morgan-Graham

1
Ứng dụng của kiểu in như vậy là gì?
Ashish Shukla

1
@ MerlynMorgan-Graham Nó chấm dứt khi máy tính hết bộ nhớ hoặc mất điện.
Mike

Dường như sự thanh lịch của giải pháp đó bắt nguồn từ việc bỏ qua các hạn chế về thời gian và bộ nhớ. Tôi khuyên bạn nên thêm một điều kiện chấm dứt (nếu có thể). Tôi cũng khuyên bạn nên di chuyển nó lên đầu câu trả lời và hiển thị đạo hàm bên dưới nó.
Merlyn Morgan-Graham

1
Trong khi câu hỏi ban đầu là về một ma trận NxM, đây thực sự là một câu trả lời rất hữu ích nếu bạn cần phải xoắn ốc vô tận cho đến khi bạn tìm thấy một cái gì đó (tức là sau đó phá vỡ hoặc quay trở lại). Tất nhiên, giống như các ý kiến ​​khác đã lưu ý, bạn cần xác định điều kiện chấm dứt đó hoặc nó sẽ chạy mãi mãi.
cclogg

16

Đây là giải pháp O (1) để tìm vị trí trong hình xoắn ốc bình phương: 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
Để bắt đầu từ trung tâm thêm hai dòng. if (n === 0) return [0, 0, r]; --n;Xem Fiddle: jsfiddle.net/Wishmesh/nwd9gt1s/2
Maris B.

15

Tôi yêu máy phát điện của trăn.

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

Kiểm tra với:

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

Bạn lấy:

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

Nỗ lực "Code golf" xoắn ốc của Java, dựa trên biến thể 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

Đây là một giải pháp C ++ cho thấy rằng bạn có thể tính toán tọa độ (x, y) tiếp theo một cách trực tiếp và dễ dàng từ các tọa độ trước - không cần theo dõi hướng hiện tại, bán kính hoặc bất cứ điều gì khác:

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

Nếu tất cả những gì bạn đang cố gắng là tạo N điểm đầu tiên trong vòng xoắn ốc (không có ràng buộc của vấn đề ban đầu về mặt nạ đối với vùng N x M), mã sẽ trở nên rất đơn giản:

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

Mẹo nhỏ là bạn có thể so sánh x và y để xác định phía bên nào của hình vuông và điều đó cho bạn biết nên di chuyển theo hướng nào.


5

TDD, bằng Java.

Xoắn ốcTest.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);
    }

}

Xoắn ốc

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: Có thể không - chắc chắn là không đủ ngắn - nhưng tôi nghĩ đó là một minh chứng tốt về TDD, và mã chính xác, sạch sẽ, dễ hiểu, hợp lý. Tôi sẽ để nó lại.
Carl Manaster

4

Đây là giải pháp của tôi (Trong 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}),"
}

Vẫn là O (max (n, m) ^ 2), nhưng phong cách đẹp.
Triptych

1
hướng = -direction thay vì hướng * = - 1? nếu bạn chơi golf thì d = -d ngắn hơn d * = - 1 quá
John La Rooy

3

Haskell, hãy lựa chọn:

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
Xin đừng coi đây là một lời tán dương hay bình luận của một kẻ troll, nhưng THIÊN CHÚA thật xấu xa!
Petruza

1
Tôi không thể đồng ý với nhận xét trên nhiều hơn.
Lén lút

Haskell này trông rất hợp thời trang với tôi.

1
Có, nhưng lưu ý cách biểu cảm của nó. So sánh độ dài của nó với một số ví dụ khác được đăng ở đây.
Robert Harvey

@Petruza Thật ra, đó không phải là giải pháp tốt nhất trong Haskell. Hãy xem ở đây: rosettacode.org/wiki/Spiral_matrix#Haskell
polkovnikov.ph

2

Đây là trong C.

Tôi tình cờ chọn tên biến xấu. Trong các tên T == top, L == left, B == bottom, R == right. Vì vậy, tli là trên cùng bên trái i và brj là dưới cùng bên phải 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

Tôi có một thư viện mã nguồn mở, pixelcan , đó là một thư viện python cung cấp các hàm để quét các pixel trên lưới theo nhiều kiểu không gian khác nhau. Các mô hình không gian bao gồm hình tròn, vòng, lưới, rắn và bước đi ngẫu nhiên. Ngoài ra còn có các biến đổi khác nhau (ví dụ: clip, hoán đổi, xoay, dịch). Vấn đề OP ban đầu có thể được giải quyết như sau

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

mang lại điểm

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

Các trình tạo và chuyển đổi thư viện có thể được kết nối để thay đổi các điểm theo nhiều thứ tự và mô hình không gian.


2

Đây là một giải pháp trong Python 3 để in các số nguyên liên tiếp theo hình xoắn ốc theo chiều kim đồng hồ và ngược chiều kim đồng hồ.

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

Giải trình

Một hình xoắn ốc được tạo thành từ các hình vuông đồng tâm, ví dụ hình vuông 5x5 với góc quay theo chiều kim đồng hồ trông như thế này:

 5x5        3x3      1x1

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

( >>>>>có nghĩa là "đi đúng 5 lần" hoặc tăng chỉ số cột 5 lần, vcó nghĩa là giảm hoặc tăng chỉ số hàng, v.v.)

Tất cả các hình vuông đều giống nhau với kích thước của chúng, tôi vòng qua các hình vuông đồng tâm.

Đối với mỗi ô vuông, mã có bốn vòng (một vòng cho mỗi bên), trong mỗi vòng lặp, chúng tôi tăng hoặc giảm các cột hoặc chỉ mục hàng. Nếu ilà chỉ số hàng và chỉ mục jcột thì có thể xây dựng hình vuông 5x5 bằng cách: - tăng jtừ 0 đến 4 (5 lần) - tăng itừ 1 đến 4 (4 lần) - giảm jtừ 3 ​​xuống 0 (4 lần) - giảm dần itừ 3 ​​đến 1 (3 lần)

Đối với các hình vuông tiếp theo (3x3 và 1x1), chúng tôi cũng làm tương tự nhưng thay đổi các chỉ số ban đầu và cuối cùng một cách thích hợp. Tôi đã sử dụng một chỉ mục kcho mỗi hình vuông đồng tâm, có n // 2 + 1 hình vuông đồng tâm.

Cuối cùng, một số toán học cho in ấn đẹp.

Để in các chỉ mục:

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

Đây là 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);
    }
  }

}

Ví dụ đầu tiên của câu hỏi (3x3) sẽ là:

var coords = SpiralCoords.GenerateOutTo(1);

Ví dụ thứ hai của câu hỏi (5x3) sẽ là:

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

1

Đây là một phiên bản hơi khác - cố gắng sử dụng recursioniteratorstrong LUA. Ở mỗi bước, chương trình đi xuống bên trong ma trận và các vòng lặp. Tôi cũng đã thêm một cờ bổ sung cho xoắn ốc clockwisehoặc anticlockwise. Đầu ra bắt đầu từ góc dưới bên phải và các vòng lặp đệ quy về phía trung tâm.

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

// Thực hiện 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

Đây là một giải pháp lặp lại JavaScript (ES6) cho vấn đề này:

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

Đây là cách sử dụng nó:

spiralMatrix(0, 0, 1, 100);

Điều này sẽ tạo ra một vòng tròn hướng ra ngoài, bắt đầu từ tọa độ (x = 0, y = 0) với bước 1 và tổng số mục bằng 100. Việc thực hiện luôn bắt đầu chuyển động theo thứ tự sau - lên, phải, dưới cùng, trái.

Xin lưu ý rằng việc thực hiện này tạo ra ma trận vuông.


1

Đây là một câu trả lời trong Julia: cách tiếp cận của tôi là gán các điểm trong các ô vuông đồng tâm ('xoắn ốc') xung quanh gốc tọa độ (0,0), trong đó mỗi ô vuông có độ dài cạnh m = 2n + 1, để tạo ra một từ điển có thứ tự với số vị trí (bắt đầu từ 1 cho gốc) và tọa độ tương ứng là giá trị.

Vì vị trí tối đa trên mỗi vòng xoắn ốc là tại (n,-n), phần còn lại của các điểm có thể được tìm thấy bằng cách đơn giản làm việc ngược từ điểm này, tức là từ góc dưới bên phải theo m-1đơn vị, sau đó lặp lại cho 3 đoạn vuông góc của m-1đơn vị.

Quá trình này được viết theo thứ tự ngược lại bên dưới, tương ứng với cách thức xoắn ốc tiến hành thay vì quá trình đếm ngược này, tức là đoạn ra[tăng dần phải] giảm dần 3(m+1), sau đó la[tăng dần] 2(m+1), v.v. - hy vọng điều này là tự giải thích .

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

Vì vậy, với ví dụ đầu tiên của bạn, hãy cắm m = 3vào phương trình để tìm n đưa ra n = (5-1)/2 = 2walk(2)đưa ra một từ điển vị trí theo thứ tự cho tọa độ, mà bạn có thể biến thành một mảng tọa độ bằng cách truy cập vào trường của từ điển 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)

Lưu ý rằng đối với một số chức năng [ví dụ norm] có thể tốt hơn là để các tọa độ trong các mảng thay vì Tuple{Int,Int}, nhưng ở đây tôi thay đổi chúng thành các tuples của Wapasas (x,y)được yêu cầu, sử dụng khả năng hiểu danh sách.

Ngữ cảnh "hỗ trợ" một ma trận không vuông không được chỉ định (lưu ý rằng giải pháp này vẫn tính toán các giá trị ngoài lưới), nhưng nếu bạn muốn lọc chỉ phạm vi xtheo y(ở đây cho ) x=5, y=3sau khi tính toán xoắn ốc đầy đủ sau đó intersectma trận này chống lại các giá trị từ 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

Câu hỏi của bạn trông giống như một câu hỏi gọi là bộ nhớ xoắn ốc. Trong bài toán đó, mỗi ô vuông trên lưới được phân bổ theo mô hình xoắn ốc bắt đầu từ số 1 nằm ở điểm gốc. Và sau đó đếm ngược trong khi xoắn ốc ra bên ngoài. Ví dụ:

17  16  15  14  13

18   5   4   3  12

19   6   1   2  11

20   7   8   9  10

21  22  23  ---->

Giải pháp của tôi để tính toán tọa độ của từng số theo mô hình xoắn ốc này được đăng dưới đây:

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

Điều này dựa trên giải pháp của riêng bạn, nhưng chúng ta có thể thông minh hơn trong việc tìm kiếm các góc. Điều này giúp dễ dàng hơn để xem cách bạn có thể bỏ qua các khu vực bên ngoài nếu M và N rất khác nhau.

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

và một giải pháp dựa trên máy phát tốt hơn O (max (n, m) ^ 2), đó là O (nm + abs (nm) ^ 2) vì nó bỏ qua toàn bộ dải nếu chúng không phải là một phần của giải pháp.

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

Đây là giải pháp rất tồi tệ của tôi, được tạo ra từ kiến ​​thức tối thiểu về Java. Ở đây tôi phải đặt các đơn vị trên một lĩnh vực theo hình xoắn ốc. Các đơn vị không thể được đặt trên đầu của các đơn vị khác hoặc trên núi hoặc trong đại dương.

Để được rõ ràng. Đây không phải là một giải pháp tốt. Đây là một giải pháp rất tệ được thêm vào để tạo niềm vui cho người khác để cười về việc nó có thể được thực hiện tồi tệ như thế nào

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 cho bất cứ ai thực sự có thể đọc này

Câu hỏi thưởng: Thời gian chạy của "thuật toán" này là gì? : P


1
+1 vì " Đây là một giải pháp rất tệ được thêm vào để tạo niềm vui cho người khác để cười về việc nó có thể được thực hiện tệ đến mức nào ".
Oriol

0

Giải pháp cho 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

Gần đây tôi đã có một thử thách tương tự khi tôi phải tạo một mảng 2D và sử dụng thuật toán ma trận xoắn ốc để sắp xếp và in kết quả. Mã C # này sẽ hoạt động với mảng N, N 2D. Nó dài dòng cho sự rõ ràng và có thể được tái hiện để phù hợp với nhu cầu của bạn.

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

Tôi đã thực hiện điều này với một người bạn điều chỉnh tỷ lệ xoắn ốc theo tỷ lệ khung hình trên Javascript. Giải pháp tốt nhất tôi nhận được cho một pixel tiến hóa hình ảnh theo pixel, lấp đầy toàn bộ hình ảnh.

Hy vọng nó sẽ giúp được ai đó.

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

Bạn có thể thấy nó hoạt động trên http://jsfiddle.net/hitbyatruck/c4Kd6/ . Chỉ cần đảm bảo thay đổi chiều rộng và chiều cao của khung vẽ trên các tệp javascript và trên các thuộc tính trên HTML.


0

Chỉ để giải trí trong 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

Phiên bản C #, xử lý kích thước không vuông là tốt.

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

Tôi đang chia sẻ mã này mà tôi thiết kế cho một mục đích khác; đó là về việc tìm số Cột "X" và số hàng "Y" của phần tử mảng @ chỉ số xoắn ốc "chỉ mục". Hàm này lấy chiều rộng "w" và chiều cao "h" của ma trận và "chỉ mục" bắt buộc. Tất nhiên, chức năng này có thể được sử dụng để tạo ra cùng một đầu ra cần thiết. Tôi nghĩ rằng đó là phương pháp nhanh nhất có thể (vì nó nhảy qua các tế bào thay vì quét chúng).

    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 lặp mã xoắn ốc theo chiều kim đồng hồ bằng cách sử dụng câu trả lời Can Berk Güder .

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
Đó là chiều kim đồng hồ và tôi đã trích dẫn Can Berk Güder. Câu hỏi ban đầu là ngược chiều kim đồng hồ 🔄. Tôi cần một chức năng theo chiều kim đồng hồ vì vậy tôi cảm thấy nó sẽ hữu ích khi để nó ở đó.
adrianmelic

0

Giải pháp tuyệt vời của Davidont trong 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.