順列を再構築する


16

前書き

nオブジェクトのランダムな順列が渡されたとします。順列はボックスに密封されているため、どの順列であるかわかりませんn!。順列をn個別のオブジェクトに適用できた場合、すぐにそのアイデンティティを推測できます。ただし、置換は長さnバイナリベクトルにのみ適用できます。つまり、それを認識するために数回適用する必要があります。明らかに、nたった1つでベクトルに適用することで1仕事はできますが、賢いのであれば、log(n)アプリケーションでそれを行うことができます。ただし、そのメソッドのコードは長くなります...

これは実験的な課題であり、スコアはコード長とクエリの複雑さの組み合わせ、つまり補助プロシージャの呼び出し回数です。仕様は少し長いので、ご容赦ください。

タスク

あなたの仕事は、0ベースまたは1ベースのインデックス付けを使用して、入力として正の整数と最初の整数の順列をとる名前付き関数(またはそれに最も近いもの) を書くことです。その出力は置換です。ただし、順列に直接アクセスすることはできません。これでできることは、ビットのベクトルに適用することだけです。この目的のために、順列とビットのベクトルを取り込んで、th座標がbitを含む順列ベクトルを返す補助関数を使用します。例えば:fnpnppnPpvp[i]v[i]

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

「ビット」は、3and -4、or 'a'、and などの任意の2つの異なる値に置き換えることができ、'b'修正する必要はありません。そのためP[-4,3,3,-4][2,2,2,1]への同じ呼び出しで両方を呼び出すことができますf。の定義はPスコアにカウントされません。

得点

特定の入力に対するソリューションのクエリの複雑さは、補助関数を呼び出す回数ですP。この尺度を明確にするためには、解決策は決定的でなければなりません。疑似ランダムに生成された数値を使用できますが、ジェネレーターの初期シードも修正する必要があります。

ではこのリポジトリあなたはというファイル見つけることができますpermutations.txt(1ベースの場合の各番号をインクリメント)0ベースのインデックスを使用して、505個の順列、50と150までの間の各長さの5が含まれています。各順列は独自の行にあり、その番号はスペースで区切られています。スコアは、これらの入力のバイト数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が呼び出された回数を記録することを許可しないことです。このため、クエリの複雑度も計算し、それをハーネスで使用するようにソリューションを再実装できます。


私たちは、この定義の下で、例えば、「二つの異なる項目のベクトル」と「ビットのベクトルを」解釈することができ、両方abaaabababaa-4 3 3 3 -4 3ビットのベクトルになります。
-FUZxxl

@FUZxxlはい、個々のアイテムが区別できない限り。
ズガルブ

それらは私が持っている実装アプローチの数字です。
-FUZxxl

@FUZxxl仕様を編集しました。
ズガルブ

回答:


11

J、44.0693 22.0693 = 37 15 + 7.06931

我々は呼び出すことができない場合Pi. n、我々は少なくとも、呼び出すことができるPの各ビットにi. n個別に。呼び出しの数がPある>. 2 ^. n(⌈log 2 N ⌉)。これが最適だと思います。

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

Pこれは、置換ベクトルを使用してp呼び出し回数をに保存する関数の実装ですPinv

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は第一のベース2表現の下に移調下にP Yの整数。

ここで、yfの仮パラメーターです。Jは、(関数)のパラメータは、と呼ばれ、X及びY動詞は、ダイアディック(2つのパラメータを有する)とである場合、Yがモナドである場合(一つのパラメータを有します)。

代わりに呼び出すのPi. n(つまり0 1 2 ... (n - 1))、我々は、起動P中の番号の各ビット位置にi. n。すべての順列は同じように順列するため、順列ビットを数値に再構成して順列ベクトルを取得できます。

  • i. y-から整数0y - 1
  • #: yy基数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
    
  • #. yy基数2の数値として解釈されます。特に、これは#:; の逆です。y ~: #. #:常に保持します。

  • |: yy転置。
  • u&.v y- u下にvあり、vinv u v yどこvinv逆にありますv。それ|:はそれ自身の逆であることに注意してください。

  • P y– 定義によりP各ベクトルに適用される関数y


3

Pyth 32 + 7.06931 = 37.06931

次のアルゴリズムは完全に独立していることがわかりました。しかし、それはFUZxxlの非常に短いJソリューションとほぼ同じです(私が理解している限り)。

まず、P未知の置換に従ってビット配列を置換する関数の定義。

D%GHJHVJ XJ@HN@GN)RJ

そして、順列を決定するコード。

Mmxmi_mbk2Cm%dHCm+_jk2*sltG]0GdG

これは、g2つの引数を取る関数を定義します。で呼び出すことができますg5[4 2 1 3 0。ここだオンラインデモが。多くの(5)ネストされたマップを使用したことはありません。

ところで、実際にテストハーネスを作成したことはありません。また、この関数は、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つのアクティブビットを持つビット配列しか使用できないため、最適なソリューションは2つの呼び出しに依存します。例えばP([1, 0, 0])P([0, 1, 0])。結果は、置換の最初と2番目の数を示し、間接的に3番目のものを取得します。

n = 4:

ここで少し面白くなります。2種類のビット配列を使用できるようになりました。1つのアクティブビットを持つものと2つのアクティブビットを持つもの。1つのアクティブビットでビット配列を使用する場合、1つの順列に関する情報のみを収集し、にフォールバックして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(2番目の出力)に移動されました。したがって、順列はでなければなりません[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つの出力配列(3番目ではなく)でビット2がアクティブな場合、ビット1が最初の2つの入力配列でアクティブな唯一のビットであるため、順列がビット1からビット2に移動することがわかります。

重要なことは、(入力を行列として解釈して)各列が一意であることです。これは、同じことが達成されるハミングコードで私を思い出しました。それらは単に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]0m0sltQ、同じ長さで6つのネストされたマップを持つことができます。
isaacg

チャレンジに従って、チャレンジを解決するコードを、理想的な名前の関数に割り当てる必要がありますfが、他の名前も許可されています。課題はスコアにカウントされます。
-FUZxxl

@FUZxxlがコードを更新しました。gSTDINから読み取る代わりに、関数を定義するようになりました。
ジャクベ

2

Mathematica、63 + 100 = 163

Mathematicaでインデックス作成が機能するので、1ベースの順列を使用しています。

まず、テストハーネス。これはクエリ関数です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.