สร้างการเรียงสับเปลี่ยน


16

บทนำ

สมมติว่าคุณได้รับการเปลี่ยนรูปแบบสุ่มของnวัตถุ การเรียงสับเปลี่ยนถูกผนึกไว้ในกล่องดังนั้นคุณจึงไม่รู้ว่าเป็นn!ไปได้ใด หากคุณพยายามที่จะใช้การเรียงสับเปลี่ยนกับnวัตถุที่แตกต่างคุณสามารถอนุมานตัวตนได้ทันที อย่างไรก็ตามคุณได้รับอนุญาตให้ใช้การเปลี่ยนแปลงกับnเวกเตอร์ไบนารี่แบบยาวซึ่งหมายความว่าคุณจะต้องใช้มันหลายครั้งเพื่อที่จะจดจำมัน เห็นได้ชัดว่านำไปใช้กับnเวกเตอร์ที่มีเพียง1งานเดียว แต่ถ้าคุณฉลาดคุณสามารถทำได้ด้วยlog(n)แอปพลิเคชัน รหัสสำหรับวิธีการนั้นจะยาวกว่า ...

นี่เป็นความท้าทายทดสอบที่คะแนนของคุณเป็นการรวมกันของความยาวของรหัสและความซับซ้อนของแบบสอบถามซึ่งหมายถึงจำนวนการโทรไปยังกระบวนการเสริม สเป็คค่อนข้างยาวดังนั้นทนกับฉัน

งาน

งานของคุณคือการเขียนฟังก์ชั่นที่ มีชื่อ (หรือเทียบเท่าที่ใกล้เคียงที่สุด)fที่ใช้เป็นอินพุตจำนวนเต็มบวกnและการเปลี่ยนแปลงpของnจำนวนเต็มแรกโดยใช้การทำดัชนีตาม 0 หรือ 1 pการส่งออกของมันคือการเปลี่ยนลําดับ แต่คุณจะไม่ได้รับอนุญาตให้เข้าถึงการเปลี่ยนลําดับpโดยตรง สิ่งเดียวที่คุณทำได้คือใช้มันกับเวกเตอร์ใด ๆ ของnบิต เพื่อจุดประสงค์นี้คุณจะต้องใช้ฟังก์ชั่นเสริมPที่ใช้เวลาในการเปลี่ยนแปลงpและเวกเตอร์ของบิตvและผลตอบแทนเวกเตอร์ permuted ซึ่งp[i]TH v[i]ประสานงานมีบิต ตัวอย่างเช่น:

P([1,2,3,4,0], [1,1,0,0,0]) == [0,1,1,0,0]

คุณสามารถแทนที่ "บิต" กับสองค่าที่แตกต่างใด ๆ เช่น3และ-4หรือ'a'และ'b'และพวกเขาไม่จำเป็นต้องได้รับการแก้ไขเพื่อให้คุณสามารถเรียกPกับทั้ง[-4,3,3,-4]และในสายเดียวกัน[2,2,2,1] fคำจำกัดความของPจะไม่ถูกนับรวมกับคะแนนของคุณ

เกณฑ์การให้คะแนน

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

ในพื้นที่เก็บข้อมูลนี้คุณจะพบไฟล์ชื่อpermutations.txtที่มี 505 permutations, 5 ของแต่ละความยาวระหว่าง 50 และ 150 รวมโดยใช้การทำดัชนีแบบ 0 (เพิ่มแต่ละหมายเลขในกรณีที่ใช้ 1) การเรียงสับเปลี่ยนแต่ละครั้งจะอยู่ในบรรทัดของตัวเองและตัวเลขจะถูกคั่นด้วยช่องว่าง คะแนนของคุณคือการนับ byte ของfความซับซ้อนแบบสอบถามเฉลี่ย + ปัจจัยเหล่านี้ คะแนนต่ำสุดชนะ

กฎพิเศษ

ต้องการรหัสที่มีคำอธิบายและไม่อนุญาตให้มีช่องโหว่มาตรฐาน โดยเฉพาะอย่างยิ่งแต่ละบิตแยกไม่ออก (ดังนั้นคุณไม่สามารถให้เวกเตอร์ของIntegerวัตถุPและเปรียบเทียบตัวตนของพวกเขา) และฟังก์ชั่นPจะส่งกลับเวกเตอร์ใหม่เสมอแทนที่จะจัดเรียงอินพุตใหม่ คุณสามารถเปลี่ยนชื่อfและPและลำดับในการโต้แย้ง

หากคุณเป็นคนแรกที่ตอบคำถามในการเขียนโปรแกรมภาษาของคุณขอแนะนำให้คุณรวมชุดทดสอบรวมถึงการใช้งานฟังก์ชั่นPที่นับจำนวนครั้งที่มีการเรียกใช้ ตัวอย่างเช่นนี่คือสายรัดสำหรับ Python 3

def f(n,p):
    pass # Your submission goes here

num_calls = 0

def P(permutation, bit_vector):
    global num_calls
    num_calls += 1
    permuted_vector = [0]*len(bit_vector)
    for i in range(len(bit_vector)):
        permuted_vector[permutation[i]] = bit_vector[i]
    return permuted_vector

num_lines = 0
file_stream = open("permutations.txt")
for line in file_stream:
    num_lines += 1
    perm = [int(n) for n in line.split()]
    guess = f(len(perm), perm)
    if guess != perm:
        print("Wrong output\n %s\n given for input\n %s"%(str(guess), str(perm)))
        break
else:
    print("Done. Average query complexity: %g"%(num_calls/num_lines,))
file_stream.close()

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


เราสามารถตีความ“ vector of bits” เป็น“ vector ของสองรายการที่แตกต่าง” ตัวอย่างเช่นภายใต้นิยามนี้ทั้งสองabaaabababaaและ-4 3 3 3 -4 3จะเป็น vector ของ bits
FUZxxl

@FUZxxl ใช่ตราบใดที่แต่ละรายการแยกไม่ออก
Zgarb

มันเป็นตัวเลขในแนวทางการนำไปปฏิบัติที่ฉันมี
FUZxxl

@FUZxxl ฉันแก้ไขสเป็ค
Zgarb

คำตอบ:


11

J, 44.0693 22.0693 = 37 15 + 7.06931

ถ้าเราไม่สามารถเรียกPได้ที่i. nเราสามารถที่โทรน้อยPบิตของแต่ละi. nแยก จำนวนการเรียกใช้Pคือ>. 2 ^. n(⌈log 2 n ⌉) ฉันเชื่อว่านี่เป็นสิ่งที่ดีที่สุด

f=:P&.|:&.#:@i.

นี่คือการดำเนินการของฟังก์ชั่นPที่ใช้เวกเตอร์การเปลี่ยนลําดับและบันทึกจำนวนสวดลงpPinv

P =: 3 : 0"1
 Pinv =: Pinv + 1
 assert 3 > # ~. y    NB. make sure y is binary
 p { y
)

นี่คือชุดควบคุมการทดสอบที่ได้รับการเปลี่ยนแปลงและส่งกลับจำนวนการร้องขอของp:

harness =: 3 : 0
 Pinv =: 0
 p =: y
 assert y = f # y     NB. make sure f computed the right permutation
 Pinv
)

และนี่คือวิธีที่คุณสามารถใช้กับไฟล์permutations.txt:

NB. average P invocation count
(+/ % #) harness@".;._2 fread 'permutations.txt'

คำอธิบาย

คำอธิบายสั้น ๆ มีให้แล้วข้างต้น แต่นี่เป็นคำอธิบายที่ละเอียดยิ่งขึ้น ก่อนอื่นfแทรกช่องว่างและเป็นฟังก์ชันที่ชัดเจน:

f =: P&.|:&.#:@i.
f =: 3 : 'P&.|:&.#: i. y'

อ่าน:

ให้fเป็น P ภายใต้การขนย้ายภายใต้การแสดงฐาน 2 ของอันแรกจำนวนเต็มy ตัว

โดยที่yคือพารามิเตอร์ทางการถึงf ใน J พารามิเตอร์เพื่อ (ฟังก์ชัน) เรียกว่าxและyถ้าคำกริยาคือ dyadic (มีสองพารามิเตอร์) และyถ้ามันเป็น monadic (มีพารามิเตอร์เดียว)

แทนการกล่าวอ้างPในi. n(เช่น0 1 2 ... (n - 1) ) เราเรียกในแต่ละตำแหน่งบิตของตัวเลขในP i. nเนื่องจากการเรียงสับเปลี่ยนทั้งหมดเปลี่ยนแปลงไปในทางเดียวกันเราสามารถประกอบบิตที่เรียงสับเปลี่ยนเป็นตัวเลขเพื่อให้ได้เวกเตอร์การเปลี่ยนแปลง:

  • i. y - จำนวนเต็มจาก 0y - 1ไป
  • #: y- yแสดงในฐาน 2 นี่คือการขยายไปยังเวกเตอร์ของตัวเลขในลักษณะที่เป็นธรรมชาติ ตัวอย่างเช่น#: i. 16อัตราผลตอบแทน:

    0 0 0 0
    0 0 0 1
    0 0 1 0
    0 0 1 1
    0 1 0 0
    0 1 0 1
    0 1 1 0
    0 1 1 1
    1 0 0 0
    1 0 0 1
    1 0 1 0
    1 0 1 1
    1 1 0 0
    1 1 0 1
    1 1 1 0
    1 1 1 1
    
  • #. y- yตีความว่าเป็นเลขฐาน 2 ยวดนี่คือสิ่งที่ตรงกันข้ามกับ#: ; y ~: #. #:มักจะถือ

  • |: y - yขนย้าย
  • u&.v y- uใต้vนั่นคือvinv u v yตำแหน่งที่vinvตรงกันข้ามกับvเป็นสิ่งที่ตรงกันข้ามไปสังเกตว่า|:มันเป็นสิ่งที่ตรงกันข้าม

  • P y- ฟังก์ชั่นที่Pใช้กับแต่ละเวกเตอร์ในyตามคำจำกัดความ


3

Pyth 32 + 7.06931 = 37.06931

ฉันพบว่าอัลกอริทึมต่อไปนี้เป็นอิสระอย่างสมบูรณ์ แต่มันก็เหมือนกันกับโซลูชั่น J สั้นมาก ๆ ของ FUZxxl (เท่าที่ฉันเข้าใจ)

ก่อนอื่นนิยามของฟังก์ชันPซึ่งอนุญาตให้บิตเรย์ตามการเปลี่ยนแปลงที่ไม่ทราบค่า

D%GHJHVJ XJ@HN@GN)RJ

และจากนั้นรหัสที่กำหนดการเปลี่ยนแปลง

Mmxmi_mbk2Cm%dHCm+_jk2*sltG]0GdG

ฟังก์ชันนี้กำหนดฟังก์ชันgซึ่งใช้สองอาร์กิวเมนต์ g5[4 2 1 3 0คุณสามารถเรียกมันโดย นี่คือการสาธิตออนไลน์การสาธิตออนไลน์ไม่เคยใช้แผนที่ซ้อนกันมากมาย (5)

Btw ฉันยังไม่ได้ทำการทดสอบจริง ฟังก์ชั่นไม่Pนับจำนวนครั้งที่ฉันเรียกมัน ฉันใช้เวลาไปกับการหาอัลกอริทึมแล้ว แต่ถ้าคุณอ่านคำอธิบายของฉันมันค่อนข้างชัดเจนว่ามันใช้การint(log2(n-1)) + 1โทร ( = ceil(log2(n))) และsum(int(log2(n-1)) + 1 for n in range(50, 151)) / 101.0 = 7.069306930693069.

คำอธิบาย:

ที่จริงฉันค่อนข้างยากที่จะหาอัลกอริทึมนี้ log(n)มันก็ไม่ได้ชัดเจนกับผมในทุกวิธีการเพื่อให้บรรลุ nดังนั้นผมจึงเริ่มทำการทดลองบางคนที่มีขนาดเล็ก

หมายเหตุแรก: บิตเรย์จะรวบรวมข้อมูลเดียวกับบิตอาเรย์เสริม ดังนั้นอาร์เรย์บิตทั้งหมดในโซลูชันของฉันจึงมีมากที่สุดn/2บิตที่แอ็คทีฟ

n = 3:

เนื่องจากเราสามารถใช้บิตอาเรย์กับ 1 บิตที่ใช้งานเท่านั้นทางออกที่ดีที่สุดขึ้นอยู่กับการเรียกสองครั้ง เช่นและP([1, 0, 0]) P([0, 1, 0])ผลการวิจัยบอกให้เราทราบถึงการเปลี่ยนแปลงครั้งแรกและครั้งที่สองโดยทางอ้อมเราได้อันดับที่สาม

n = 4:

นี่มันน่าสนใจนิดหน่อย ตอนนี้เราสามารถใช้บิตเรย์สองชนิด ผู้ที่มี 1 บิตที่ใช้งานและผู้ที่มี 2 บิตที่ใช้งาน ถ้าเราใช้บิตอาร์เรย์มีบิตที่ใช้งานหนึ่งเราจะรวบรวมข้อมูลเกี่ยวกับจำนวนหนึ่งของการเปลี่ยนแปลงและถอยกลับไปยังn = 3ที่เกิดในสายของ1 + 2 = 3 Pส่วนที่น่าสนใจคือเราสามารถทำสิ่งเดียวกันด้วยการโทรเพียง 2 ครั้งถ้าเราใช้บิต - อาเรย์กับ 2 บิตที่ใช้งานอยู่ เช่นและP([1, 1, 0, 0])P([1, 0, 1, 0])

สมมติว่าเราได้รับผลผลิตและ[1, 0, 0, 1] [0, 0, 1, 1]เราจะเห็นว่าบิตหมายเลข 4 นั้นทำงานในทั้งสองอาร์เรย์เอาต์พุต บิตเดียวที่มีบทบาทในการป้อนข้อมูลอาร์เรย์ทั้งสองเป็นจำนวนบิต 1 4ดังนั้นการเปลี่ยนแปลงอย่างเห็นได้ชัดเริ่มต้นด้วย ตอนนี้มันง่ายที่จะดูว่าบิต 2 ถูกย้ายไปที่บิต 1 (เอาต์พุตแรก) และบิต 3 ถูกย้ายไปเป็นบิต 3 (เอาต์พุตที่สอง) [4, 1, 3, 2]ดังนั้นการเปลี่ยนแปลงจะต้อง

n = 7:

ตอนนี้สิ่งที่ใหญ่กว่า ฉันจะแสดงสายของPทันที พวกเขาเป็นครั้งหนึ่งที่ฉันคิดขึ้นมาหลังจากคิดและทดลองเล็กน้อย (โปรดสังเกตว่าสิ่งเหล่านี้ไม่ใช่สิ่งที่ฉันใช้ในรหัสของฉัน)

P([1, 1, 1, 0, 0, 0, 0])
P([1, 0, 0, 1, 1, 0, 0])
P([0, 0, 1, 1, 0, 1, 0])

หากในสองอาร์เรย์แรกเอาต์พุต (และไม่ใช่ที่สาม) บิต 2 นั้นแอ็คทีฟเรารู้ว่าการเปลี่ยนรูปจะย้ายบิต 1 ถึงบิต 2 เนื่องจากบิตหนึ่งเป็นบิตเดียวที่แอ็คทีฟในสองอินพุตอาร์เรย์แรก

สิ่งสำคัญคือ (การตีความอินพุตเป็นเมทริกซ์) แต่ละคอลัมน์มีความเป็นเอกลักษณ์ สิ่งนี้ทำให้ฉันจำรหัส Hamming ได้ซึ่งประสบความสำเร็จในสิ่งเดียวกัน พวกเขาใช้ตัวเลข 1 ถึง 7 และใช้การแทนค่าบิตเป็นคอลัมน์ ฉันจะใช้ตัวเลข 0-6 ตอนนี้ส่วนที่ดีเราสามารถตีความผลลัพธ์ (คอลัมน์อีกครั้ง) เป็นตัวเลขอีกครั้ง [0, 1, 2, 3, 4, 5, 6]เหล่านี้บอกเราผลมาจากการเปลี่ยนแปลงนำไปใช้กับ

   0  1  2  3  4  5  6      1  3  6  4  5  0  2
P([0, 1, 0, 1, 0, 1, 0]) = [1, 1, 0, 0, 1, 0, 0]
P([0, 0, 1, 1, 0, 0, 1]) = [0, 1, 1, 0, 0, 0, 1]
P([0, 0, 0, 0, 1, 1, 1]) = [0, 0, 1, 1, 1, 0, 0]

ดังนั้นเราต้องย้อนกลับไปที่ตัวเลข บิต 0 สิ้นสุดลงในบิต 5 บิต 1 สิ้นสุดลงในบิต 0 บิต 2 สิ้นสุดลงในบิต 6 ... [5, 0, 6, 1, 3, 4, 2]ดังนั้นการเปลี่ยนแปลงได้

Mmxmi_mbk2Cm%dHCm+_jk2*sltG]0GdG
M                                 define a function g(G, H), that will return
                                  the result of the following computation:
                                  G is n, and H is the permutation. 
                m            G     map each k in [0, 1, ..., Q-1] to:
                  _                   their inverse
                   jk2                binary representation (list of 1s and 0s)
                 +                    extended with 
                      *sltG]0         int(log2(Q - 1)) zeros
               C                   transpose matrix # rows that are longer 
                                                   # than others are shortened
           m%dH                    map each row (former column) d of 
                                   the matrix to the function P (here %)
          C                        transpose back
   m                              map each row k to:                         
    i    2                           the decimal number of the 
     _mbk                            inverse list(k) # C returns tuple :-(
Let's call the result X.  
 m                             G   map each d in [0, 1, ..., Q - 1] to:
  x         X                 d       the index of d in X

และรหัสสำหรับฟังก์ชันการเรียงสับเปลี่ยน:

D%GHJHVJ XJ@HN@GN)RJ
D%GH                     def %(G, H):  # the function is called %
    JH                     J = copy(H)
      VJ         )        for N in [0, 1, ..., len(J) - 1]: 
         XJ@HN@GN            J[H[N]] = G[N]           
                  RJ      return J

1
หากคุณแทนที่*sltQ]0ด้วยm0sltQคุณสามารถมี 6 แผนที่ซ้อนกันที่ความยาวเท่ากัน
isaacg

คุณควรกำหนดรหัสของคุณที่สามารถแก้ไขความท้าทายให้กับฟังก์ชั่นที่ตั้งชื่อตามความท้าทายได้fแม้ว่าจะอนุญาตให้ใช้ชื่ออื่นได้ก็ตาม การบ้านจะนับรวมคะแนนของคุณ
FUZxxl

@FUZxxl อัปเดตรหัสของฉันแล้ว ตอนนี้กำหนดฟังก์ชั่นgแทนการอ่านจาก STDIN
Jakube

2

Mathematica, 63 + 100 = 163

ฉันใช้การเรียงสับเปลี่ยนแบบใช้ฐานเนื่องจากเป็นวิธีการจัดทำดัชนีใน Mathematica

ก่อนอื่นให้ทำการทดสอบเทียม นี่คือฟังก์ชันการสืบค้นp(ชื่อที่ผู้ใช้กำหนดไม่ควรเป็นตัวพิมพ์ใหญ่ใน Mathematica):

p[perm_, vec_] := (
   i += 1;
   vec[[Ordering@perm]]
   );

และการเตรียมการอินพุตพร้อมกับลูปการทดสอบ:

permutations = 
  ToExpression@StringSplit@# + 1 & /@ 
   StringSplit[Import[
     "https://raw.githubusercontent.com/iatorm/permutations/master/permutations.txt"
   ], "\n"];
total = 0;
(
    i = 0;
    result = f@#;
    If[# != result, 
      Print["Wrong result for ", #, ". Returned ," result ", instead."]
    ];
    total += i;
    ) & /@ permutations;
N[total/Length@permutations]

และสุดท้ายการส่งจริงของฉันซึ่งใช้อัลกอริทึมไร้เดียงสาสำหรับตอนนี้:

f=(v=0q;v[[#]]=1;Position[q~p~v,1][[1,1]])&/@Range@Length[q=#]&

หรือด้วยการเยื้อง:

f = (
     v = 0 q;
     v[[#]] = 1;
     Position[q~p~v, 1][[1, 1]]
) & /@ Range@Length[q = #] &
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.