Permütasyonu Yeniden Yapılandırma


16

Giriş

Farz edelim ki, nnesnelerin rastgele bir permütasyonu verildi . Permütasyon bir kutu içinde mühürlenmiştir, bu yüzden n!olası olanlardan hangisi olduğu hakkında hiçbir fikriniz yoktur . Permütasyonu nfarklı nesnelere uygulamayı başarırsanız, hemen kimliğini çıkarabilirsiniz. Bununla birlikte, permütasyonu sadece uzunlamasına nvektörlere uygulamanıza izin verilir , yani onu tanımak için birkaç kez uygulamanız gerekir. Açıkça, bunu nsadece biriyle vektörlere uygulamak 1işi yapar, ancak zekiyseniz log(n)uygulamalarla yapabilirsiniz. Yine de bu yöntemin kodu daha uzun olacak ...

Bu, puanınızın kod uzunluğu ve sorgu karmaşıklığının bir kombinasyonu olduğu , yardımcı bir prosedüre yapılan çağrıların sayısı olduğu deneysel bir sorundur . Spesifikasyon biraz uzun, bu yüzden bana katlan.

Görev

Göreviniz girdi olarak pozitif bir tamsayı ve ilk tamsayıların permütasyonunu 0 tabanlı veya 1 tabanlı indeksleme kullanarak alan adlandırılmış bir işlev (veya en yakın eşdeğeri) yazmaktır . Çıktısı permütasyon . Ancak, permütasyona doğrudan erişmenize izin verilmez . Bununla yapabileceğiniz tek şey, onu herhangi bir bit vektörüne uygulamaktır . Bu amaçla, bir permütasyon ve bir bit vektörü alan ve koordinatı biti içeren izin verilen vektörü döndüren yardımcı bir fonksiyon kullanmalısınız . Örneğin:fnpnppnPpvp[i]v[i]

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

Aşağıdaki gibi herhangi iki ayrı değerlere sahip "bit" yerine 3ve -4ya 'a've 'b'çağrı böylece onlar sabit gerekmez ve Pher ikisi ile [-4,3,3,-4]ve [2,2,2,1]aynı çağrısında f. Tanımı, Ppuanınıza dahil edilmez.

puanlama

Sorgu karmaşıklığı Belirli bir girdi üzerinde çözümün bu yardımcı işlevine yapar aramaların sayısıdır P. Bu tedbiri açık hale getirmek için çözümünüz deterministik olmalıdır. Yalancı rasgele oluşturulmuş sayılar kullanabilirsiniz, ancak daha sonra jeneratör için bir başlangıç ​​tohumunu da düzeltmelisiniz.

Olarak , bu depo adlı bir dosya bulacaksınız permutations.txt0 tabanlı dizin (1 tabanlı bir durumda her numarası artırmak) kullanılarak 505 permütasyon, 50 ve 150 dahil bunlar arasında her uzunlukta 5 ihtiva etmektedir. Her permütasyon kendi satırındadır ve sayıları boşluklarla ayrılmıştır. Puanınız, bu girdilerdeki + ortalama sorgu karmaşıklığının bayt sayısıdırf . En düşük puan kazanır.

Ek Kurallar

Açıklamalı kod tercih edilir ve standart boşluklara izin verilmez. Özellikle, ayrı bitler ayırt edilemez (bu nedenle Integernesnelerin bir vektörünü veremez Pve kimliklerini karşılaştıramazsınız) ve işlev P, girişini yeniden düzenlemek yerine her zaman yeni bir vektör döndürür. Ve öğelerinin adlarını fve Pargümanlarını alma sırasını serbestçe değiştirebilirsiniz .

Programlama dilinizde ilk cevap veren kişi sizseniz, işlevinin Pçağrılma sayısını da sayan bir uygulama da dahil olmak üzere bir test takımı eklemeniz önemle tavsiye edilir . Örnek olarak, Python 3'ün koşum takımı.

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

Bazı dillerde, böyle bir koşum takımı yazmak imkansızdır. En önemlisi, Haskell saf işlevin Pkaç kez çağrıldığını kaydetmesine izin vermez . Bu nedenle, çözümünüzü sorgu karmaşıklığını da hesaplayacak şekilde yeniden uygulamanıza ve bunu kablo demetinde kullanmanıza izin verilir.


“Bit vektörü” nü, örneğin bu tanım kapsamında “iki farklı öğenin vektörü” olarak yorumlayabilir miyiz abaaabababaave -4 3 3 3 -4 3bir bit vektörü olur.
FUZxxl

@FUZxxl Evet, her bir öğe ayırt edilemez olduğu sürece.
Zgarb

Bunlar benim sahip olduğum uygulama yaklaşımının sayıları.
FUZxxl

@FUZxxl Özellikleri düzenledim.
Zgarb

Yanıtlar:


11

J, 44.0693 22.0693 = 37 15 + 7.06931

Dediğimiz yapamıyorsanız Püzerinde i. n, biz az çağrının en can Pher bit i. nayrı ayrı. Arasında çağrıları sayısı Polduğu >. 2 ^. n(⌈log 2 n ⌉). Bunun en uygun olduğuna inanıyorum.

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

Burada Ppermütasyon vektörünü kullanan pve invokasyon sayısını kaydeden fonksiyonun bir uygulaması bulunmaktadır Pinv.

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

İşte bir permütasyon alan ve aşağıdakilerin çağrılma sayısını döndüren bir test takımı p:

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

Ve dosyada nasıl kullanabileceğiniz aşağıda açıklanmıştır permutations.txt:

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

açıklama

Kısa açıklama yukarıda verilmiştir, ancak burada daha ayrıntılı bir açıklama bulunmaktadır. İlk olarak, fboşluklar eklenmiş ve açık bir işlev olarak:

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

oku:

Let f birinci baz-2 gösterimi altında aktarılması altında olan P y tamsayı.

burada y , f'nin resmi parametresidir . J, bir (fonksiyon) parametreleri olarak adlandırılır x ve y fiil diyadik (iki parametre vardır) ve eğer Y bu monadik ise (bir parametre vardır).

Bunun yerine çağırmanın Püzerine i. n(yani 0 1 2 ... (n - 1)), biz çağırmak Psayıların her bit pozisyonuna i. n. Tüm permütasyonlar aynı şekilde izin verdiğinden, permütasyonlu bitleri bir permütasyon vektörü elde etmek için sayılara yeniden monte edebiliriz:

  • i. y- dan tamsayılar 0için y - 1.
  • #: y- ytaban 2'de temsil edilir. Bu, doğal olarak sayı vektörlerine genişletilir. Örneğin, #: i. 16verim:

    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- ytemel 2 sayısı olarak yorumlanır. Özellikle, bunun tersi #:; y ~: #. #:hep tutar.

  • |: y- yaktarılmış.
  • u&.v y- ualtında v, yani vinv u v ynerede vinvtersi olan v. |:Kendi tersine dikkat edin .

  • P y- tanımdaki Pher vektöre uygulanan işlev y.


3

Pyth 32 + 7.06931 = 37.06931

Aşağıdaki algoritmayı tamamen bağımsız buldum. Ama bu FUZxxl çok kısa J çözümü ile hemen hemen aynı şey (anladığım kadarıyla).

İlk olarak, Pbilinmeyen bir permütasyona göre bir bit dizisine izin veren fonksiyonun tanımı .

D%GHJHVJ XJ@HN@GN)RJ

Ve sonra permütasyonu belirleyen kod.

Mmxmi_mbk2Cm%dHCm+_jk2*sltG]0GdG

Bu, giki argüman alan bir işlevi tanımlar . Buna göre diyebilirsiniz g5[4 2 1 3 0. İşte çevrimiçi bir gösteri . Hiç bu kadar çok (5) iç içe harita kullanmadım.

Ama aslında bir test kemeri bile yapmadım. İşlev, Pkaç kez çağırdığımı da saymaz. Algoritmayı bulmak için çok zaman harcadım. Ancak açıklamamı okursanız, int(log2(n-1)) + 1aramalar ( = ceil(log2(n))) kullandığı oldukça açıktır . Ve sum(int(log2(n-1)) + 1 for n in range(50, 151)) / 101.0 = 7.069306930693069.

Açıklama:

Aslında bu algoritmayı bulmakta oldukça zorlandım. Bana nasıl ulaşılacağı belli değildi log(n). Böylece küçük deneyler yapmaya başladım n.

İlk not: Bir bit dizisi, tamamlayıcı bit dizisiyle aynı bilgileri toplar. Bu nedenle, çözümümdeki tüm bit dizilerinin en fazla n/2aktif biti vardır.

n = 3:

Yalnızca 1 aktif bitli bit dizisini kullanabileceğimiz için, en uygun çözüm iki çağrıya bağlıdır. Örneğin P([1, 0, 0])ve P([0, 1, 0]). Sonuçlar bize permütasyonun birinci ve ikinci sayısını gösterir, dolaylı olarak üçüncü olanı alırız.

n = 4:

İşte biraz ilginç. Şimdi iki tür bit dizisi kullanabiliriz. 1 aktif biti olanlar ve 2 aktif biti olanlar. Biz bir aktif bit ile bir bit-dizi kullanırsanız, sadece permütasyon biri sayısı hakkında bilgi toplamak ve geri düşmek n = 3sonuçlanan 1 + 2 = 3çağrılarına P. İlginç olan, aynı şeyi sadece 2 çağrı ile yapabilmemizdir, eğer 2 aktif bitli bit dizileri kullanırsak. Örneğin P([1, 1, 0, 0])ve P([1, 0, 1, 0]).

Diyelim ki çıktıları alıyoruz [1, 0, 0, 1]ve [0, 0, 1, 1]. Görüyoruz ki, bit sayısı 4 her iki çıkış dizisinde de aktiftir. Her iki giriş dizisinde de etkin olan tek bit, 1 numaralı bitti, bu yüzden permütasyon açıkça başlar 4. Şimdi, bit 2'nin bit 1'e (ilk çıkış) ve bit 3'ün bit 3'e (ikinci çıkış) taşındığını görmek kolaydır. Bu nedenle permütasyon olmalıdır [4, 1, 3, 2].

n = 7:

Şimdi daha büyük bir şey. PHemen çağrılarını göstereceğim . Onlar küçük bir düşünce ve deneyden sonra bulduğum bir zamanlar. (Bunların benim kodumda kullandıklarına dikkat edin.)

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

İlk iki çıkış dizisinde (ve üçüncü değil), bit 2 aktifse, bit 1'in bit 2'ye hareket ettiğini biliyoruz, çünkü bit birinci, ilk iki giriş dizisinde aktif olan tek bittir.

Önemli olan, (girişleri matris olarak yorumlama) sütunların her birinin benzersiz olmasıdır. Bu beni aynı şeyin yapıldığı Hamming kodlarını hatırladı . Sadece 1 ila 7 arasındaki sayıları alırlar ve bit gösterimlerini sütun olarak kullanırlar. 0 ile 6 arasındaki sayıları kullanacağım. Şimdi güzel kısım, çıktıları (yine sütunlar) tekrar sayı olarak yorumlayabiliriz. Bunlar bize uygulanan permütasyonun sonucunu söyler [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]

Bu yüzden sadece sayıları izlemeliyiz. Bit 0 bit 5'te sona erdi, bit 1 bit 0'da bitti, bit 2 bit 6'da bitti ... Yani permütasyon oldu [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

Ve permütasyon fonksiyonunun kodu:

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
Eğer değiştirirseniz *sltQ]0ile m0sltQ, aynı uzun uzadıya 6 iç içe haritalar olabilir.
isaacg

Meydan okumaya uygun olarak, fdiğer isimlere izin verilse de , meydan okumayı çözen kodunuzu ideal olarak adlandırılmış bir işleve atamalısınız . Ödev skorunuza dahil edilir.
FUZxxl

@FUZxxl kodumu güncelledi. Şimdi gSTDIN'den okumak yerine bir işlev tanımlar .
Jakube

2

Mathematica, 63 + 100 = 163

Tek tabanlı permütasyonlar kullanıyorum, çünkü Mathematica'da indeksleme bu şekilde çalışıyor.

İlk olarak, test koşum takımı. Bu sorgu işlevidir p(Mathematica'da kullanıcı tanımlı adlar büyük harf olmamalıdır):

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

Test döngüsüyle birlikte giriş hazırlığı:

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]

Ve son olarak, şu an için saf algoritmayı kullanan gerçek gönderimim:

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

Veya girinti ile:

f = (
     v = 0 q;
     v[[#]] = 1;
     Position[q~p~v, 1][[1, 1]]
) & /@ Range@Length[q = #] &
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.