web-dev-qa-db-ja.com

数値の配列から最小値または最大値を取得する最良の方法は何ですか?

数値の配列があるとしましょう:[2,3,3,4,2,2,5,6,7,2]

その配列の最小値または最大値を見つける最良の方法は何ですか?

今、最大値を取得するために、配列をループし、変数が既存の値よりも大きい場合はその値にリセットしています。

var myArray:Array /* of Number */ = [2,3,3,4,2,2,5,6,7,2];

var maxValue:Number = 0;

for each (var num:Number in myArray)
{
    if (num > maxValue)
        maxValue = num;
}

これは、これを行うのに最適なパフォーマンスの方法とは思えません(可能な限りループを回避しようとしています)。

33
Eric Belair

他のすべての人からの理論的な答えはすべてき​​ちんとしていますが、実際的なものにしましょう。 ActionScriptは必要なツールを提供するため、この場合はループを作成する必要さえありません。

まず、Math.min()Math.max()は任意の数の引数を取ることができることに注意してください。また、Functionオブジェクトで使用可能なapply()メソッドを理解することも重要です。 Arrayを使用して関数に引数を渡すことができます。両方を活用しましょう:

var myArray:Array = [2,3,3,4,2,2,5,6,7,2];
var maxValue:Number = Math.max.apply(null, myArray);
var minValue:Number = Math.min.apply(null, myArray);

最高の部分は次のとおりです。「ループ」は実際にはネイティブコード(Flash Player内)を使用して実行されるため、純粋なActionScriptループを使用して最小値または最大値を検索するよりも高速です。

81
joshtynjala

すべての値をテストせずに最小値/最大値を取得する信頼できる方法はありません。ソートなどを試してみたいとは思わないので、配列をウォークスルーするのはO(n)です。これは、一般的なケースでソートアルゴリズムが実行できるよりも優れています。

30
Adam Bellaire

もし

  1. 配列はソートされていません
  2. 最小値と最大値の検索は同時に行われます

次に、3n/2回の比較で最小値と最大値を見つけるアルゴリズムがあります。行う必要があるのは、配列の要素をペアで処理することです。ペアの大きい方を現在の最大値と比較し、ペアの小さい方を現在の最小値と比較する必要があります。また、配列に奇数の要素が含まれる場合は特別な注意が必要です。

C++コード(Mehrdadの一部のコードを借用)。

struct MinMax{
   int Min,Max;
}

MinMax FindMinMax(int[] array, int start, int end) {
   MinMax  min_max;
   int index;
   int n = end - start + 1;//n: the number of elements to be sorted, assuming n>0
   if ( n%2 != 0 ){// if n is odd

     min_max.Min = array[start];
     min_max.Max = array[start];

     index = start + 1;
   }
   else{// n is even
     if ( array[start] < array[start+1] ){
       min_max.Min = array[start];
       min_max.Max = array[start+1];
     }
     else{
       min_max.Min = array[start+1];
       min_max.Max = array[start];
     }
     index = start + 2;
   }

   int big, small;
   for ( int i = index; i < n-1; i = i+2 ){
      if ( array[i] < array[i+1] ){ //one comparison
        small = array[i];
        big = array[i+1];
      }
      else{
        small = array[i+1];
        big = array[i];
      }
      if ( min_max.Min > small ){ //one comparison
        min_max.Min = small;
      }
      if ( min_max.Max < big ){ //one comparison
        min_max.Max = big;
      }
   }

   return min_max;
}

必要な比較の数が3n/2であることが簡単にわかります。ループはn/2回実行され、各反復で3回の比較が実行されます。これはおそらく達成できる最適な方法です。現時点では、その明確な情報源を指すことはできません。 (しかし、どこかでその証拠を見たと思います。)

上記のMehrdadによって提供された再帰的なソリューションは、おそらくこの最小数の比較を達成します(最後の行を変更する必要があります)。しかし、同数の比較では、反復ソリューションは、彼が述べたように、関数呼び出しのオーバーヘッドのために常に再帰的ソリューションに勝ります。ただし、(Eric Belairがそうであるように)数の最小値と最大値を見つけることにのみ関心がある場合、上記のアプローチのいずれかで今日のコンピューターの違いに気付くことはありません。大きな配列の場合、違いはかなり大きくなる可能性があります。

このソリューションとMatthew Brubakerが提供するソリューションにはO(n)複雑性がありますが、実際には、隠された定数を慎重に評価する必要があります。彼のソリューションの比較数は2nです2nの比較とは対照的に3n/2の比較のソリューションでは顕著です。

27
Rayhan

配列が並べ替えられていない限り、これが最良の結果です。ソートされている場合は、最初と最後の要素を取得します。

もちろん、ソートされていない場合、最初にソートして最初と最後を取得すると、1回ループするよりも効率が低下することが保証されます。最高の並べ替えアルゴリズムでさえ、各要素を複数回調べる必要があります(各要素の平均O(log N)回。これは合計O(N * Log N)です。1回の単純なスキャンはO(N)のみです。

データ構造内の最大要素にすばやくアクセスしたい場合は、ヒープを見て、オブジェクトを何らかの順序で保持する効率的な方法を探してください。

19
Eclipse

配列をループする必要があります。すべての要素をチェックする他の方法はありません。コードの1つの修正-すべての要素が負の場合、maxValueは最後に0になります。整数の最小値で初期化する必要があります。
配列を何度も検索する場合は、最初に並べ替えることをお勧めします。検索は高速(バイナリ検索)で、最小要素と最大要素は最初と最後です。

12
Rumen Georgiev

「ベスト」と呼ぶものに依存します。理論的な観点からは、決定論的チューリングマシンのO(n)未満では問題を解決できません。

素朴なアルゴリズムはループが多すぎるため、最小、最大を更新します。ただし、最小値と最大値を同時に取得する場合、再帰的なソリューションは単純なアルゴリズムよりも比較が少なくて済みます(関数呼び出しのオーバーヘッドのため、必ずしも高速ではありません)。

struct MinMax{
   public int Min,Max;
}

MinMax FindMinMax(int[] array, int start, int end) {
   if (start == end)
      return new MinMax { Min = array[start], Max = array[start] };

   if (start == end - 1)
      return new MinMax { Min = Math.Min(array[start], array[end]), Max = Math.Max(array[start], array[end]) } ;

   MinMax res1 = FindMinMax(array, start, (start + end)/2);
   MinMax res2 = FindMinMax(array, (start+end)/2+1, end);
   return new MinMax { Min = Math.Min(res1.Min, res2.Min), Max = Math.Max(res1.Max, res2.Max) } ;
}

最も簡単な解決策は、最初と最後のアイテムをソートして取得することですが、それは明らかに最速ではありません;)

最小値を見つけるためのパフォーマンス面での最適なソリューション または 最大値は、作成した単純なアルゴリズムです(1つのループで)。

4
Mehrdad Afshari

Math.max()は、実際にはAVM2オペコードにコンパイルされたas3コードであり、他のas3コードよりも「ネイティブ」ではありません。結果として、必ずしも最速の実装ではありません。

実際、Array型で機能することを考えると、Vectorを使用して慎重に記述されたコードよりも低速です。

GskinnerのPerformanceTest(同一の乱数で埋められたベクターと配列)を使用して、Math.maxのいくつかの素朴なベクターと配列の実装の簡単なベンチマーク比較を行いました。最近のAIR SDK /リリースプレーヤー(FlashプレーヤーWIN 14,0,0,122リリース、AIR SDK 14でコンパイル)では、最速のベクター実装はMath.maxの3倍以上高速に見えました。

math.max()の平均11ミリ秒と比較して、1,000,000値の場合は平均3.5ミリ秒:

function max(values:Vector.<Number>):Number
{
    var max:Number = Number.MIN_VALUE;
    var length:uint = values.length;
    for (var i:uint = 0; i < length ; ++i)
        if (values[i] > max)
            max = values[i];
    return max;
}

結論として、パフォーマンスに懸念がある場合は、最初にできる限りどこでもVector over Arrayを使用する必要があります。特に、デフォルトの実装に常に依存するわけではありません。特に、Arrayの使用を強制する場合

PS:for each()ループと同じ実装は12倍遅い...!

3
jauboux

配列onceを構築していて、最大値を1回だけ検索する場合は、反復が最善です。

配列を変更し、最大要素を知りたい場合は、 Priority Queue を使用する必要があります。そのための最良のデータ構造の1つは フィボナッチヒープ で、これが複雑すぎる場合は バイナリヒープ を使用します。

最小値と最大値を見つけるには、2つのヒープを構築し、そのうちの1つの数値の符号を変更します。

2
martinus

これは、実際のアプリケーション要件に依存します。

あなたの質問が単なる仮説である場合、基本はすでに説明されています。これは典型的な検索とソートの問題です。その場合、アルゴリズム的にはO(n)よりも良い結果を得られないだろうということは既に述べた。

ただし、実際の使用を検討している場合は、より興味深いものになります。次に、配列の大きさ、およびデータセットの追加と削除に関係するプロセスを考慮する必要があります。これらの場合、オンザフライでソートすることにより、挿入/削除時に計算の「ヒット」を取得するのが最適です。事前にソートされた配列への挿入はそれほど高価ではありません。

Min Maxリクエストに対する最も速いクエリ応答は、常にソートされた配列からのものです。他の人が述べたように、最初または最後の要素を取得するだけなので、O(1)コストが発生します。

関連する計算コストに関するもう少し技術的な説明とBig O表記については、Wikipediaの記事 here をご覧ください。

ニック。

2
Nick

配列の並べ替えは、配列の特定のサイズまでループするよりも高速になることを考慮してください。配列が小さい場合(いつでもそのようになります)、ソリューションは完全に問題ありません。しかし、配列が大きすぎる場合は、配列が小さい場合は条件を使用してソート方法を使用し、配列が大きすぎる場合は通常の反復を使用する必要があります

1
Javier

これにはいくつかの方法があります。

  1. 強引な。最小と最大の両方を個別に線形検索します。 (2N比較と2Nステップ)
  2. 線形に反復し、最小と最大の両方の各数値を確認します。 (2N比較)
  3. 分割して征服する。 (2Nと3N/2の比較の間)
  4. 以下で説明するペアで比較(3N/2比較)

最小比較を使用して配列の最大値と最小値を見つける方法?


速度、実行時間、比較の回数について本当に妄想している場合は、 http://www.geeksforgeeks.org/maximum-and-minimum-in-an-array/ も参照してください

0
Shogo Warrior

アルゴリズムMaxMin(first、last、max、min)

//このアルゴリズムは、最高および最低の要素を保存します

//グローバル変数maxおよびminのグローバル配列Aの値

// tmaxおよびtminは一時的なグローバル変数です

{
if (first==last) //Sub-array contains single element
 {
    max=A[first];
    min=A[first];
 }
 else if(first+1==last) //Sub-array contains two elements
  {
     if(A[first]<A[Last])
      {
      max=a[last];  //Second element is largest
      min=a[first]; //First element is smallest
      }
   else
   {
     max=a[first]; //First element is Largest 
     min=a[last];  //Second element is smallest
   }
}
else
 //sub-array contains more than two elements
{
 //Hence partition the sub-array into smaller sub-array 
 mid=(first+last)/2;
 //Recursively solve the sub-array
 MaxMin(first,mid,max,min);
 MaxMin(mid+1,last,tmax,tmin);
 if(max<tmax)
  {
     max=tmax;
  }
    if(min>tmin)
  {
   min=tmin;
  }
 }
}
0

配列から最大値を見つける単一の機能を使用して最小値、最大値を取得する方法を見てみましょう

public void findMaxValue(){
   int[] my_array = {1,2,,6,5,8,3,9,0,23};
   int max = my_array[0];
   for(int i=1; i<my_array.length; i++)
   {
      if(my_array[i] > max)
         max = my_array[i];
   }
   return max; 
}

同じことが最小値を見つけるためにできる

驚いたことに、ここでは並列処理について言及していません。

本当に巨大な配列を取得した場合は、サブ範囲で並列処理を使用できます。最後に、すべてのサブ範囲を比較します。しかし、並列処理には幅のペナルティも伴うため、小さな配列では最適化されません。ただし、巨大なデータセットを取得すると意味があり始め、テストを実行するスレッドの量に近い時分割の削減が得られます。

0
user3800527

以下はo(n)を使用したソリューションです:-

public static void findMaxAndMinValue(int A[]){
    int min =0, max = 0;
    if(A[0] > A[1] ){
        min = A[1];
        max = A[0];
    }else{
        max = A[1];
        min = A[0];
    }
    for(int i = 2;i<A.length ;i++){
        if(A[i] > max){
            max = A[i];
        }
        if(min > A[i]){
            min = A[i];
        }
    }
    System.out.println("Maxinum Value is  "+min+" & Minimum Value is  "+max);
}
0
Ajay Kumar

最短の方法:

Math.min.apply(null、array); //これは配列から最小値を返します
Math.max.apply(null、array); //これは配列から最大値を返します

配列から最小値と最大値を取得する方法

 function maxVal(givenArray):Number
    {
    var max = givenArray[0];
    for (var ma:int = 0; ma<givenArray.length; ma++)
    {
    if (givenArray[ma] > max)
    {
    max = givenArray[ma];
    }
    }
    return max;
    }

    function minVal(givenArray):Number
    {
    var min = givenArray[0];
    for (var mi:int = 0; mi<givenArray.length; mi++)
    {
    if (givenArray[mi] < min)
    {
    min = givenArray[mi];
    }
    }
    return min;
    }

ご覧のとおり、これらの関数の両方のコードは非常に似ています。関数は変数-max(またはmin)を設定し、ループで配列を実行し、次の各要素をチェックします。次の要素が現在の要素よりも高い場合は、max(またはmin)に設定します。最後に、数値を返します。

0
sajan

最小と最大の両方を同時に見つけたい場合は、ループを次のように変更できます。

int min = int.maxValue;
int max = int.minValue;

foreach num in someArray {
  if(num < min)
    min = num;
  if(num > max)
    max = num;
}

これは達成する必要がありますO(n)タイミング。

0