web-dev-qa-db-ja.com

トリプルの中間値を見つける最も速い方法は?

3つの数値の配列が与えられ、3つの値の中間値を知りたいのですが。

問題は、何ですか 最速 の方法 3つの中間を見つける

私のアプローチはこの種のパターンです-3つの数字があるので、6つの順列があります:

if (array[randomIndexA] >= array[randomIndexB] &&
    array[randomIndexB] >= array[randomIndexC])

誰かが私を見つけるのを手伝うことができれば、それは本当に素晴らしいでしょう よりエレガント そして もっと早く これを行う方法。

42
Gnark

あなたが最も効率的なソリューションを探しているなら、私はそれが次のようなものだと思います:

if (array[randomIndexA] > array[randomIndexB]) {
  if (array[randomIndexB] > array[randomIndexC]) {
    return "b is the middle value";
  } else if (array[randomIndexA] > array[randomIndexC]) {
    return "c is the middle value";
  } else {
    return "a is the middle value";
  }
} else {
  if (array[randomIndexA] > array[randomIndexC]) {
    return "a is the middle value";
  } else if (array[randomIndexB] > array[randomIndexC]) {
    return "c is the middle value";
  } else {
    return "b is the middle value";
  }
}

このアプローチでは、少なくとも2回、最大3回の比較が必要です。 2つの値が等しくなる可能性を意図的に無視します(質問と同様)。これが重要な場合は、アプローチを拡張してこれを確認することもできます。

25
Tim

ここには、min/maxを使用し、ブランチを使用しないという答えがあります( https://stackoverflow.com/a/14676309/22336 )。実際には、中央値を見つけるには4 min/max操作で十分です。xorの必要はありません。

median = max(min(a,b), min(max(a,b),c));

ただし、中央値のインデックスは表示されません...

すべてのケースの内訳:

a b c
1 2 3   max(min(1,2), min(max(1,2),3)) = max(1, min(2,3)) = max(1, 2) = 2
1 3 2   max(min(1,3), min(max(1,3),2)) = max(1, min(3,2)) = max(1, 2) = 2
2 1 3   max(min(2,1), min(max(2,1),3)) = max(1, min(2,3)) = max(1, 2) = 2
2 3 1   max(min(2,3), min(max(2,3),1)) = max(2, min(3,1)) = max(2, 1) = 2
3 1 2   max(min(3,1), min(max(3,1),2)) = max(1, min(3,2)) = max(1, 2) = 2
3 2 1   max(min(3,2), min(max(3,2),1)) = max(2, min(3,1)) = max(2, 1) = 2
79
Gyorgy Szekely

ハードウェアがブランチなしでminおよびmaxクエリに回答できる場合、ブランチなしでクエリに回答することが可能です(今日のほとんどのCPUはこれを実行できます)。

演算子^はビット単位のxorを示します。

Input: triple (a,b,c)
1. mx=max(max(a,b),c)
2. mn=min(min(a,b),c)
3. md=a^b^c^mx^mn
4. return md

これは正しいためです。

  • xorは可換および結合的です
  • 等しいビットのxorはゼロを生成します
  • ゼロのxorはビットを変更しません

Int/floatには適切なmin/max関数を選択する必要があります。正のフロートのみが存在する場合、浮動小数点表現で整数min/maxを直接使用することができます(整数演算は一般に高速であるため、これが望ましい場合があります)。

ハードウェアがmin/maxをサポートしないというまれなシナリオでは、次のようなことができます。

max(a,b)=(a+b+|a-b|)/2
min(a,b)=(a+b-|a-b|)/2

ただし、float操作を使用する場合、正確なmin/maxが必要であり、それに近いものではないため、これは正しくありません。幸いなことに、float min/maxは古くからハードウェアでサポートされています(x86、Pentium III以降)。

35
Max

これは、最大で2つの比較で実行できます。

int median(int a, int b, int c) {
    if ( (a - b) * (c - a) >= 0 ) // a >= b and a <= c OR a <= b and a >= c
        return a;
    else if ( (b - a) * (c - b) >= 0 ) // b >= a and b <= c OR b <= a and b >= c
        return b;
    else
        return c;
}
20
Zach Conn

そしてもう1つのアイデア。 3つの数字があります{a,b,c}。次に:

middle = (a + b + c) - min(a,b,c) - max(a,b,c);

もちろん、数値の制限については覚えておく必要があります...

13
jacek.ciach

条件のみを使用してこれを表現する方法は次のとおりです。

int a, b, c = ...
int middle = (a <= b) 
    ? ((b <= c) ? b : ((a < c) ? c : a)) 
    : ((a <= c) ? a : ((b < c) ? c : b));

編集:

  1. @Pagasで見つかった上記のエラーは修正されました。
  2. @Pagasは、条件のみを使用する場合、5つ未満の条件ではこれを実行できないことも指摘しましたが、一時変数または値交換を使用してこれを減らすことができます。
  3. 純粋な条件付きソリューションまたは割り当てソリューションの方が速いかどうかを予測するのは難しいと付け加えます。 JITがどれだけ優れているかに依存する可能性がありますが、オプティマイザーが条件付きバージョンを分析する方が簡単だと思います。
7
Stephen C

スワップを実装するソリューションが見つかりませんでした:

int middle(int a, int b, int c) {
    // effectively sort the values a, b & c
    // putting smallest in a, median in b, largest in c

    int t;

    if (a > b) {
        // swap a & b
        t = a;
        a = b;
        b = t;
    }

    if (b > c) {
        // swap b & c
        t = b;
        b = c;
        c = t;

        if (a > b) {
            // swap a & b
            t = a;
            a = b;
            b = t;
        }
    }

    // b always contains the median value
    return b;
}
4
mckamey

これを最も簡単な方法で書くこともできます。あなたが言ったように、たった6つの可能性があります。より高速または低速になる合理的なアプローチはないので、読みやすいものを探してください。

簡潔にするためにmin()とmax()を使用しますが、3つのネストされたif/thenも同じように良いと思います。

3
Mark Bessey

いくつかの基準を満たすX値の1つを見つける必要がある場合、少なくともその値を他のX-1のそれぞれと比較する必要があります。 3つの値の場合、これは少なくとも2つの比較を意味します。これは「最小でも最大でもない値を見つける」ことなので、2つの比較だけで済みます。

次に、コードの記述に集中して、何が起こっているかを非常に明確に確認し、シンプルに保つ必要があります。ここで、これはネストされたifを意味します。これにより、JVMは実行時にこの比較を可能な限り最適化できます。

この例を参照するには、Timが提供するソリューション( トリプルの中間値を見つける最も速い方法? )を参照してください。多くのコード行は、ネストされた疑問符のコロンよりも大きなコードであるとは限りません。

median = (a+b+c) - Math.min(Math.min(a,b),c) - Math.max(Math.max(a,b),c)

これは基本的なものです。これがどれほど効率的かはわかりませんが、これらの関数はif条件を使用します。必要に応じて、このステートメントをif-elseステートメントに変換できますが、時間がかかります。なぜそんなに怠zyなのか?

2
Melih Yıldız'

最も簡単な方法は並べ替えです。たとえば、次のコードを検討してください。

import Java.util.Arrays;


int[] x = {3,9,2};
Arrays.sort(x); //this will sort the array in ascending order 

//so now array x will be x = {2,3,9};
//now our middle value is in the middle of the array.just get the value of index 1
//Which is the middle index of the array.

int middleValue = x[x.length/2]; // 3/2 = will be 1

それだけです。とても簡単です。

この方法では、配列のサイズを考慮する必要はありません。したがって、47の異なる値がある場合は、このコードを使用して中央の値を見つけることもできます。

2
Ratul Bin Tazul

Pythonでの答えはここにありますが、同じロジックがJavaプログラムに適用されます。

def middleOfThree(a,b,c):
    middle = a
    if (a < b and b < c) or (c < b and b < a):
        middle = b 
    Elif (a < c and c < b) or (b < c and c < a):
        middle = c    
    print 'Middle of a=%d, b=%d, c=%d is %d' % (a,b,c,middle)

middleOfThree(1,2,3)
middleOfThree(1,3,2)
middleOfThree(2,1,3)
middleOfThree(2,3,1)
middleOfThree(3,2,1)
middleOfThree(3,1,2)
1
Pike Zarate

Gyorgyからの優れた回答に基づいて、min/maxを条件付き移動に置き換えることにより、分岐なしで中央値のインデックスを取得できます。

int i = (array[A] >= array[B]) ? A : B;
int j = (array[A] <= array[B]) ? A : B;
int k = (array[i] <= array[C]) ? i : C;
int median_idx = (array[j] >= array[k]) ? j : k;

javacは、これらの3項割り当てごとにConditionalNodeを生成し、cmp/cmovアセンブリ内のペア。また、比較は、等しい場合にアルファベット順の最初のインデックスが返されるように選択されていることに注意してください。

1
traffaillac

方法1

int a,b,c,result;
printf("enter three number");
scanf("%d%d%d",&a,&b,&c);
result=a>b?(c>a?a:(b>c?b:c)):(c>b?b:(a>c?a:c));
printf("middle %d",result);

方法2

int a=10,b=11,c=12;
//Checking for a is middle number or not
if( b>a && a>c || c>a && a>b )
{
    printf("a is middle number");
}

//Checking for b is middle number or not
if( a>b && b>c || c>b && b>a )
{
    printf("b is middle number");
}

//Checking for c is middle number or not
if( a>c && c>b || b>c && c>a )
{
    printf("c is middle number");
}

方法3

if(a>b)
{
    if(b>c)
    {
        printf("b is middle one");
    }
    else if(c>a)
    {
        printf("a is middle one");
    }
    else
    {
        printf("c is middle one");
    }
}
else
{
    if(b<c)
    {
        printf("b is middle one");
    }
    else if(c<a)
    {
        printf("a is middle one");
    }
    else
    {
        printf("c is middle one");
    }
}

トリプルの中間値を見つける の適切なansを得ました

1
manoj yadav

古いスレッドを増やしたが、それでも最短の解決策であり、誰もそれについて言及しなかった。

解決策:

int median2(int a, int b, int c) {
    return (a > b) ^ (a > c) ? a : (a > b) ^ (b > c) ? c : b;
}

テスト:

(テストはすべての可能な組み合わせをカバーし、すべてが6を印刷します)

public static void main(String[] args) {

    System.out.println(median(3, 6, 9));
    System.out.println(median(3, 9, 6));
    System.out.println(median(6, 3, 9));
    System.out.println(median(6, 9, 3));
    System.out.println(median(9, 3, 6));
    System.out.println(median(9, 6, 3));
    System.out.println(median(6, 6, 3));
    System.out.println(median(6, 6, 9));
    System.out.println(median(6, 3, 6));
    System.out.println(median(6, 9, 6));
    System.out.println(median(3, 6, 6));
    System.out.println(median(9, 6, 6));
    System.out.println(median(6, 6, 6));

}

説明1

(a > b) ^ (a > c)c > a > bまたはc < a < bの場合はfalse-aを返します。

それ以外の場合は、(a > b) ^ (b > c)またはa > b > cの場合はa < b < c false-bを返します。

それ以外の場合はcを返します。

説明2

p = a > b;と仮定しましょう。 q = b > c; s = a > c;

Karnaugh map を作成しましょう。

   | 00  01  11  10 (p, q)
---+----------------------
 0 |  b   c   *   a
 1 |  *   a   b   c
(s)|

*は、組み合わせが不可能であることを意味します(a > b; b > c; a < cなど)

右の部分はミラーされた左の部分であり、マップはt = p ^ q; u = s ^ pを導入することで簡略化できることに注意してください。

   |  0   1 (t)
---+---------
 0 |  b   c  
 1 |  *   a  
(u)|

したがって、関数は次のように記述できます。

private static int median(int a, int b, int c) {
    boolean t = (a > b) ^ (b > c);
    boolean u = (a > b) ^ (a > c);
    if (u)
        return a;
    else if (t)
        return c;
    else
        return b;
}

変数をインライン化し、ifsを?に置き換えると、答えが得られます

int median2(int a, int b, int c) {
    return (a > b) ^ (a > c) ? a : (a > b) ^ (b > c) ? c : b;
}

入力の一部が等しい場合でも、ソリューションは正常に機能します。これは明らかではないかもしれませんが、かなり論理的です。

1
ekaerovets

これは動作します:

template<typename T> T median3_1_gt_2(const T& t1, const T& t2, const T& t3) {
    if (t3>t1) {
        return t1;
    } else {
        return std::max(t2, t3);
    }
}
template<typename T> T median3(const T& t1, const T& t2, const T& t3) {
    if (t1>t2) {
        return median3_1_gt_2(t1, t2, t3);
    } else {
        return median3_1_gt_2(t2, t1, t3);
    }
}

https://github.com/itroot/firing-ground/blob/864e26cdfced8394f8941c8c9d97043da8f998b4/source/median3/main.cpp

1
    if(array[aIndex] > array[bIndex]) {
        if(array[bIndex] > array[cIndex]) return bIndex;
        if(array[aIndex] > array[cIndex]) return cIndex;
        return aIndex;
    } else {
        if(array[bIndex] < array[cIndex]) return bIndex;
        if(array[aIndex] < array[cIndex]) return cIndex;
        return aIndex;
    }
1
azmi
largest=(a>b)&&(a>c)?a:(b>c?b:c);
smallest=(a<b)&&(a<c)?a:(b<c?b:c);
median=a+b+c-largest-smallest;
1
Mykola Lavrenko

AryでidxAからidxCを使用して、

int ab = ary[idxA] < ary[idxB] ? idxA : idxB;
int bc = ary[idxB] < ary[idxC] ? idxB : idxC;
int ac = ary[idxA] < ary[idxC] ? idxA : idxC;

int idxMid = ab == bc ? ac : ab == ac ? bc : ab;

indexMiddleは中間値を指します。

説明:3つの最小値から2は全体の最小値であり、他の値は中間値でなければなりません。等価性をチェックするため、配列値を比較する代わりに、最後の行のインデックスを比較できます。

0
rsp

整数の100%ブランチフリーバージョン:

int mid(const int a, const int b, const int c) {
    const int d0 = b - a;
    const int m = (d0 >> 31);
    const int min_ab = a + (d0 & m);
    const int max_ab = a + (d0 & ~m);
    const int d1 = c - max_ab;
    const int min_max_ab_c = max_ab + (d1 & (d1 >> 31));
    const int d2 = min_ab - min_max_ab_c;
    return min_ab - (d2 & (d2 >> 31));
}

ブランチフリーの最小/最大関数を使用して構築:

int min(const int a, const int b) { const int d = b - a; return a + (d & (d >> 31)); }
int max(const int a, const int b) { const int d = a - b; return a - (d & (d >> 31)); }

きれいに見えないかもしれませんが、一部のアーキテクチャではマシンコードがより効率的であることが判明する場合があります。特に、最小/最大の指示がないもの。しかし、私はそれを確認するベンチマークを作成していません。

0
Malström

次のように配列を使用できます。

private static long median(Integer i1, Integer i2, Integer i3) {

    List<Integer> list = Arrays.asList(
            i1 == null ? 0 : i1,
            i2 == null ? 0 : i2,
            i3 == null ? 0 : i3);

    Collections.sort(list);
    return list.get(1);
}
0
cizek