0

いくつかの時間間隔を表すソートされていない整数のペアがあります(最初の数値は常に2番目の数値よりも小さくなります)。問題は、チャネル番号 (0..x) と呼ばれる整数を各時間間隔に割り当てて、衝突しない間隔が同じチャネルを共有するようにすることです。可能な限り少ない数のチャネルを使用する必要があります。

たとえば、これらの間隔は 2 つのチャネルを使用します。

50 100 //1

10 70 //0

80 200 //0

入力を最初の列でソートするためにカウントソートを使用して実装し、次に線形検索を使用して、互いに続くペアのチェーンを見つけました。また、最初に入力 *const* 配列を新しい配列にコピーし、最後に入力配列の正しい位置に値を割り当てます。

はい、それは私が大学から受け取った課題であり、すでに実装されていますが、コードを高速化する方法を誰か教えてもらえますか? ペアの並べ替え、チェーン化が可能な限り高速になるように、どのアルゴリズムを使用しますか? 入力配列の長さは最大 1,000 万要素です。

コードは次のとおりです。

#include <cstdlib>
#include <cstdio>
#include <iostream>
using namespace std;   

struct TPhone
 {
   unsigned int    m_TimeFrom;
   unsigned int    m_TimeTo;
   unsigned int    m_Channel;
 };

 class TElement
 {
 public:

  TPhone m_data;
  int index;

  TElement(TPhone  * const data, int index_)
  {
    m_data.m_TimeFrom=data->m_TimeFrom;
    m_data.m_TimeTo=data->m_TimeTo;
    m_data.m_Channel=-1;
    index=index_;
  }
  TElement()
  {
  }
 };

int FindNext(TElement** sorted_general_array, int general_array_size, int index_from)
{
  for (int i=index_from+1; i<general_array_size; i++ )
  {
    if (sorted_general_array[i]->m_data.m_TimeFrom > sorted_general_array[index_from]->m_data.m_TimeTo)
    {
      if (sorted_general_array[i]->m_data.m_Channel==(unsigned int)-1)
      {
        return i;
      }
    }
  }
  return -1;
}

int AssignChannels(TElement **sorted_general_array, int general_array_size)
{
  int current_channel=-1;
  for (int i=0; i<general_array_size; i++)
    {
      if (sorted_general_array[i]->m_data.m_Channel==(unsigned int)-1)
      {
        current_channel++;
        sorted_general_array[i]->m_data.m_Channel=current_channel;
        //cout << sorted_general_array[i]->m_data.m_TimeFrom << " " << sorted_general_array[i]->m_data.m_TimeTo << " " << sorted_general_array[i]->m_data.m_Channel << endl;
        int next_greater=i;
        while (1)
        {
          next_greater=FindNext(sorted_general_array,general_array_size,next_greater);
          if (next_greater!=-1)
          {
            sorted_general_array[next_greater]->m_data.m_Channel=current_channel;
            //cout << sorted_general_array[next_greater]->m_data.m_TimeFrom << " " << sorted_general_array[next_greater]->m_data.m_TimeTo << " " << sorted_general_array[next_greater]->m_data.m_Channel << endl;
          }
          else
          {
            break;
          } 
        }
      }
    }
    return current_channel;
}

int AllocChannels ( TPhone  * const * req, int reqNr )
 {
  //initialize
  int count_array_size=1700000;
  int * count_array;
  count_array=new int [count_array_size];
  for (int i=0; i<count_array_size; i++)
  {
     count_array[i]=0;
  }
  //
  int general_array_size=reqNr;
  TElement ** general_array;
  general_array=new TElement *[general_array_size];
  for (int i=0; i<general_array_size; i++)
  {
    general_array[i]= new TElement(req[i],i);
  }
  //--------------------------------------------------
  //counting sort
  //count number of each element
  for (int i=0; i<general_array_size; i++)
  {
    count_array[general_array[i]->m_data.m_TimeFrom]++;
  }
  //modify array to find postiions
  for (int i=0; i<count_array_size-1; i++)
  {
    count_array[i+1]=count_array[i+1]+count_array[i];
  }
  //make output array, and fill in the sorted data
  TElement ** sorted_general_array;
  sorted_general_array=new TElement *[general_array_size];

  for (int i=0; i <general_array_size; i++)
  {
    int insert_position=count_array[general_array[i]->m_data.m_TimeFrom]-1;
    sorted_general_array[insert_position]=new TElement;

    //cout << "inserting " << general_array[i]->m_data.m_TimeFrom << " to " << insert_position << endl;
    sorted_general_array[insert_position]->m_data.m_TimeFrom=general_array[i]->m_data.m_TimeFrom;
    sorted_general_array[insert_position]->m_data.m_TimeTo=general_array[i]->m_data.m_TimeTo;
    sorted_general_array[insert_position]->m_data.m_Channel=general_array[i]->m_data.m_Channel;
    sorted_general_array[insert_position]->index=general_array[i]->index;


    count_array[general_array[i]->m_data.m_TimeFrom]--;
    delete  general_array[i];
  }
  //free memory which is no longer needed
  delete [] general_array;
  delete [] count_array;
  //--------------------------------------------------

  int channels_number=AssignChannels(sorted_general_array,general_array_size);
  if (channels_number==-1)
  {
    channels_number=0;
  }
  else
  {
    channels_number++;
  }

  //output
  for (int i=0; i<general_array_size; i++)
  {
    req[sorted_general_array[i]->index]->m_Channel=sorted_general_array[i]->m_data.m_Channel;
  }


  //free memory and return
  for (int i=0; i<general_array_size; i++)
  {
    delete sorted_general_array[i];
  }
  delete [] sorted_general_array;

  return channels_number;
 }                                                             


int main ( int argc, char * argv [] )
 {
   TPhone ** ptr;
   int cnt, chnl;

   if ( ! (cin >> cnt) ) return 1;

   ptr = new TPhone * [ cnt ];
   for ( int i = 0; i < cnt; i ++ )
    {
      TPhone * n = new TPhone;
      if ( ! (cin >> n -> m_TimeFrom >> n -> m_TimeTo) ) return 1;
      ptr[i] = n;
    }

   chnl = AllocChannels ( ptr, cnt );

   cout << chnl << endl;
   for ( int i = 0; i < cnt; i ++ )
    {
      cout << ptr[i] -> m_Channel << endl;
      delete ptr[i];
    }
   delete [] ptr; 
   return 0;
  }
4

6 に答える 6

2

エントリをstd::vector<TPhone>ではなく に保存しますTPhone **。これにより、連続するオブジェクトがメモリ内で連続してレイアウトTPhoneされるため、キャッシュ ミスが少なくなります。

unsigned intのメンバー以外のデータ型を試してTPhoneください。<cstdint>試すことができるタイプについては、を参照してください。

于 2013-11-03T20:17:19.010 に答える
0

コレクションがソートされている場合、なぜ線形検索を使用するのでしょうか? 二分探索を使用します。

于 2013-11-03T20:38:54.390 に答える
0

ここでネクロマンサーになって申し訳ありませんが、質問を読んで回答を投稿した後、私はこれを手放すことができませんでした.

チャネル割り当てアルゴリズム:

この問題には、非常に効率的な貪欲な解決策があります。次の再帰を考えてみましょう。

  • 最初の間隔をチャネル 1 に割り当てます
  • 残りの間隔ごとに:
    • 各チャネル (少なくとも 1 つの間隔が割り当てられている) について:
      • 割り当てられた間隔に競合が存在しない場合は、このチャネルに間隔を割り当て、次の間隔を処理します。
    • すべてのチャネルに競合が存在する場合は、間隔を新しいチャネルに割り当てます。

これにより、最適な数のチャネルが生成されます。証明は、区間数の帰納法によって自明です。

入力の並べ替え

スピードの鍵は「競合が存在しない場合」にあります。これは、処理されたものと処理されていないものが比較されることを意味し、最初に入力をソートする方が、処理しながらソートするよりも最終的に高速になることを納得させるのは簡単です (または処理しない)。それらをまったく並べ替えます)。

確信が持てない場合は、次の 2 つの極端な例を検討してください。

  1. すべての間隔が重なります。
  2. 重複する間隔はありません。

ソートアルゴリズムの選択

入力を開始時間、次に終了時間でソートする必要があります。安定した並べ替えを選択し、最初に終了時刻で並べ替え、次に開始時刻で並べ替えれば、これは簡単です。すべての値が整数であることを考えると、Counting Sort の安定バージョンが最適なオプションである可能性があります。入力の数が入力の範囲よりもはるかに多い。メモリ使用量は重要な考慮事項ではありません。この並べ替えは、これらの条件下での入力数に比例します。

チャンネルの並べ替え

入力をソートすることにより、各間隔を各チャネルに割り当てられた最後の間隔と比較するだけで済みます。間隔がオーバーラップしない極端なケースでは、このアルゴリズムは線形です: O(n) ソート、+ O(n) 処理 = O(n)。すべての間隔がオーバーラップする極端な反対側では、アルゴリズムをさらに改善しなければ、アルゴリズムは 2 次になります。

これを改善するために、すべてのチャネルと比較するのではなく、チャネルが最も早い終了時間で並べ替えられている場合、最初のチャネルとの競合は、すべてのチャネルが競合していることを自動的に示します。次に、間隔ごとに必要な比較は 1 つだけで、チャネルの並べ替え順序を維持するために必要なものは何でもあります。

このため、チャネルを最小ヒープに (終了時間までに) 保持することをお勧めします。比較に必要なチャネルは常に一番上にあります。そのチャンネルをのぞくと、次のようになります。

  • オーバーラップがある場合は、新しいチャネルを作成し、それをヒープに追加します。この新しいチャネルは、O(lg m) のコストでヒープを上に移動する必要がある場合があります。ここで、m は現在のチャネル数です。
  • それ以外の場合は、最小チャネル O(lg m) をポップし、それに間隔を追加して (その値を変更して)、通常 O(lg m) をヒープに追加します。

最悪の場合の悪夢のシナリオでは、並べ替えられた間隔の開始時間が単調に増加し、終了時間が単調に減少します。これにより、O(n + lg 1 + lg 2 + ... + lg n) = O(n + lg(n!)) = O(n + n lg n) = O のアルゴリズムの最悪のケースが得られます。 (n lg n)

現実の世界

漸近的に優れていることが常に優れているとは限りません。これは、入力の分布と入力のサイズに大きく依存します。ここで説明されているアルゴリズムは、提示されている他のアルゴリズムよりも優れていると確信していますが、実装には、漸近的に同一であるが異なる結果を生成する選択肢の余地があることは確かです。

于 2015-12-02T21:47:32.527 に答える
0

[a i , b i ) を間隔、i = 1、...、n とします。n 間隔ごとにチャネル番号を返す関数 channel(i) を設計します。

唯一の制約は、2 つの交差する間隔が同じチャネル上にないことです。これは、間隔が頂点である無向グラフに対応し、対応する間隔が交差する場合にのみ、2 つの頂点間にエッジがあります。

頂点が独立したセットを形成している場合は、チャネル C を特定の頂点セット (間隔) に割り当てることができます。

この形式の独立したセットのセットを見つけたいとします。これらのすべての和集合がグラフをカバーし、それらは対ごとに互いに素です。独立したセットをできるだけ少なくします。

最大独立集合を見つける (関連する) 問題は NP 完全です。したがって、最小数のチャネルを与える解を見つけるための多項式時間アルゴリズムを見つけることを期待すべきではないと思います。

より現実的な期待値は、(A) 問題を解決するために超多項式の時間を費やすか、(B) 大域的な最適値が得られない可能性のある近似アルゴリズムを使用するかの 2 つの形式のいずれかになります。

(B)の場合、これを行うことができます:

feasible(M)

    initialize M empty channels (lists of intervals)

    sort intervals by a_i value

    for each interval I = [a_i, b_i):

        insert it into the channel for which the most recent
        interval is closest to the current interval (but not intersecting)

        if I cannot be inserted at the end of any channel, return false

    return true //the M channels are a feasible solution

この手順を使用すると、実行可能が true を返す最小の M を指数関数的に検索できます。

trueを返す最初の M = 2 kに到達するまで、M = 1、2、4、8、16、... を試してください。次に、2 k - 1と 2 kfeasible(M)の間で二分探索を行い、最小の M を見つけます。

于 2013-11-03T19:48:43.107 に答える