11

誰かがこの質問を数週間前にここに投稿しましたが、事前の調査なしでは宿題のように見え、OP はいくつかの反対票を受け取った後、すぐに削除しました。

質問自体はかなり興味深いものでしたが、満足のいく解決策が見つからないまま、1 週間考え続けました。うまくいけば、誰かが助けることができますか?

問題は次のとおりです。範囲が からまでの任意の値を取ることができる N 個の整数区間のリストが与えられた場合、どの入力区間にも属さないような最小の整数を見つけます。0ii

たとえば、リスト[3,5] [2,8] [0,3] [10,13](N = 4) が与えられた場合、アルゴリズムは を返す必要があり9ます。

私が考えることができる最も簡単な解決策は で実行さO(n log(n))れ、3 つのステップで構成されます。

  1. 下限を増やして間隔を並べ替える
    • 最小の下限が > 0 の場合は 0 を返します。
    • [a, b]それ以外の場合は、最初の間隔 (たとえば) が 2 番目の間隔 (たとえば ) に触れなく[c, d]なるまで、つまり、b + 1 < c になるまで、または間隔が 1 つになるまで、最初の間隔と 2 番目の間隔を繰り返しマージします。
  2. 戻るb + 1

この単純なソリューションは で実行されますO(n log(n))が、元の投稿者は、アルゴリズムは で実行する必要があると書いていますO(n)間隔が既にソートされている場合、それは簡単ですが、OP が示した例にはソートされていない間隔が含まれていました。boundと何か関係があるに違いないと思いますが、何がわからないのですか...ハッシュ?線形時間ソート?アイデアは大歓迎です。


上記のアルゴリズムの大まかな python 実装を次に示します。

def merge(first, second):
    (a, b), (c, d) = first, second
    if c <= b + 1:
        return (a, max(b, d))
    else:
        return False

def smallest_available_integer(intervals):
    # Sort in reverse order so that push/pop operations are fast
    intervals.sort(reverse = True)

    if (intervals == [] or intervals[-1][0] > 0):
        return 0

    while len(intervals) > 1:
        first = intervals.pop()
        second = intervals.pop()

        merged = merge(first, second)
        if merged:
            print("Merged", first, "with", second, " -> ", merged)
            intervals.append(merged)
        else:
            print(first, "cannot be merged with", second)
            return first[1] + 1

print(smallest_available_integer([(3,5), (2,8), (0,3), (10,13)]))

出力:

Merged (0, 3) with (2, 8)  ->  (0, 8)
Merged (0, 8) with (3, 5)  ->  (0, 8)
(0, 8) cannot be merged with (10, 13)
9
4

2 に答える 2

8

@mripのコメントについて詳しく説明します。説明した正確なアルゴリズムを使用してO(n)時間でこれを行うことができますが、ソートアルゴリズムの仕組みを変更します。

通常、基数ソートは基数 2 を使用します。要素は、ビットが 0 か 1 かに基づいて 2 つの異なるバケットに分割されます。基数ソートの各ラウンドには O(n) の時間がかかり、最大数のビットごとに 1 つのラウンドがあります。その最大数を U とすると、これは時間計算量が O(n log U) であることを意味します。

ただし、基数ソートの基数を他の基数に変更することはできます。ベース b を使用すると、各ラウンドには O(n + b) の時間がかかります。これは、バケットの初期化と反復に O(b) の時間がかかり、要素をバケットに分配するのに O(n) の時間がかかるためです。次に、log b U ラウンドがあります。これにより、O((n + b)log b U) のランタイムが得られます。

ここでの秘訣は、最大数 U = n 3であるため、b = n を設定して基数 n の基数ソートを使用できることです。ラウンド数は log n U = log n n 3 = 3 になり、各ラウンドには O(n) の時間がかかるため、数値をソートするための合計作業は O(n) になります。より一般的には、任意の k について時間 O(kn) で範囲 [0, n k ) の数値を並べ替えることができます。k が固定定数の場合、これは O(n) 時間です。

元のアルゴリズムと組み合わせると、これは時間 O(n) で問題を解決します。

お役に立てれば!

于 2013-10-10T17:25:27.300 に答える
0

別のアイデアは、これらの間隔の補数を何らかの形で使用することです。C() が間隔の補数を与えるとします。たとえば、C([3,5]) は 3 より小さい整数と 5 より大きい整数になります。最大数が N^3 の場合、モジュロ N^ を使用します。 3+1 これを別の区間 [6,(N^3+1)+2] として表すこともできます。

元の間隔のいずれにも属さない数値が必要な場合は、これらの間隔のすべての補数に同じ数値が存在する必要があります。次に、そのような 2 つの「補数間隔」の交点を計算できる関数を作成します。

私はこのアイデアを実装していません。ペンと紙で描いたものは、このような交差を計算する際に最初に想像したよりも多くのケースを考慮する必要があることを示していたからです。しかし、この背後にあるアイデアは有効であり、O(n) アルゴリズムになると思います。

編集

さらに考えてみると、私が最初に想像したよりも事態を複雑にする最悪のシナリオがあります。

于 2013-10-10T18:43:09.423 に答える