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


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=|x2x1|
  • y=%4-%2,w=y>>31,y*=w|1คำนวณและy = | y 2 - y 1 |w=y1>y2y=|y2y1|
  • z=x+(y+x&1)*(-(%1+%2+w&1)|1)-yปัจจัยการแก้ไขc=(y+(xmod2))(12((x1+y1+w)mod2)),z=x+cy
  • z*=z>>31,x+y+zคำนวณmax(x,yc)+y=x+ymin(0,x+cy)

2

เยลลี่ 24 ไบต์

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

ลองออนไลน์!

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

d=|yY|+max(|xX|,|yY|+((x+y+X+Y)mod2)xy(Yy))=|yY|+max(|xX|,|yY|+[(|xX|+|yY|mod2)]x+y+(Yy))=max(|xX|+|yY|,2|yY|+[(|xX|+|yY|mod2)](Yy)+x+y).

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

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

สายกลางที่ได้รับคำนวณ- ( ( + ) mod 2 )ใช้เวลานั้นไปยัง¢อำนาจ, th แล้วเพิ่ม2L=[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:

|aibi|+|(aiaj2)(bibj2)|+|aj+12bj+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:

|aibi|+|(aiaj+12)(bibj+12)|+|aj2bj2|

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.