วนเป็นเกลียว


154

เพื่อนต้องการอัลกอริทึมที่จะปล่อยให้เขาวนผ่านองค์ประกอบของเมทริกซ์ NxM (N และ M เป็นเลขคี่) ฉันคิดวิธีแก้ปัญหา แต่ฉันอยากเห็นว่าเพื่อนร่วมงานของฉันสามารถหาทางแก้ปัญหาที่ดีกว่านี้ได้หรือไม่

ฉันโพสต์โซลูชันของฉันเป็นคำตอบสำหรับคำถามนี้

ตัวอย่างผลลัพธ์:

สำหรับเมทริกซ์ 3x3 เอาต์พุตควรเป็น:

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

เมทริกซ์ 3x3

นอกจากนี้อัลกอริทึมควรสนับสนุนเมทริกซ์ที่ไม่ใช่สแควร์ดังนั้นตัวอย่างเช่นเมทริกซ์ 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)

เมทริกซ์ 5x3


คุณช่วยอธิบายสิ่งที่คุณต้องการสำหรับเมทริกซ์ที่ไม่ใช่สแควร์ได้ไหม? โซลูชันของคุณมี "ข้าม" จาก (2,1) ถึง (-2,1) - สิ่งนี้มีจุดประสงค์หรือไม่ [เช่นสำหรับเมทริกซ์ 7x3 มันจะมี "การกระโดด" เพิ่มอีกสองตัวและสำหรับเมทริกซ์ (2k + 1) x3 จะมีการกระโดด 2k-3 หรือเปล่า?
ShreevatsaR

3
ใช่การกระโดดนั้นเป็นการจงใจ ฉันได้อัปเดตคำถามด้วยภาพเมทริกซ์ 5x3 อย่างที่คุณเห็นจากภาพเรากำลังข้ามแถวบนและล่าง
Berk Güderสามารถ

ตกลงแล้วรหัสของคุณเองก็ดูดีที่สุด และถึงแม้ว่านี่จะเป็นเรื่องไม่แน่นอน: คุณสร้างภาพเหล่านั้นได้อย่างไร :)
ShreevatsaR

=)) ฉันไม่ได้สร้างพวกเขา ในความเป็นจริงวิธีที่ฉันสร้างพวกเขาค่อนข้างโง่ ฉันสร้างตารางใน OO.org Calc ถ่ายภาพหน้าจอและแก้ไขภาพหน้าจอใน GIMP =))
สามารถ Berk Güder

1
@Ying: ฉันไม่รู้จริง ๆ ว่าทำไมเพื่อนของฉันต้องการสิ่งนี้ แต่เขาบอกว่าเขาต้องการให้สมาชิกของเมทริกซ์เข้าใกล้ศูนย์กลางในอัลกอริทึมการค้นหา
Berk Güderสามารถ

คำตอบ:


63

นี่คือโซลูชันของฉัน (ใน 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
นี่เป็นวิธีที่ดีที่สุดในการเขียนเท่าที่ฉันเห็น การปรับปรุงที่เป็นไปได้เพียงอย่างเดียวคือทำให้ O (MN) แทนที่จะเป็น O (สูงสุด (M, N) ^ 2) โดยการข้ามผ่านโดยตรง (x, y) ที่ไม่ได้ถูกพิมพ์ แต่จะทำให้โค้ด น่าเกลียดขึ้นอีกเล็กน้อย
ShreevatsaR

ฉันเพิ่มประสิทธิภาพโซลูชันของฉันและใกล้เคียงกับสิ่งที่คุณมีอยู่แล้ว นี่เป็นทางออกที่ดีที่ฉันคิดว่า นอกจากคำแนะนำของ ShreevatsaR และสิ่งต่าง ๆ ที่ไม่คำนวณ x / 2 และ y / 2 ในแต่ละรอบการทำซ้ำไม่มีอะไรมากไปกว่าการปรับปรุงยกเว้นสไตล์
Triptych

โซลูชั่นใด ๆ สำหรับ matlab!
แซม

นี่เป็นการเชื่อมโยงแคชที่ดีสำหรับการเข้าถึงข้อมูลบัฟเฟอร์รูปภาพหรือไม่? (มีคำตอบมากมายที่นี่ แต่มีข้อมูลไม่มากนักที่เหมาะกับการใช้งานภาพประสิทธิภาพสูง)
ideasman42

@ ideasman42 - นั่นไม่ได้เข้ามาเล่นเพราะผลลัพธ์จะเป็นรูปแบบเกลียวของพิกัดเสมอ รูปแบบเกลียวเป็นแคชที่สอดคล้องกันหรือไม่ฉันคิดว่าจะขึ้นอยู่กับการใช้บัฟเฟอร์รูปภาพ (ฉันเดาว่ามันจะฟาดแคชมากกว่าวิธีอื่น ๆ ในการเดินภาพเช่นไปทีละบรรทัดตามลำดับ) แต่ทางเลือกของอัลกอริทึมในการสร้างพิกัดเหล่านี้อาจไม่ส่งผลกระทบต่อแคช
Raptormeat

31

C ++ ทุกคน? แปลอย่างรวดเร็วจาก python โพสต์เพื่อความสมบูรณ์

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

คุณยังสามารถใช้และ ds เหมือนฉันทำเพื่อตรวจหามุมที่ได้รับการกำจัดของใหญ่ถ้าเงื่อนไข
จอห์นลา Rooy

1
แก้ไขโพสต์นี้ได้รับการแนะนำที่นี่ แม้ว่าการแก้ไขจะถูกปฏิเสธเพราะมันเปลี่ยนความหมายของโพสต์ของคุณคุณอาจต้องการพิจารณารวมการเปลี่ยนแปลงที่แนะนำถ้ามันเหมาะสมที่จะทำ
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

มีวิธีแก้ปัญหาที่เสนอมามากมายสำหรับปัญหานี้เขียนในภาษาการเขียนโปรแกรมต่าง ๆ อย่างไรก็ตามพวกเขาทั้งหมดดูเหมือนจะเกิดจากวิธีการที่ซับซ้อนเช่นเดียวกัน ฉันจะพิจารณาปัญหาทั่วไปเพิ่มเติมของการคำนวณเกลียวซึ่งสามารถแสดงออกอย่างกระชับโดยใช้การเหนี่ยวนำ

เคสฐาน: เริ่มต้นที่ (0, 0), เดินไปข้างหน้า 1 ตาราง, เลี้ยวซ้าย, เดินไปข้างหน้า 1 ตาราง, เลี้ยวซ้าย ขั้นตอนอุปนัย: เลื่อนไปข้างหน้า n + 1 สี่เหลี่ยมเลี้ยวซ้ายมุ่งหน้าไปข้างหน้า n + 1 สี่เหลี่ยมเลี้ยวซ้าย

ความสง่างามทางคณิตศาสตร์ของการแสดงปัญหานี้ขอแนะนำอย่างยิ่งว่าควรมีอัลกอริทึมที่ง่ายในการคำนวณวิธีแก้ปัญหา โดยคำนึงถึงสิ่งที่เป็นนามธรรมฉันได้เลือกที่จะไม่ใช้อัลกอริทึมในภาษาการเขียนโปรแกรมเฉพาะ แต่เป็นการใช้โค้ดหลอก

ก่อนอื่นฉันจะพิจารณาอัลกอริทึมเพื่อคำนวณวนซ้ำ 2 ครั้งโดยใช้ลูป 4 คู่ โครงสร้างของแต่ละคู่มีความคล้ายคลึงกัน แต่แตกต่างกันในสิทธิของตนเอง นี่อาจดูบ้าในตอนแรก (บางลูปถูกเรียกใช้ครั้งเดียวเท่านั้น) แต่ทีละขั้นตอนฉันจะทำการแปลงจนกว่าเราจะมาถึงที่ลูป 4 คู่ที่เหมือนกันและด้วยเหตุนี้สามารถแทนที่ด้วยคู่เดียวที่อยู่ภายในลูปอื่น สิ่งนี้จะช่วยให้เรามีวิธีแก้ปัญหาทั่วไปในการคำนวณซ้ำโดยไม่ใช้เงื่อนไขใด ๆ

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

การเปลี่ยนแปลงครั้งแรกที่เราจะทำคือการแนะนำตัวแปรใหม่ d สำหรับทิศทางที่เก็บค่า +1 หรือ -1 ทิศทางจะสลับหลังจากลูปแต่ละคู่ เนื่องจากเรารู้ค่าของ d ทุกจุดเราสามารถคูณแต่ละด้านของความไม่เท่าเทียมกันแต่ละข้างโดยปรับทิศทางของความไม่เท่าเทียมกันและทำให้การคูณของ d เป็นค่าคงที่เป็นค่าคงที่อื่น สิ่งนี้ทำให้เรามีดังต่อไปนี้

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

ตอนนี้เราทราบว่าทั้ง x * d และ RHS เป็นจำนวนเต็มดังนั้นเราสามารถลบค่าจริงใด ๆ ระหว่าง 0 และ 1 จาก RHS โดยไม่ส่งผลต่อผลลัพธ์ของความไม่เท่าเทียมกัน เราเลือกที่จะลบ 0.5 จากความไม่เท่าเทียมกันของคู่อื่น ๆ ในขณะที่ลูปเพื่อสร้างรูปแบบมากขึ้น

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

ตอนนี้เราสามารถแนะนำตัวแปรอีก m สำหรับจำนวนขั้นตอนที่เราทำในแต่ละคู่ของลูป

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

ในที่สุดเราจะเห็นว่าโครงสร้างของแต่ละคู่ของลูปในขณะที่ลูปเหมือนกันและสามารถลดเป็นลูปเดี่ยวที่อยู่ภายในลูปอื่น นอกจากนี้เพื่อหลีกเลี่ยงการใช้ตัวเลขมูลค่าจริงฉันได้คูณค่าเริ่มต้นของ m ค่า m ถูกเพิ่มค่าโดย; และทั้งสองด้านของความไม่เท่าเทียมกัน 2

สิ่งนี้นำไปสู่การแก้ปัญหาที่แสดงในตอนต้นของคำตอบนี้


1
คำตอบสุดท้ายของคุณจะสิ้นสุดลงในเงื่อนไขใด
Merlyn Morgan-Graham

1
แอปพลิเคชั่นของการพิมพ์ลวดลายประเภทนั้นคืออะไร?
Ashish Shukla

1
@ MerlynMorgan-Graham มันจะหยุดทำงานเมื่อคอมพิวเตอร์ไม่มีหน่วยความจำหรือกำลังไฟเหลืออยู่
Mike

ดูเหมือนว่าความสง่างามของโซลูชันนั้นเกิดจากการละเว้นข้อ จำกัด ด้านเวลาและหน่วยความจำ ฉันขอแนะนำให้เพิ่มเงื่อนไขการยกเลิกอย่างหรูหรา (ถ้าเป็นไปได้) ฉันขอแนะนำให้ย้ายไปที่ด้านบนสุดของคำตอบและแสดงแหล่งที่มาด้านล่าง
Merlyn Morgan-Graham

1
ในขณะที่คำถามเริ่มแรกเกี่ยวกับเมทริกซ์ NxM นี่เป็นคำตอบที่มีประโยชน์จริง ๆ ถ้าคุณจำเป็นต้องหมุนวนออกไปเรื่อย ๆ จนกว่าคุณจะพบอะไรบางอย่าง แน่นอนเช่นเดียวกับความคิดเห็นอื่น ๆ ที่ระบุไว้คุณจำเป็นต้องกำหนดเงื่อนไขการเลิกจ้างนั้นหรือจะทำงานตลอดไป
cclogg

16

ต่อไปนี้เป็นวิธีการแก้ปัญหา O (1) เพื่อค้นหาตำแหน่งในเกลียวที่มีสอง: 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
ในการเริ่มต้นจากศูนย์เพิ่มสองบรรทัด if (n === 0) return [0, 0, r]; --n;ดู Fiddle: jsfiddle.net/Wishmesh/nwd9gt1s/2
Maris B.

15

ฉันรักเครื่องกำเนิดไฟฟ้าของหลาม

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

ทดสอบกับ:

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

คุณได้รับ:

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

ความพยายามหมุนวน "Code golf" ของ Java โดยอิงตามตัวแปร 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

นี่คือโซลูชัน C ++ ที่แสดงให้เห็นว่าคุณสามารถคำนวณพิกัดถัดไป (x, y) ได้โดยตรงและง่ายดายจากตำแหน่งก่อนหน้า - ไม่จำเป็นต้องติดตามทิศทางปัจจุบันรัศมีหรืออย่างอื่น:

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 แรกในเกลียว (โดยไม่มีข้อ จำกัด ของปัญหาดั้งเดิมของการกำบังไปยังภูมิภาค N x M) รหัสจะง่ายมาก:

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

เคล็ดลับคือคุณสามารถเปรียบเทียบ x และ y เพื่อกำหนดว่าคุณอยู่ด้านใดของสี่เหลี่ยมจัตุรัสและนั่นบอกคุณทิศทางที่จะย้ายเข้า


5

TDD ใน Java

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: อาจจะไม่ - แน่นอนไม่สั้นพอ - แต่ฉันคิดว่ามันเป็นการสาธิตที่ดีของ TDD และทำความสะอาดด้วยรหัสที่ถูกต้องเข้าใจง่ายเข้าใจง่าย ฉันจะทิ้งมันไว้
Carl Manaster

4

นี่คือทางออกของฉัน (ในทับทิม)

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

ยังคง O (สูงสุด (n, m) ^ 2) แต่สไตล์ดี
Triptych

1
direction = -direction แทนที่จะเป็น direction * = - 1? ถ้าคุณเล่นกอล์ฟ d = -d สั้นกว่า d * = - 1 ด้วย
John La Rooy

3

Haskell เลือกของคุณ:

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
กรุณาอย่าใช้สิ่งนี้เป็นคำพูดรุนแรงหรือความคิดเห็นของโทรลล์ แต่พระเจ้าทรงเป็นที่น่าเกลียด!
Petruza

1
ฉันไม่สามารถเห็นด้วยกับความคิดเห็นข้างต้นมากขึ้น
ส่อเสียด

แฮสเค็ลล์นี้ดูเท่มากสำหรับฉัน

1
ใช่ แต่โปรดสังเกตว่ามันแสดงออกได้อย่างไร เปรียบเทียบความยาวกับตัวอย่างอื่น ๆ ที่โพสต์ไว้ที่นี่
Robert Harvey

@ Petruza จริงๆแล้วมันไม่ใช่ทางออกที่ดีที่สุดใน Haskell ดูที่นี่: rosettacode.org/wiki/Spiral_matrix#Haskell
polkovnikov.ph

2

นี่คือใน C

ฉันบังเอิญเลือกชื่อตัวแปรที่ไม่ดี ในชื่อ T == ด้านบน, L == ซ้าย, B == ด้านล่าง, R == ด้านขวา ดังนั้น tli คือซ้ายบน i และ brj คือล่างขวา 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

ฉันมีโอเพนซอร์ซไลบรารี, pixelscanซึ่งเป็นไลบรารีหลามที่ให้ฟังก์ชั่นการสแกนพิกเซลบนกริดในรูปแบบเชิงพื้นที่ที่หลากหลาย รูปแบบพื้นที่รวมเป็นวงกลม, แหวน, กริด, งูและเดินสุ่ม นอกจากนี้ยังมีการแปลงต่าง ๆ (เช่นคลิปสลับหมุนแปล) ปัญหา OP ดั้งเดิมสามารถแก้ไขได้ดังนี้

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

ซึ่งให้คะแนน

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

เครื่องกำเนิดไฟฟ้าห้องสมุดและการเปลี่ยนแปลงสามารถถูกล่ามโซ่เพื่อเปลี่ยนคะแนนในคำสั่งที่หลากหลายและรูปแบบเชิงพื้นที่


2

นี่คือวิธีการแก้ปัญหาใน Python 3 สำหรับการพิมพ์จำนวนเต็มต่อเนื่องในรูปเกลียวตามเข็มนาฬิกาและทวนเข็มนาฬิกา

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

คำอธิบาย

เกลียวทำจากศูนย์กลางสี่เหลี่ยมเช่น 5x5 ตารางที่มีการหมุนตามเข็มนาฬิกามีลักษณะดังนี้:

 5x5        3x3      1x1

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

( >>>>>หมายถึง "ไป 5 ครั้งถูกต้อง" หรือเพิ่มดัชนีคอลัมน์ 5 ครั้งvหมายถึงลดลงหรือเพิ่มดัชนีแถว ฯลฯ )

สี่เหลี่ยมจัตุรัสทุกตัวมีขนาดเท่ากันฉันวนลูปสี่เหลี่ยมศูนย์กลาง

สำหรับแต่ละตารางรหัสมีสี่ลูป (หนึ่งสำหรับแต่ละด้าน) ในแต่ละวงเราเพิ่มหรือลดคอลัมน์หรือดัชนีแถว ถ้าiเป็นดัชนีแถวและดัชนีjคอลัมน์ตาราง 5x5 สามารถสร้างได้โดย: - การเพิ่มjจาก 0 เป็น 4 (5 ครั้ง) - เพิ่มขึ้นiจาก 1 เป็น 4 (4 ครั้ง) - ลดลงjจาก 3 เป็น 0 (4 ครั้ง) - การลดลงiจาก 3 เป็น 1 (3 ครั้ง)

สำหรับสี่เหลี่ยมถัดไป (3x3 และ 1x1) เราทำเช่นเดียวกัน แต่เลื่อนดัชนีเริ่มต้นและดัชนีสุดท้ายอย่างเหมาะสม ฉันใช้ดัชนีkสำหรับจตุรัสศูนย์กลางแต่ละแห่งมีสี่เหลี่ยมศูนย์กลาง n // 2 + 1

ในที่สุดคณิตศาสตร์สำหรับการพิมพ์แบบสวย ๆ

หากต้องการพิมพ์ดัชนี:

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

นี่คือ 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);
    }
  }

}

ตัวอย่างแรกของคำถาม (3x3) จะเป็น:

var coords = SpiralCoords.GenerateOutTo(1);

ตัวอย่างที่สองของคำถาม (5x3) จะเป็น:

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

1

นี่เป็นรุ่นที่แตกต่างออกไปเล็กน้อย - พยายามใช้recursionและiteratorsใน LUA ในแต่ละขั้นตอนโปรแกรมลงไปอีกภายในเมทริกซ์และลูป ฉันยังเพิ่มธงพิเศษเพื่อเกลียวหรือclockwise anticlockwiseเอาต์พุตเริ่มต้นจากมุมขวาล่างและวนซ้ำไปทางตรงกลาง

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

// การติดตั้ง 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

นี่คือโซลูชันวนซ้ำ JavaScript (ES6) สำหรับปัญหานี้:

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

นี่คือวิธีการใช้งาน:

spiralMatrix(0, 0, 1, 100);

สิ่งนี้จะสร้างเกลียวออกไปด้านนอกเริ่มต้นที่พิกัด (x = 0, y = 0) ด้วยขั้นตอนที่ 1 และจำนวนไอเท็มทั้งหมดเท่ากับ 100 การใช้งานจะเริ่มการเคลื่อนไหวตามลำดับต่อไปนี้ - ขึ้น, ขวา, ล่าง, ซ้าย.

โปรดทราบว่าการใช้งานนี้สร้างเมทริกซ์จตุรัส


1

นี่คือคำตอบใน Julia: วิธีการของฉันคือการกำหนดจุดในศูนย์กลางสี่เหลี่ยม ('เกลียว') รอบจุดกำเนิด(0,0)ซึ่งแต่ละตารางมีความยาวด้านข้างm = 2n + 1เพื่อสร้างพจนานุกรมที่เรียงลำดับด้วยหมายเลขสถานที่ตั้ง (เริ่มจาก 1 สำหรับต้นกำเนิด) เป็นคีย์ และพิกัดที่เกี่ยวข้องเป็นค่า

เนื่องจากสถานที่ตั้งสูงสุดต่อเกลียวที่(n,-n)ส่วนที่เหลือของจุดที่สามารถพบได้โดยเพียงแค่การทำงานย้อนกลับจากจุดนี้คือมุมล่างขวาโดยm-1หน่วยแล้วซ้ำสำหรับตั้งฉาก 3 ส่วนของm-1หน่วย

กระบวนการนี้เขียนขึ้นในลำดับย้อนกลับด้านล่างซึ่งสอดคล้องกับวิธีการหมุนวนของรายได้มากกว่ากระบวนการนับย้อนกลับนั่นคือส่วนra[right ascending] ลดค่าลงโดย3(m+1)จากนั้นla[left ascending] โดย2(m+1)และหวังว่านี่จะอธิบายตนเอง .

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

ดังนั้นสำหรับตัวอย่างแรกของคุณให้เสียบm = 3สมการเพื่อหา n ให้n = (5-1)/2 = 2และwalk(2)ให้พจนานุกรมเรียงลำดับของตำแหน่งไปยังพิกัดซึ่งคุณสามารถเปลี่ยนเป็นเพียงอาร์เรย์ของพิกัดโดยการเข้าถึง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)

โปรดทราบว่าสำหรับบางฟังก์ชั่น [เช่นnorm] มันสามารถดีกว่าที่จะปล่อยให้พิกัดในอาร์เรย์มากกว่าTuple{Int,Int}แต่ที่นี่ฉันเปลี่ยนพวกเขาเป็น tuples (x,y)- - ตามที่ร้องขอโดยใช้รายการความเข้าใจ

บริบทสำหรับ "สนับสนุน" ไม่ใช่ตารางเมทริกซ์ไม่ได้ระบุ (โปรดสังเกตว่าการแก้ปัญหานี้ยังคงคำนวณค่าปิดตาราง) แต่ถ้าคุณต้องการที่จะกรองเฉพาะช่วงxโดยy(ที่นี่สำหรับx=5, y=3) หลังจากการคำนวณเกลียวเต็ม แล้วแมทริกซ์นี้กับค่าจากintersectwalk

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

คำถามของคุณดูเหมือนคำถามที่เรียกว่าหน่วยความจำหมุนวน ในปัญหานั้นแต่ละตารางบนตารางจะถูกจัดสรรในรูปแบบเกลียวโดยเริ่มจากหมายเลข 1 ซึ่งหาตำแหน่งที่จุดกำเนิด แล้วนับเพิ่มขึ้นขณะวนออกไปด้านนอก ตัวอย่างเช่น:

17  16  15  14  13

18   5   4   3  12

19   6   1   2  11

20   7   8   9  10

21  22  23  ---->

โซลูชันของฉันสำหรับคำนวณพิกัดของแต่ละหมายเลขหลังจากรูปแบบเกลียวนี้โพสต์ไว้ด้านล่าง:

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

สิ่งนี้ขึ้นอยู่กับโซลูชันของคุณเอง แต่เราฉลาดขึ้นเกี่ยวกับการค้นหามุมต่างๆ สิ่งนี้ทำให้ง่ายต่อการดูว่าคุณจะข้ามพื้นที่ด้านนอกได้อย่างไรถ้า M และ N แตกต่างกันมาก

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

และโซลูชันที่ใช้เครื่องกำเนิดไฟฟ้าที่ดีกว่า O (สูงสุด (n, m) ^ 2) มันเป็น O (nm + abs (nm) ^ 2) เพราะข้ามแถบทั้งหมดหากไม่ได้เป็นส่วนหนึ่งของโซลูชัน

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

นี่เป็นวิธีแก้ปัญหาที่แย่มากของฉันทำจากความรู้ขั้นต่ำของ Java ที่นี่ฉันต้องวางหน่วยบนสนามในเกลียว ไม่สามารถวางหน่วยบนยอดของหน่วยอื่นหรือบนภูเขาหรือในมหาสมุทร

ต้องมีความชัดเจน. นี่ไม่ใช่ทางออกที่ดี นี่เป็นวิธีแก้ปัญหาที่แย่มากที่เพิ่มขึ้นเพื่อความสนุกสนานของคนอื่น ๆ ที่จะหัวเราะในสิ่งที่ทำได้ไม่ดี

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

ขอชื่นชมทุกคนที่อ่านสิ่งนี้ได้จริง

คำถามโบนัส: เวลาในการทำงานของ "อัลกอริทึม" นี้คืออะไร? : P


1
+1 เพราะ " นี่เป็นคำตอบที่แย่มากที่เพิ่มขึ้นเพื่อความสนุกสนานของคนอื่นที่จะหัวเราะว่ามันแย่ขนาดไหนที่ทำได้ "
Oriol

0

ทางออกสำหรับ 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

เมื่อเร็ว ๆ นี้ฉันมีความท้าทายที่คล้ายกันซึ่งฉันต้องสร้างอาร์เรย์ 2D และใช้อัลกอริธึมแบบเกลียวเมทริกซ์เพื่อเรียงลำดับและพิมพ์ผลลัพธ์ รหัส C # นี้จะทำงานกับอาร์เรย์ N, N 2D มันมีความชัดเจนและมีแนวโน้มที่จะสามารถนำกลับมาใช้ใหม่เพื่อให้เหมาะกับความต้องการของคุณ

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

ฉันทำสิ่งนี้กับเพื่อนที่ปรับหมุนวนเป็นอัตราส่วนภาพต่อบน Javascript ทางออกที่ดีที่สุดที่ฉันได้รับสำหรับพิกเซลวิวัฒนาการภาพทีละพิกเซลทำให้เต็มทั้งรูปภาพ

หวังว่าจะช่วยได้บ้าง

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

คุณสามารถดูได้ทำงานเกี่ยวกับhttp://jsfiddle.net/hitbyatruck/c4Kd6/ เพียงให้แน่ใจว่าได้เปลี่ยนความกว้างและความสูงของผืนผ้าใบบน javascript vars และบนคุณสมบัติใน HTML


0

เพื่อความสนุกสนานใน 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

รุ่น C # รองรับขนาดที่ไม่เป็นสี่เหลี่ยมเช่นกัน

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

ฉันกำลังแบ่งปันรหัสนี้ซึ่งฉันออกแบบมาเพื่อจุดประสงค์อื่น มันเกี่ยวกับการค้นหาหมายเลขคอลัมน์ "X" และหมายเลขแถว "Y" ขององค์ประกอบแถว @ ดัชนีดัชนี "เกลียว" ฟังก์ชันนี้ใช้ความกว้าง "w" และความสูง "h" ของเมทริกซ์และ "ดัชนี" ที่ต้องการ แน่นอนฟังก์ชั่นนี้สามารถใช้ในการผลิตผลลัพธ์ที่ต้องการเดียวกัน ฉันคิดว่ามันเป็นวิธีที่เร็วที่สุด (กระโดดข้ามเซลล์แทนที่จะสแกนพวกมัน)

    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

งูหลามบ่วงรหัสเกลียวตามเข็มนาฬิกาโดยใช้คำตอบ 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
มันตามเข็มนาฬิกา🔃และฉันก็อ้าง Can Berk Güder คำถามต้นฉบับสำหรับทวนเข็มนาฬิกา🔄 ฉันต้องการฟังก์ชั่นตามเข็มนาฬิกาดังนั้นฉันจึงรู้สึกว่ามีประโยชน์ที่จะออกจากที่นั่น
adrianmelic

0

โซลูชั่นที่ยอดเยี่ยมของ Davidont ใน 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.