web-dev-qa-db-fra.com

Moyen le plus rapide de trouver la valeur moyenne d'un triple?

Given est un tableau de trois valeurs numériques et j'aimerais connaître la valeur centrale des trois.

La question est, quel est le le plus rapide de manière trouver le milieu des trois?

Mon approche est la suivante: comme il y a trois chiffres, il y a six permutations:

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

Ce serait vraiment bien si quelqu'un pouvait m'aider à trouver un plus élégant et plus rapide façon de faire cela.

37
Gnark

Si vous recherchez la solution la plus efficace, j'imagine que c'est à peu près comme ceci:

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";
  }
}

Cette approche nécessite au moins deux et au plus trois comparaisons. Il ignore délibérément la possibilité que deux valeurs soient égales (tout comme votre question): si cela est important, l'approche peut être étendue pour vérifier cela également.

23
Tim

Il y a une réponse ici en utilisant min/max et sans branches ( https://stackoverflow.com/a/14676309/2233603 ). En fait 4 opérations min/max suffisent pour trouver la médiane, il n’est pas nécessaire d’utiliser xor:

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

Cependant, cela ne vous donnera pas l'indice de la valeur médiane ...

Ventilation de tous les cas:

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
72
Gyorgy Szekely

Il est possible de répondre à la requête sans branches si le matériel peut répondre aux requêtes min et max sans branches (la plupart des processeurs peuvent le faire aujourd'hui).

L'opérateur ^ désigne bitor 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

Ceci est correct parce que:

  • xor est commutatif et associatif
  • xor sur des bits égaux produit zéro
  • xor avec zéro ne change pas le bit

Les fonctions min/max appropriées doivent être choisies pour int/float . Si seuls des flottants positifs sont présents, il est possible d'utiliser un entier entier min/max directement sur la représentation en virgule flottante (cela peut être souhaitable, car les opérations sur les entiers sont généralement plus rapides ).

Dans le cas improbable où le matériel ne prend pas en charge min/max, il est possible de faire quelque chose comme ceci:

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

Cependant, cela n’est pas correct lorsqu’on utilise des opérations de type float, car les valeurs min/max exactes sont requises et non similaires. Heureusement, float min/max est pris en charge dans le matériel depuis des lustres (sur x86, à partir de Pentium III).

33
Max

Cela peut être fait avec deux comparaisons au maximum.

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;
}
18
Zach Conn

Et encore une idée. Il y a trois chiffres {a,b,c}. Ensuite:

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

Bien sûr, nous devons nous rappeler les limites numériques ...

11
jacek.ciach

Voici comment vous pouvez exprimer cela en utilisant uniquement des conditions:

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

EDITS: 

  1. Les erreurs ci-dessus trouvées par @Pagas ont été corrigées.
  2. @Pagas a également souligné que vous ne pouvez pas faire cela avec moins de 5 conditions si vous utilisez uniquement des conditions, mais vous pouvez le réduire en utilisant des variables temporaires ou un échange de valeurs. 
  3. J'ajouterais qu'il est difficile de prédire si une solution purement conditionnelle ou par assignation serait plus rapide. Cela dépendra probablement de la qualité du JIT, mais je pense que la version conditionnelle serait plus facile à analyser pour l'optimiseur.
7
Stephen C

Je n'ai pas vu de solution qui implémente des swaps:

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

Vous pourriez aussi bien écrire ceci de la manière la plus simple. Comme vous l'avez dit, il n'y a que six possibilités. Aucune approche raisonnable ne sera plus rapide ou plus lente, alors optez pour quelque chose de facile à lire. 

Je voudrais utiliser min () et max () pour la concision, mais trois imbriqués si/thens serait tout aussi bien, je pense 

3
Mark Bessey

Si vous devez trouver une des valeurs X satisfaisant certains critères, vous devez au moins comparer cette valeur à chacune des autres valeurs X-1. Pour trois valeurs, cela signifie au moins deux comparaisons. Comme il s'agit de "trouver la valeur qui n'est ni la plus petite ni la plus grande", vous pouvez vous en tirer avec seulement deux comparaisons.

Vous devez ensuite vous concentrer sur la rédaction du code afin de pouvoir voir très clairement ce qui se passe et rester simple. Ici, cela signifie si imbriqué. Cela permettra à la JVM d'optimiser cette comparaison autant que possible au moment de l'exécution.

Voir la solution fournie par Tim ( Le moyen le plus rapide de trouver la valeur moyenne d'un triple? ) pour voir un exemple. La ligne de code multiple ne s'avère pas nécessairement être un code plus volumineux que celui de questionmark-colon imbriqué.

Voici la réponse en Python, mais la même logique s'applique au programme 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

Sur la base de l'excellente réponse de Gyorgy, vous pouvez obtenir l'index de la médiane sans branche en remplaçant min/max par des mouvements conditionnels:

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 doit générer un ConditionalNode pour chacune de ces assignations ternaires, traduites en paires cmp/cmov dans Assembly. Notez également que les comparaisons ont été choisies de telle sorte qu'en cas d'égalité, le premier index par ordre alphabétique est renvoyé.

1
traffaillac

Méthode 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);

Méthode 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");
}

Méthode 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");
    }
}

Je me suis approprié de trouver la valeur moyenne d'un triple

1
manoj yadav

Le moyen le plus simple consiste à trier . Par exemple, considérons le code suivant:

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

C'est ça.C'est tellement simple.

De cette façon, vous n'avez pas besoin de considérer la taille du tableau. Ainsi, si vous avez comme 47 valeurs différentes, vous pouvez également utiliser ce code pour trouver la valeur intermédiaire.

1
Ratul Bin Tazul

Casser un vieux fil, mais c'est toujours la solution la plus courte, et personne ne l'a mentionnée.

Solution:

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

Tests:

(les tests couvrent toutes les combinaisons possibles, toutes imprimées 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));

}

Explication 1

(a > b) ^ (a > c) false si c > a > b ou c < a < b - return a;

sinon (a > b) ^ (b > c) false si a > b > c ou a < b < c - renvoie b;

sinon, retournez c;

Explication 2

Supposons que p = a > b; q = b > c; s = a > c;

Construisons une carte Karnaugh

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

* signifie que la combinaison est impossible (comme a > b; b > c; a < c)

Notez que la partie droite est une partie gauche en miroir, et la carte peut être simplifiée en introduisant t = p ^ q; u = s ^ p

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

Donc, la fonction peut être écrite comme 

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;
}

Inlining variables et remplacer ifs par?: Donne la réponse

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

La solution fonctionne bien même si certaines entrées sont égales, ce qui n’est peut-être pas évident, mais tout à fait logique.

1
ekaerovets

Celui-ci fonctionnera:

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

Utiliser idxA pour idxC dans ary,

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 pointe vers la valeur moyenne.

Explication: parmi les 3 minima, 2 sont le minimum global et l’autre valeur doit être le milieu. Puisque nous vérifions l’égalité, nous pouvons comparer les indices de la dernière ligne au lieu d’avoir à comparer les valeurs du tableau.

0
rsp

Version 100% sans branches pour les nombres entiers:

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));
}

Construit en utilisant les fonctions min/max sans branche: 

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

Cela peut ne pas sembler joli mais le code machine pourrait s’avérer plus efficace sur certaines architectures. Surtout ceux sans instructions min/max. Mais je n'ai pas fait de points de repère pour le confirmer.

0
Malström

Vous pouvez utiliser un tableau, comme ceci:

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