11

次のように解決するコードを書いているパズルがあります。

最初はすべてゼロである長さ n のバイナリ ベクトルを考えます。ベクトルのビットを選択し、それを 1 に設定します。ここで、任意の 1 ビットから $1$ までの距離が最大のビットを設定するプロセスが開始されます (複数ある場合は、最も遠いビットの任意の選択)。これは、2 つの 1 ビットが隣り合ってはならないというルールで繰り返し発生します。1 ビットを配置するスペースがなくなると終了します。目標は、終了時にできるだけ多くのビットが 1 に設定されるように、最初の 1 ビットを配置することです。

n = 2 とします。この場合、ビットを設定する場所はどこでも、正確に 1 つのビットが設定されます。

n = 3 の場合、最初のビットを設定すると、最終的に 101 になります。しかし、中間ビットを設定すると、最適ではない 010 が得られます。

n = 4 の場合、どちらのビットを設定しても、最終的には 2 セットになります。

n = 5 の場合、最初に設定すると 10101 になり、最後に 3 ビットが設定されます。

n = 7 の場合、3 番目のビットを設定して 1010101 を取得する必要があるようです。

最適な値を見つけるためのコードを書きましたが、大きな n にうまくスケーリングしません。私のコードは n = 1000 あたりで遅くなり始めますが、n が 100 万あたりの問題を解決したいと考えています。

#!/usr/bin/python
from __future__ import division
from math import *

def findloc(v):
    count = 0
    maxcount = 0
    id = -1
    for i in xrange(n):
        if (v[i] == 0):
            count += 1
        if (v[i] == 1):
            if (count > maxcount):
                maxcount = count
                id = i
            count = 0

#Deal with vector ending in 0s
    if (2*count >= maxcount and count >= v.index(1) and count >1):
        return n-1
#Deal with vector starting in 0s
    if (2*v.index(1) >= maxcount and v.index(1) > 1):
        return 0
    if (maxcount <=2):
        return -1    
    return id-int(ceil(maxcount/2))


def addbits(v):
    id = findloc(v)
    if (id == -1):
        return v
    v[id] = 1
    return addbits(v)

#Set vector length
n=21    
max = 0
for i in xrange(n):
    v = [0]*n
    v[i] = 1
    v = addbits(v)
    score = sum([1 for j in xrange(n) if v[j] ==1])
#        print i, sum([1 for j in xrange(n) if v[j] ==1]), v
    if (score > max):
        max = score       
print max
4

3 に答える 3

4

したがって、証明できないアルゴリズムを投稿しないという私の通常の伝統とは一線を画して、50,000+ までの数値に対して正しいように見え、O(log n) 時間で実行されるアルゴリズムがあることを言及する必要があると思います。 . これは、私が今日この問題に約 3 時間取り組んだSophia Westwoodによるものです。これに対するすべての功績は彼女によるものです。経験的に、それは美しく機能しているように見え、O(n) ソリューションよりもはるかに高速です。

この問題の構造に関する観察事項の 1 つは、n が十分に大きい場合 (n ≥ 5)、どこかに 1 を置くと、問題は 1 の左側と右側の 2 つのサブ問題に分割されることです。1 は異なる時間に異なる半分に配置される可能性がありますが、最終的な配置は、各半分を別々に解決してそれらを再び結合した場合と同じです。

次の観察結果は次のとおりです。ある k に対してサイズ 2 k + 1 の配列があるとします。その場合、配列の両側に 1 を入れるとします。それで:

  • 次の 1 は配列の反対側に配置されます。
  • 次の 1 は真ん中に配置されます。
  • これで、サイズ 2 k-1 + 1 の 2 つの小さな部分問題ができました。

これに関する重要な部分は、結果として得られるビット パターンが一連の 1 と 0 が交互になることです。例えば:

  • 5 = 4 + 1 の場合、10101 を取得します。
  • 9 = 8 + 1 の場合、101010101 が得られます。
  • 17 = 16 + 1 の場合、10101010101010101 が得られます。

これが重要な理由は次のとおりです。配列に合計 n 個の要素があり、k を 2 k + 1 ≤ n の可能な最大値とします。1 を 2 k + 1 の位置に配置すると、その位置までの配列の左側の部分が 1 と 0 が交互にタイル状に配置され、配列に多くの 1 が配置されます。

明らかではないのは、50,000 までのすべての数に対して 1 ビットをそこに配置すると、最適な解が得られるように見えるということです! これをチェックする Python スクリプト (@justhalf に似た再帰関係を使用) を作成しましたが、うまく機能しているようです。この事実が非常に役立つ理由は、この指数を計算するのが非常に簡単だからです。特に、2 k + 1 ≤ n の場合、2 k ≤ n - 1 なので、k ≤ lg (n - 1) となります。k の選択として値 ⌊lg (n - 1) ⌋ を選択すると、2 k + 1 を計算してビット インデックスを計算できます。この k の値は O(log n) 時間で計算でき、べき乗を実行できます。同様に O(log n) 時間なので、総実行時間は Θ(log n) です。

唯一の問題は、これが機能することを正式に証明していないことです。私が知っているのは、私たちが試した最初の 50,000 個の値が正しいということだけです。:-)

お役に立てれば!

于 2013-10-11T04:30:57.647 に答える