web-dev-qa-db-fra.com

Algorithme pour diviser une gamme en gammes, puis trouver la plage d'un nombre appartenant à

Ayant

  • un minimum
  • un maximum
  • nombre de gammes
  • une valeur entre minimum et maximum

J'essaie de trouver une méthode, ou deux, ce qui calculerait quelle portée la valeur fournie appartient à.

Pour min = 1, max = 10, nombre de gammes = 5 Les gammes seraient [1,2], [3,4], [5,6], [7,8], [9-10], [9-10]

L'autre méthode se comporterait comme indiqué ci-dessous:

  • méthode (1) -> [1-2]
  • méthode (2) -> [1-2]
  • méthode (3) -> [3-4]
  • méthode (4) -> [3-4]
  • méthode (5) -> [5-6]
  • méthode (6) -> [5-6]
  • méthode (7) -> [7-8]
  • méthode (8) -> [7-8]
  • méthode (9) -> [9-10]
  • méthode (10) -> [9-10]

Ceci serait utilisé pour générer une légende pour une carte où la taille du marqueur dépend de la plage qu'une valeur appartient.

Je me demande s'il y a une belle solution algorithmique pour cela.

Les chiffres que je travaille sont des entiers.

Éditer:

Un autre exemple:

Pour min = 1, max = 3, nombre de gammes = 2 les gammes seraient

a) [1-2], [3-3]

ou

b) [1-1], [2-3]

L'autre méthode se comporterait comme indiqué ci-dessous:

une)

  • méthode (1) -> [1-2]
  • méthode (2) -> [1-2]
  • méthode (3) -> [3-3]

ou b)

  • méthode (1) -> [1-1]
  • méthode (2) -> [2-3]
  • méthode (3) -> [2-3]

Je n'ai pas de préférence pour a) ou b).

7
tymtam

Voici ce que je ferais:

Commencez par commencer par un tableau de la taille du nombre de gammes pour garder une trace de la longueur de chaque plage. Appelons ceci bucket_sizes[number_of_ranges]

  1. Initialisez la taille de chaque godet avec la longueur maximale uniformément possible: (max-min+1)/number_of_ranges (Division entière)
  2. Ensuite, trouvez le surplus qui ne pouvait pas correspondre uniformément dans chaque godet, (max-min+1) % number_of_ranges (reste de la division entière)
  3. Distribuez le surplus aussi uniformément que possible entre chaque godet (démarrez à l'index 0, ajoutez 1 à chaque godet tout en soustrayant 1 du surplus. Si l'index s'enveloppait à la fin de la matrice Bucket_size, commencez à l'index 0 et continuez jusqu'à ce que le surplus soit 0).

Maintenant que nous connaissons la taille de chaque seau, nous pouvons générer les gammes:

for (i=0, k=min; i<number_of_ranges; i++) {
  ranges[i].lo = k;
  ranges[i].hi = k+bucket_sizes[i]-1;
  k += bucket_sizes[i];
}

Pour trouver la plage d'un numéro spécifique, itérer simplement le tableau ranges et correspond à la plage où ranges[i].lo <= number <= ranges[i].hi.

Voici le code source complet que j'ai utilisé pour tester cela (c'est écrit en C):

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct range
{
    int lo;
    int hi;
};

int generate_ranges(int min, int max, int number_of_ranges, struct range ranges[])
{
    int i;
    int bucket_sizes[number_of_ranges];

    int even_length = (max-min+1)/number_of_ranges;
    for(i=0; i<number_of_ranges; ++i)
        bucket_sizes[i] = even_length;

    /* distribute surplus as evenly as possible across buckets */
    int surplus = (max-min+1)%number_of_ranges;
    for(i=0; surplus>0; --surplus, i=(i+1)%number_of_ranges)
        bucket_sizes[i] += 1; 

    int n=0, k=min;
    for(i=0; i<number_of_ranges && k<=max; ++i, ++n){
        ranges[i].lo=k;
        ranges[i].hi=k+bucket_sizes[i]-1;
        k += bucket_sizes[i];
    }
    return n;
}

int number_range_index(int number, int number_of_ranges, const struct range ranges[]) {
    int i;
    for(i=0; i<number_of_ranges; ++i)
        if(number >= ranges[i].lo && number <= ranges[i].hi)
            return i;
    return number_of_ranges;
}


#define MAX_RANGES 50

int main(int argc, char *argv[]) {
    int i;
    struct range ranges[MAX_RANGES];

    if(argc != 5) {
        printf("usage: %s <min> <max> <number_of_ranges> <number>\n", argv[0]);
        return EXIT_FAILURE;
    }

    int min = atoi(argv[1]);
    int max = atoi(argv[2]);
    int number_of_ranges = atoi(argv[3]);
    int number = atoi(argv[4]);

    assert(max > min);
    assert(number >= min && number <= max);
    assert(number_of_ranges > 0);
    assert(number_of_ranges <= MAX_RANGES);

    printf("min=%d max=%d number_of_ranges=%d number=%d\n\n", min, max, number_of_ranges, number);

    int n = generate_ranges(min, max, number_of_ranges, ranges);
    for(i=0; i<number_of_ranges; i++) {
        if(i<n)
            printf("%s[%d-%d]", i>0?",":"", ranges[i].lo, ranges[i].hi);
        else
            printf("%s[]", i>0?",":"");
    }
    printf("\n\n");

    int number_idx = number_range_index(number, n, ranges);
    printf("method(%d)->[%d,%d]\n", number, ranges[number_idx].lo, ranges[number_idx].hi);

    return EXIT_SUCCESS;
}
8
Oskar N.

Soit n être le nombre de gammes. Si vous pouvez diviser votre gamme en étagères égales, vous pouvez le faire comme ceci:

length_of_range = (max - min + 1) / n

For i = 1 to n:
start_of_range(i) = length_of_range * (i-1) + min  
end_of_range(i) = start_of_range(i) + length_of_range - 1   

method(number) = (number - min) / length_of_range + 1   // '/' is integer division

Si vous ne pouvez pas les diviser en interranges égales, le premier (max - min + 1) % n Les subranges devraient avoir de la longueur ((max - min + 1) / n) + 1 Et le reste devrait avoir de la longueur (max - min + 1) / n. Sachant que vous devriez être capable d'ajuster vous-même les formules ci-dessus.

2
iCanLearn

Voici une version C++ 11 de la réponse de Oskar N:

/** Divides a given range of values into consecutive sub-ranges as evenly as possible.
 * Returns a vector of pairs.  The first member of each pair is the min and the second, the max.
 */
std::vector< std::pair<int, int> > generateSubRanges( int mainRangeMin,
                                                      int mainRangeMax,
                                                      int numberOfSubRanges )
{
   std::vector<std::pair<int, int> > result;
   std::vector<int> bucket_sizes;
   int i;

   //init vectors
   bucket_sizes.reserve( numberOfSubRanges );
   result.reserve( numberOfSubRanges );
   for( i = 0; i < numberOfSubRanges; ++i ){
       bucket_sizes.Push_back( 0 );
       result.Push_back( {0, 0} );
   }

   int even_length = (mainRangeMax-mainRangeMin+1)/numberOfSubRanges;
   for(i=0; i<numberOfSubRanges; ++i)
       bucket_sizes[i] = even_length;

   /* distribute surplus as evenly as possible across buckets */
   int surplus = (mainRangeMax-mainRangeMin+1)%numberOfSubRanges;
   for(i=0; surplus>0; --surplus, i=(i+1)%numberOfSubRanges)
       bucket_sizes[i] += 1;

   int n=0, k=mainRangeMin;
   for(i=0; i<numberOfSubRanges && k<=mainRangeMax; ++i, ++n){
       result[i] = { k, k+bucket_sizes[i]-1 };
       k += bucket_sizes[i];
   }

   return result;
}
0
Paulo Carvalho