6

私は真珠のプログラミングに関する本を読んでいます。

質問:ランダムな順序で最大40億個の32ビット整数を含むシーケンシャルファイルがある場合、ファイルにない32ビット整数を見つけます(少なくとも1つは欠落している必要があります)。数百バイトのメインメモリといくつかのシーケンシャルファイルがある場合、この問題を解決する必要があります。

解決策:これを二分探索として設定するには、範囲、範囲内の要素の表現、および範囲のどちらの半分が欠落している整数を保持しているかを判別するためのプロービング方法を定義する必要があります。これをどのように行うのですか?

少なくとも1つの欠落している要素を含むことがわかっている整数のシーケンスを範囲として使用し、その範囲をすべての整数を含むファイルで表します。洞察は、中点の上下の要素を数えることで範囲を調査できるということです。上部または下部の範囲には、合計範囲の最大で半分の要素があります。範囲全体に欠落している要素があるため、小さい方の半分にも欠落している要素が含まれている必要があります。これらは、上記の問題に対する二分探索アルゴリズムのほとんどの要素です。

上記のテキストは、プログラミング真珠の本からのJonBentlyの著作権です。

いくつかの情報は次のリンクで提供されています

「ProgrammingPearls」バイナリ検索ヘルプ

バイナリ検索を使用してパスで検索し、上記のリンクに示されている例に従わない場合はどうすればよいですか?論理を理解するために、百万の整数ではなく、5つの整数だけで論理を理解するのを手伝ってください。

4

5 に答える 5

3

投稿「ProgrammingPearls」バイナリ検索ヘルプの回答をもう一度読んでみませんか。それはあなたが尋ねるように5つの整数のプロセスを説明します。
アイデアは、各リストを解析し、最初のビットの値に基づいて2つ(これはバイナリ部分が由来する)の別々のリストに分割することです。

つまり、実際の数値の2進表現を表示します。元のリスト "":001、010、110、000、100、011、101 =>(に割り込まれます)
(最初のビットを削除して、新しいリストの "名前"に追加します)
以下の各リストを作成するために、上記のリストから[0または1]で始まる値を取得しました。リスト
" 0 ":01、10、00、11(リスト ""のサブセット001、010、000、011から作成されます。最初のビットを削除して新しいリストの「名前」に追加)
リスト " 1 ":10、00、01(リスト ""のサブセット110、100、101から形成され、最初のビットを削除して追加します。新しいリストの「名前」)

次に、結果のリストの1つを順番に取得し、プロセスを繰り返します。
リスト「0」が元のリストになり、
リスト「0 ***0**」と
リスト「0***1 **」に分割します(太字の数字は、リスト内の数字の1 [残り]ビットが壊れています)

空のリストが表示されるまで続けます。

EDIT
プロセスのステップバイステップ:
リスト "":001、010、110、000、100、011、101=>
リスト"0":01、10、00、11(リストのサブセット001、010、000、011から) "")=>
リスト "00":1、0(リスト "0"のサブセット01、00から)=>
リスト "000":0 [最終結果](リスト "00"のサブセット0から)
リスト"001":1 [最終結果](リスト "00"のサブセット1から)
リスト "01":0、1(リスト "0"のサブセット10、11から)=>
リスト "010":0 [最終結果](リスト "01"のサブセット0から)
リスト "011":1 [最終結果](リスト "01"のサブセット1から)
リスト "1":10、00、01(リスト ""のサブセット110、100、101から)=>
リスト "10":0、1(リスト "1"のサブセット00、01から)=>
リスト"100":0 [最終結果](リスト "10"のサブセット0から)
リスト "101":1 [最終結果](リスト "10"のサブセット1から)
リスト "11":0(からリスト"1"のサブセット10)=>
リスト "110":0 [最終結果](リスト "11"のサブセット0から)
リスト "111":なし[最終結果](リストのサブセットEMPTYから" 11 ")

この方法の利点は、セット内の欠落している番号をいくつでも見つけることができることですつまり、複数の欠落している場合です。

完全な範囲から1つの欠落している番号に対するPSAFAIRには、XORすべての番号のさらに洗練されたソリューションがあります。

于 2012-09-07T09:08:44.433 に答える
1

アイデアは、より簡単な問題を解決することです。

範囲[minVal、X]または(X、maxVal)に欠落している値です。これを知っている場合は、Xを移動してもう一度確認できます。

たとえば、3、4、1、5があります(2はありません)。minVal = 1、maxVal=5であることがわかります。

  1. 範囲=[1、5]、X = 3、範囲[1、3]に3つの整数、範囲[4、5]に2つの整数が必要です。範囲[1、3]には2つしかないため、範囲[1、3]を探しています。
  2. 範囲=[1、3]、X =2。範囲[1、2]には値が1つしかないため、範囲[1、2]を探しています。
  3. 範囲=[1、2]、X =1。範囲[2、2]には値がないため、これが答えです。

編集:いくつかの疑似C ++コード:

minVal = 1, maxVal = 5; //choose correct values
while(minVal < maxVal){
    int X = (minVal + maxVal) / 2
    int leftNumber = how much in range [minVal, X]
    int rightNumber = how much in range [X + 1, maxVal]
    if(leftNumber < (X - minVal + 1))maxVal = X
    else minVal = X + 1
}
于 2012-09-07T09:08:10.007 に答える
1

これは、テクニックを説明するための簡単なCソリューションです。面倒なファイルI/Oの詳細を抽象化するために、次の3つの関数の存在を想定しています。

  • unsigned long next_number (void)ファイルから数値を読み取り、それを返します。再度呼び出されると、ファイル内の次の番号が返されます。ファイルの終わりに遭遇したときの動作は未定義です。

  • int numbers_left (void)を使用して読み取ることができる数値がさらにある場合はtrue値を返し、next_number()ファイルの終わりに達した場合はfalseを返します。

  • void return_to_start (void)読み取り位置をファイルの先頭に巻き戻し、次の呼び出しでnext_number()ファイルの最初の番号が返されるようにします。

unsigned longまた、ANSI C実装に準拠するために必要な、少なくとも32ビット幅であると想定しています。現代のCプログラマーは、代わりにuint32_tfromを使用することを好むかもしれませんstdint.h

これらの仮定を前提として、解決策は次のとおりです。

unsigned long count_numbers_in_range (unsigned long min, unsigned long max) {
    unsigned long count = 0;

    return_to_start();

    while ( numbers_left() ) {
        unsigned long num = next_number();
        if ( num >= min && num <= max ) {
            count++;
        }
    }
    return count;
}

unsigned long find_missing_number (void) {
    unsigned long min = 0, max = 0xFFFFFFFF;

    while ( min < max ) {
        unsigned long midpoint = min + (max - min) / 2;
        unsigned long count = count_numbers_in_range( min, midpoint );

        if ( count < midpoint - min + 1 ) {
            max = midpoint;  // at least one missing number below midpoint
        } else {
            min = midpoint;  // no missing numbers below midpoint, must be above
        }
    }
    return min;
}

注意すべき詳細の1つは、とmin + (max - min) / 2の平均を計算する安全な方法です。一見単純なように中間値がオーバーフローするため、偽の結果は生成されません。minmax(min + max) / 2

また、再帰を使用してこの問題を解決したくなる場合でも、2つの理由から、代わりに反復ソリューションを選択しました。1つは、実際に行われていることを(おそらく)より明確に示すためです。2つ目は、タスクがメモリを最小限に抑えることであったためです。おそらくスタックも含まれている使用。

最後に、このコードを最適化するのは簡単です。たとえば、countゼロに等しくなるとすぐに戻る、1回のパスで範囲の両方の半分の数を数え、欠落している数が多い方を選択する、またはバイナリ検索をパスの数を減らすために、いくつかのn >2をn- ary検索します。ただし、サンプルコードをできるだけ単純にするために、このような最適化は行わないでおきます。必要に応じて、たとえば、現在の32ではなくファイルを最大8回パスするようにコードを変更してみてください(ヒント:16要素の配列を使用してください)。

于 2012-09-07T11:11:27.223 に答える
0

実際、aからbまでの整数の範囲がある場合。サンプル:[a..b]。そして、この範囲にはba整数があります。つまり、1つだけが欠落しています。また、1つだけが欠落している場合は、単一のサイクルのみを使用して結果を計算できます。まず、範囲[a..b]内のすべての整数の合計を計算できます。これは、次のようになります。

sum = (a + b) * (b - a + 1) / 2

次に、シーケンス内のすべての整数の合計を計算します。

long sum1 = 0;
for (int i = 0; i < b - a; i++)
sum1 += arr[i];

次に、これら2つの合計の差として欠落している要素を見つけることができます。

長い結果=sum1-合計;

于 2012-09-07T11:35:24.670 に答える
0

i番目の桁に2^31個のゼロまたは1が表示されている場合、答えのi番目に1または0があります。(例:5番目の2項位置に2 ^ 31個ある場合は、回答の5番目の2項位置にゼロがあることを意味します。

cコードの最初のドラフト:

uint32_t binaryHistogram[32], *list4BILLION, answer, placesChecked[32];
uint64_t limit = 4294967296;
uint32_t halfLimit = 4294967296/2;
int i, j, done

//General method to point to list since this detail is not important to the question.
list4BILLION = 0000000000h;


//Initialize array to zero. This array represents the number of 1s seen as you parse through the list
for(i=0;i<limit;i++)
{   
    binaryHistogram[i] = 0;
}

//Only sum up for first half of the 4 billion numbers
for(i=0;i<halfLimit;i++)
{
    for(j=0;j<32;j++)
    {
        binaryHistogram[j] += ((*list4BILLION) >> j);
    }
}

//Check each ith digit to see if all halfLimit values have been parsed
for(i=halfLimit;i<limit;i++)
{
    for(j=0;j<32;j++)
    {
        done = 1;   //Dont need to continue to the end if placesChecked are all 
        if(placesChecked[j] != 0) //Dont need to pass through the whole list
        {
            done = 0; //
            binaryHistogram[j] += ((*list4BILLION) >> j);
            if((binaryHistogram[j] > halfLimit)||(i - binaryHistogram[j] == halfLimit))
            {
                answer += (1 << j);
                placesChecked[j] = 1;
            }
        }
    }
}
于 2014-05-31T23:26:18.330 に答える