ระยะทางสามเหลี่ยมแมนฮัตตัน


26

ระยะแมนฮัตตันบนตารางปกติเป็นจำนวนขั้นตอนฉากหนึ่งที่ต้องใช้เวลาในการเข้าถึงเซลล์หนึ่งจากที่อื่น ขั้นตอนมุมฉากเป็นขั้นตอนที่ผ่านขอบของเซลล์กริด (ตรงข้ามกับมุมซึ่งจะทำให้เราระยะทาง Chebyshev )

เราสามารถกำหนดระยะทางที่ใกล้เคียงกันบนกริดอื่น ๆ เช่นตารางสามเหลี่ยม เราสามารถระบุที่อยู่ของแต่ละเซลล์ในตารางด้วยชุดรูปแบบการทำดัชนีต่อไปนี้โดยที่แต่ละเซลล์มีx,yคู่:

    ____________________________________...
   /\      /\      /\      /\      /\
  /  \ 1,0/  \ 3,0/  \ 5,0/  \ 7,0/  \
 / 0,0\  / 2,0\  / 4,0\  / 6,0\  / 8,0\
/______\/______\/______\/______\/______\...
\      /\      /\      /\      /\      /
 \ 0,1/  \ 2,1/  \ 4,1/  \ 6,1/  \ 8,1/
  \  / 1,1\  / 3,1\  / 5,1\  / 7,1\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  /  \ 1,2/  \ 3,2/  \ 5,2/  \ 7,2/  \
 / 0,2\  / 2,2\  / 4,2\  / 6,2\  / 8,2\  
/______\/______\/______\/______\/______\...
\      /\      /\      /\      /\      /
 \ 0,3/  \ 2,3/  \ 4,3/  \ 6,3/  \ 8,3/
  \  / 1,3\  / 3,3\  / 5,3\  / 7,3\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  .  .    .  .    .  .    .  .    .  .
 .    .  .    .  .    .  .    .  .    .

ทีนี้ระยะทางแมนฮัตตันบนกริดนี้เป็นจำนวนก้าวเล็ก ๆ ข้ามขอบเพื่อรับจากเซลล์หนึ่งไปอีกเซลล์หนึ่ง ดังนั้นคุณจึงสามารถย้ายจาก3,1ไป2,1, 4,1หรือ3,2แต่ไม่ถึงสามเหลี่ยมอื่น ๆ เนื่องจากผู้ที่จะข้ามจุดมากกว่าขอบ

ตัวอย่างเช่นระยะห่างจาก2,1ถึง5,2คือ4เป็นโดยทั่วไปเส้นทางที่สั้นที่สุดนั้นไม่ซ้ำกัน แต่วิธีหนึ่งที่จะทำให้ระยะทางใน 4 ขั้นตอนคือ:

2,1 --> 3,1 --> 3,2 --> 4,2 --> 5,2

ความท้าทาย

รับสองคู่ประสานงานและx1,y1x2,y2จากโครงการที่อยู่ข้างต้นกลับระยะทางแมนฮัตตันระหว่างพวกเขา

คุณอาจสันนิษฐานว่าอินพุตทั้งสี่นั้นเป็นจำนวนเต็มไม่เป็นลบแต่ละตัวมีค่าน้อยกว่า 128 คุณสามารถรับได้ในลำดับใด ๆ และจัดกลุ่มตามอำเภอใจ (สี่อาร์กิวเมนต์แยกต่างหากรายการสี่จำนวนเต็มสองคู่จำนวนเต็ม 2x2 เมทริกซ์ .. .)

คุณสามารถเขียนโปรแกรมหรือฟังก์ชั่นและใช้วิธีการมาตรฐานใด ๆรับอินพุตและจัดเตรียมเอาต์พุต

คุณอาจใช้ภาษาการเขียนโปรแกรมใด ๆแต่โปรดทราบว่าช่องโหว่เหล่านี้เป็นสิ่งต้องห้ามตามค่าเริ่มต้น

นี่คือดังนั้นคำตอบที่สั้นที่สุดที่ถูกต้อง - วัดเป็นไบต์ - ชนะ

กรณีทดสอบ

กรณีทดสอบแต่ละคนจะได้รับเป็นx1,y1 x2,y2 => result

1,2 1,2 => 0
0,1 1,1 => 1
1,0 1,1 => 3
2,1 5,2 => 4
0,0 0,127 => 253
0,0 127,0 => 127
0,0 127,127 => 254
0,127 127,0 => 254
0,127 127,127 => 127
127,0 127,127 => 255
75,7 69,2 => 11
47,58 36,79 => 42
77,9 111,23 => 48
123,100 111,60 => 80
120,23 55,41 => 83
28,20 91,68 => 111
85,107 69,46 => 123
16,25 100,100 => 159
62,85 22,5 => 160
92,26 59,113 => 174
62,22 35,125 => 206

ช่องโหว่ที่ได้รับการจัดอันดับติดลบสุทธิรวมอยู่ในช่องโหว่ของทางการหรือไม่?
— DavidC

@DavidC ไม่จากคำถามของช่องโหว่: "[... ] ช่องโหว่ที่อธิบายไว้ในคำตอบใด ๆ ซึ่งอยู่ที่ +5 ขึ้นไปและมี upvotes อย่างน้อยสองเท่าของ downvotes อาจถูกมองว่าไม่เป็นที่ยอมรับต่อชุมชน "
— Martin Ender

เราได้รับอนุญาตให้รับอินพุตที่ห้าซึ่งเริ่มต้นที่ 0 โดยค่าเริ่มต้น (ผลลัพธ์) หรือไม่ จากนั้นฉันไม่จำเป็นต้องเพิ่ม(a,b,x,y)->c(a,b,x,y,0)(เรียกวิธีการแยกcด้วยสี่ข้อโต้แย้งและข้อโต้แย้ง0ที่ห้า) ในคำตอบของฉัน
— Kevin Cruijssen

3
@KevinCruijssen ไม่เสียใจ ข้อโต้แย้งคงที่เพิ่มเติมเป็นบิตใช้งานได้ง่ายเกินไป (และให้ 0 เป็นกรณีพิเศษดูเหมือนแปลก)
— Martin Ender

@ มาร์ตินเดอร์โอเคคิดอย่างนั้น แต่ก็ไม่สามารถทำร้ายถามได้ ในกรณีนี้คำตอบของฉันยังคงอยู่ 190 ไบต์ แม้ว่าฉันจะตอบได้ครึ่งปีที่แล้ว แต่กรณีทดสอบหนึ่งข้อล้มเหลว มาข้ามคำถามอีกครั้งตอนนี้และสามารถแก้ไขข้อบกพร่องในคำตอบของฉัน
— Kevin Cruijssen

คำตอบ:


7

JavaScript (ES6), 84 78 ไบต์

บันทึก 6 ไบต์ขอบคุณ Neil

(a,b,c,d,x=a>c?a-c:c-a,y=b>d?b-d:d-b,z=x>y?x:y)=>y+z+(x+z&1?a+b+(b>d)&1||-1:0)

กรณีทดสอบ

วิธีแก้ปัญหาแบบเรียกซ้ำครั้งแรก100 88 81

บันทึก 12 ไบต์ด้วย ETHproductions
บันทึก 7 ไบต์ขอบคุณ Neil

f=(a,b,c,d,e=b==d|a+b+(b>d)&1)=>a-c|b-d&&f(e?a+1-2*(a>c):a,e?b:b+1-2*(b>d),c,d)+1

มันทำงานอย่างไร

แม้ว่าจะยังคงใช้กับเวอร์ชันปัจจุบันเป็นหลัก แต่คำอธิบายต่อไปนี้จะอ้างถึงเวอร์ชันเริ่มต้นโดยเฉพาะ:

f=(a,b,c,d)=>b-d?a+b+(b>d)&1?f(a+1-2*(a>c),b,c,d)+1:f(a,b+1-2*(b>d),c,d)+1:Math.abs(a-c)

การเปลี่ยนจาก(x0, y)ถึง(x1, y)นั้นไม่สำคัญเพราะเราสามารถข้ามขอบด้านข้างได้ตลอดจากสามเหลี่ยมแหล่งที่มาจนถึงเป้าหมาย ระยะทางแมนฮัตตันในกรณีนี้คือ| x0 - x1 |.

ส่วนที่ยุ่งยากคือขั้นตอนในแนวตั้ง หากต้องการเปลี่ยนจากแถวy0เป็นแถวy1เราต้องคำนึงถึงพารามิเตอร์ทั้งสองนี้:

  • การวางแนวของสามเหลี่ยมปัจจุบัน
  • ไม่ว่าy0น้อยกว่าหรือมากกว่าy1

การวางแนวของรูปสามเหลี่ยมถูกกำหนดโดยความเท่าเทียมกันของx + y :

  • ถ้ามันเท่ากันสามเหลี่ยมนั้นจะชี้ขึ้น
  • ถ้ามันแปลกสามเหลี่ยมจะชี้ลง

เราสามารถลงจากสามเหลี่ยมชี้ขึ้น (มีประโยชน์เมื่อy0 <y1 ) และขึ้นจากสามเหลี่ยมชี้ลง (มีประโยชน์เมื่อy0> y1 )

โดยการรวมการวางแนวของรูปสามเหลี่ยมกับการเปรียบเทียบระหว่างy0และy1เราจะได้สูตรx + y0 + (y0> y1? 1: 0)ซึ่งมีผลลัพธ์แม้ว่าเราสามารถไปในทิศทางที่ต้องการและแปลกถ้าไม่ได้

หากเราไม่สามารถเข้าถึงแถวถัดไปได้โดยตรงอันดับแรกเราจำเป็นต้องได้รับการจัดตำแหน่งที่ถูกต้องโดยการอัพเดตx :

  • ถ้าxยังไม่เท่ากับx1เราต้องการเคลื่อนที่ในทิศทางที่ถูกต้องแน่นอนดังนั้นเราจึงเพิ่มมันถ้าxน้อยกว่าx1และเราลดมันลงถ้าxมากกว่าx1
  • ถ้าxเท่ากับx1แล้วเราสามารถเพิ่มหรือลดลงได้

กรณีทดสอบ


นั่นคือ ... การดำเนินการทางคณิตศาสตร์ที่มีขนาดเล็กมาก ... แต่คุณไม่สามารถข้ามnตัวแปรทั้งหมดและเพียงเพิ่ม 1 เข้าไปในผลลัพธ์ของการวนซ้ำแต่ละครั้งได้หรือไม่ ( ฉันคิดว่า90 ตัวอักษร )
— ETHproductions

@ETHproductions พูดตามตรงฉันโพสต์มันโดยไม่ต้องเล่นกอล์ฟอย่างจริงจัง แต่นั่นเป็นสิ่งแรกที่ต้องทำ ขอบคุณ!
— Arnauld

1
นอกจากนี้ฉันคิดว่าตัวดำเนินการลำดับความสำคัญของ&วิธีการที่คุณสามารถทำได้a+b+(b>d)&1เพื่อประหยัด 2 ไบต์
— ETHproductions

รับลงไปที่ 81 ฉันคิดว่า:f=(a,b,c,d,e=b==d|a+b+(b>d)&1)=>a-c|b-d&&f(e?a+1-2*(a>c):a,e?b:b+1-2*(b>d),c,d)+1
— นีล

ฉันคิดว่าอาจเป็นไปได้ที่จะบันทึกไบต์อื่นโดยใช้การแกงอย่างฉลาด
— Neil

5

Python 2, 74 ไบต์

lambda x,y,X,Y:abs(y-Y)+max(x-X,X-x,abs(y-Y)+((x+y+X+Y)%-2)**(x^y^(Y>=y)))

1
คุณกรุณาอธิบายส่วนนี้ได้**(x^y^(Y>=y))ไหม
— Dead Possum

1
@DeadPossum การเคลื่อนที่ด้วยระยะทาง 1 ในแนวตั้งสามารถทำได้ 1 หรือ 3 การเคลื่อนไหว ไม่มีวิธีที่จะบอกได้โดยดูที่ความเท่าเทียมดังนั้นคุณต้องเปรียบเทียบค่า y
— feersum

2

แบตช์ 99 ไบต์

@cmd/cset/a"x=%3-%1,x*=x>>31|1,y=%4-%2,w=y>>31,y*=w|1,z=x+(y+x&1)*(-(%1+%2+w&1)|1)-y,z*=z>>31,x+y+z

คำอธิบาย: การเคลื่อนไหวเฉพาะขอบฟ้าเพียงแค่นำความแตกต่างของพิกัด x แบบสัมบูรณ์ สำหรับขนาดใหญ่พอ x การเคลื่อนที่ในแนวตั้งจะใช้ขั้นตอนพิเศษเพียงหนึ่งก้าวต่อความแตกต่างพิกัด y แบบสัมบูรณ์ แต่สำหรับ x ขนาดเล็กจะใช้ขั้นตอนพิเศษสี่ขั้นต่อความแตกต่างพิกัด y สองบวกหนึ่งหรือสามขั้นตอนสำหรับความแตกต่างแปลก ๆ นี่คือ calcluated สองขั้นตอนต่อความแตกต่างบวกปัจจัยแก้ไข ขนาดใหญ่กว่าของสองขั้นตอนที่ถูกแก้ไขและผลรวมของความแตกต่างสัมบูรณ์คือผลลัพธ์แม้ว่านี่จะคำนวณเองว่าเป็นความแตกต่างของพิกัด y สัมบูรณ์ที่ถูกต้องที่แก้ไขมากขึ้นและระยะทางพิกัด x สัมบูรณ์ที่เพิ่มเข้ามา .

  • @cmd/cset/a" - หาค่านิพจน์คั่นด้วยเครื่องหมายจุลภาคและพิมพ์นิพจน์สุดท้าย
  • x=%3-%1,x*=x>>31|1คำนวณx=|x2−x1|
  • y=%4-%2,w=y>>31,y*=w|1คำนวณและy = | y 2 - y 1 |w=y1>y2y=|y2−y1|
  • z=x+(y+x&1)*(-(%1+%2+w&1)|1)-yปัจจัยการแก้ไขc=(y+(xmod2))(1−2((x1+y1+w)mod2)),z=x+c−y
  • z*=z>>31,x+y+zคำนวณmax(x,y−c)+y=x+y−min(0,x+c−y)

2

เยลลี่ 24 ไบต์

⁴<³¬Ḋ;³S
SḂN*¢+ḊḤ$
ạµS»Ç

ลองออนไลน์!

ลองเรียกอินพุต(x,y),(X,Y) )ฉันทำงานจากสูตรของ feersum:

d=|y−Y|+max(|x−X|,|y−Y|+((x+y+X+Y)mod−2)x⊕y⊕(Y≥y))=|y−Y|+max(|x−X|,|y−Y|+[−(|x−X|+|y−Y|mod2)]x+y+(Y≮y))=max(|x−X|+|y−Y|,2|y−Y|+[−(|x−X|+|y−Y|mod2)](Y≮y)+x+y).

บรรทัดแรกคำนวณ¢=(Y≮y)+x+yเลขชี้กำลังในสูตร

บรรทัดสุดท้ายคำนวณครั้งแรกแล้วคำนวณค่าสูงสุดของผลรวม( L )และf ( L )โดยที่fคือฟังก์ชันบนเส้นกลางL=[|x−X|,|y−Y|]sum(L)f(L)f

สายกลางที่ได้รับคำนวณ- ( ( + ข) mod 2 )ใช้เวลานั้นไปยัง¢อำนาจ, th แล้วเพิ่ม2ขL=[a,b]−((a+b)mod2)¢2b


2

แร็กเกต / โครงร่าง, 214 ไบต์

(define(f x y X Y)(let m((p x)(q y)(c 0))
(let((k(+ c 1))(d(- Y q)))
(cond((= 0(- X p)d)c)
((and(> d 0)(even?(+ p q)))(m p(+ q 1)k))
((and(< d 0)(odd?(+ p q)))(m p(- q 1)k))
((< p X)(m(+ p 1)q k))
(else(m(- p 1)q k))))))

2

05AB1E , 24 ไบต์

(x1,x2),(Y1,Y2). แก้ไขข้อผิดพลาดสำหรับ +1 ไบต์แล้วแก้ไขข้อผิดพลาดอีกครั้งสำหรับ +1 แต่ให้ผลลัพธ์ที่ถูกต้องสำหรับกรณีทดสอบทั้งหมด ...

ÆÄ`©I˜OÉ(IøнOIθD{Q+m+M®+

ลองออนไลน์!

ทำให้พังถล่ม

©Ä`© I˜OÉ (IøнOIθD {Q + m + M® + โปรแกรมเต็มรูปแบบฉันหมายถึงอินพุตที่ได้รับการประเมิน
ReduceÄลดคู่ด้วยการลบเอาค่าสัมบูรณ์
  `©ถ่ายโอนข้อมูลแยกกันลงบนสแต็กและเก็บวินาที
                            หนึ่ง | | y1-y2 | ในทะเบียน C
    I Push กดผลรวมของอินพุตที่แบนเข้าสู่สแต็ก
       É (ใช้ความเสมอภาคและปฏิเสธ
         Iøн                Push [x1, y1].
            O               Take x1+y1 (sum them).
             IθD{Q          Then check if the second pair is sorted (y1 ≤ y2).
                  +         And sum that with x1+y1.
                   m        Exponentiate. Push the parity above ** the result.
                    +       And add the second absolute difference to that.
                     M®+    As a result, push the largest number on the stack
                            plus the value stored in register C.

I'm not 100% sure, but can't you change the © to D and remove the ®? It seems to work for the case currently in your TIO, but I'm not sure if it follows the same path for every case.
— Kevin Cruijssen

1
@KevinCruijssen EDIT: No, because M's behaviour would be affected by this. Fails for [[0, 127], [0, 0]].
— Mr. Xcoder

2

Python 2, 74 72 71 bytes

lambda c,a,d,b:abs(a-b)+abs(a+(-c-a)/2-b-(-d-b)/2)+abs((c+a)/2-(d+b)/2)

Try it online! Link includes test cases. Edit: Saved 2 bytes thanks to @JoKing. Saved a further byte thanks to @Mr.Xcoder. Based on the following formula I found in this question:

|ai−bi|+|(ai−⌊aj2⌋)−(bi−⌊bj2⌋)|+|⌊aj+12⌋−⌊bj+12⌋|

The coordinate systems differ in three ways; the coordinates are exchanged (which explains my somewhat strange parameter name order), the coordinates are angled at 120∘ rather than 90∘ (which explains the two additions) and the coordinates in the linked question use inferior 1-indexing. Since we're taking differences this cancels out most of the time and we are left with:

|ai−bi|+|(ai−⌊aj+12⌋)−(bi−⌊bj+12⌋)|+|⌊aj2⌋−⌊bj2⌋|

This can then be golfed by noting that −⌊aj+12⌋=⌊−aj2⌋.


You can make it a one-liner by removing the newline
— Jo King

1
lambda c,a,d,b:abs(a-b)+abs(a+-(c+a)/2-b--(d+b)/2)+abs((c+a)/2-(d+b)/2) should save 3 bytes.
— Mr. Xcoder

1

Pyth, 31 28 bytes

Uses roughly the same approach as in feersum's Python answer. Takes input as a list of pairs of coordinates, (x1,x2),(y1,y2). Fixed a bug for -1 byte.

+eKaMQg#hK+eK^%ssQ_2+shCQSIe

Try it here! or Try the test suite!

Breakdown

+eKaMQg#hK+eK^%ssQ_2xxFhCQSIe     Full program. Q = eval(input()).
  KaMQ                            Store the differences [|x1-x2|, |y1-y2|] in K.
 e                                Retrieve the latter (|y1-y2|).
+     g#                          And add it to the greatest value between:
        hK                          - The head of K (|x1-x2|)
          +                         - And the result of adding:
           eK                           The end of K (|y1-y2|).
             ^                      - with the result of exponentiating:
              %ssQ_2                    The sum of the flattened Q, modulo -2.
                                        Yields -1 if x1+x2+y1+y2 is odd, 0 otherwise.
                    xxFhCQSIe       - by the result of this expression:
                       hCQ              Transpose Q and get the head (x1, y1).
                     xF                 Reduce by bitwise XOR.
                          SIe           And check if the list [y1, y2] is sorted.
                    x                   After which, xor the result by the bool (0/1).

1

05AB1E, 16 bytes

Uses a modified version of Neil's answer, optimised for stack-based languages like 05AB1E. Takes input as two pairs of coordinates, (x1,x2),(y1,y2), separated by a newline from STDIN. Initially I merged that with my other 05AB1E answer but then decided to post it separately because it's very, very different.

+D(‚2÷Æ`²Æ©+®)ÄO

Try it online! or Try the test suite! (Uses a slightly modified version of the code (® instead of ²), courtesy of Kevin Cruijssen)


Nice answer! Not something to golf, but when you change ©+® to DŠ+ it's easier to set up a test suite. ;) Here is that test suite, and all test cases are indeed succeeding (ignore the messy header ;p).
— Kevin Cruijssen

@KevinCruijssen I had that as an alternate version, but it didn't occur to me that I could write a test suite... Thanks, I'll add it
— Mr. Xcoder

1
@KevinCruijssen I golfed off two more (very obvious...!) bytes, and succeeded breaking the test suite compatibility even more, so I kept it as-is :P Thanks for the edit, by the way.
— Mr. Xcoder


1

Java 8, 157 190 188 144 142 141 127 bytes

(a,b,x,y)->{int r=0,c=1,z=1;for(;(c|z)!=0;r--){c=x-a;z=y-b;if((z<0?-z:z)<(c<0?-c:c)|a%2!=b%2?z<0:z>0)b+=z<0?-1:1;else a+=c<0?-1:1;}return~r;}

+33 bytes (157 → 190) due to a bug fix.
-44 bytes (188 → 144) converting the recursive method to a single looping method.
-14 bytes thanks to @ceilingcat.

Explanation:

Try it here.

(a,b,x,y)->{          // Method with four integers as parameter and integer return-type
                      // (a=x1; b=y1; x=x2; y=y2)
  int r=0,            //  Result-integer `r`, starting at 0
      c=1,z=1;        //  Temp integers for the differences, starting at 1 for now
  for(;(c|z)!=0;      //  Loop until both differences are 0
      r--){           //    After every iteration: decrease the result `r` by 1
    c=x-a;            //   Set `c` to x2 minus x1
    z=y-b;            //   Set `z` to y2 minus y1
    if(z*Z            //   If the absolute difference between y2 and y1
       <c*c)          //   is smaller than the absolute difference between x2 and x1
       |a%2!=b%2?     //   OR if the triangle at the current location is facing downwards
         z<0          //       and we have to go upwards,
        :z>0)         //      or it's facing upwards and we have to go downwards
      b+=z<0?-1:1;    //    In/decrease y1 by 1 depending on where we have to go
    else              //   Else:
     a+=c<0?-1:1;}    //    In/decrease x1 by 1 depending on where we have to go
  return~r;           //  Return `-r-1` as result

1
Suggest z*z<c*c instead of (z<0?-z:z)<(c<0?-c:c)
— ceilingcat

@ceilingcat Ah, nice one. Thanks!
— Kevin Cruijssen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.