web-dev-qa-db-fra.com

Tri d'une gamme de nombres avec des décimales

Supposons que j'ai une gamme de valeurs de flotteur dans le format suivant:

{ 1.34, 1.15, 1.1, 1.2, 1.26, 1.10, 1.20, 1.17 }

Supposons qu'ils ont été fournis par l'utilisateur de l'utilisateur (ou un autre mécanisme), où l'utilisateur prend "1.1" pour signifier "1.01" - ce qui signifie qu'il est séparé de "1.10".

L'utilisation d'un algorithme de tri standard (tri de bulles, tri rapide, ou certains types de charme spécifique) se traduira par une matrice qui devrait ressembler à ce qui suit:

{ 1.1, 1.10, 1.15, 1.17, 1.2, 1.20, 1.26, 1.34 }

Cependant, la matrice de sortie requise serait la suivante:

{ 1.1, 1.2, 1.10, 1.15, 1.17, 1.20, 1.26, 1.34 }

Je pense que la façon de le faire serait de faire itérair le tableau avant de trier et de:

  • vérifiez si la valeur d'a n décimales
  • si oui, élever des décimales M (N + 1) - Ajoutez un 0?

Cela conduirait à deux matrices, une valeur contenant des valeurs avec N ou M NUMÉMENTS DECIMAL (entrée d'utilisateur brut) et une contenant toutes les valeurs avec des immeubles de nombres M (entrée de l'utilisateur "désinfectée"). Ce qui signifierait que le tri de la matrice contenant des valeurs avec M décimaux fournirait le résultat requis.

Y a-t-il d'autres moyens de faire cela? Je cherche des algorithmes plus rapides, ou ceux avec une surcharge inférieure.

Je vois cela comme étant un domaine problématique commun, et je vais deviner qu'il existe une multitude de façons de résoudre ce problème. Quels sont certains des autres algorithmes pouvant être utilisés pour atteindre le résultat requis.

3
Jamie Taylor

Votre problème est que vos "chiffres" n'ont pas de décimales. Vous avez une chaîne qui consiste en deux entiers séparés par .. Analysez-les comme deux entiers et écrivez un comparateur personnalisé. L'algorithme de tri peut rester le même, il vous suffit de passer dans le comparateur.

En C # Un tel comparateur pourrait sembler similaire à celui-ci:

void Test()
{
    var data=new string[]{ "1.34", "1.15", "1.1", "1.2", "1.26", "1.10", "1.20", "1.17" };
    Array.Sort(data, VersionComparer);
    data.Dump();
}

static int VersionComparer(string s1, string s2)
{
    List<int> parts1 = s1.Split('.').Select(int.Parse).ToList();
    List<int> parts2 = s2.Split('.').Select(int.Parse).ToList();

    while(parts1.Count < parts2.Count)
        parts1.Add(0);
    while(parts2.Count < parts1.Count)
        parts2.Add(0);

    for(int i = 0; i < parts1.Count; i++)
    {
         if(parts1[i] < parts2[i])
             return -1;
         if(parts1[i] > parts2[i])
             return +1;
    }
    return 0;
}
9
CodesInChaos

Un peu de nitpicking: si certaines valeurs ont des chiffres plus significatifs que d'autres, ce que vous avez, c'est pas flotteurs. Ce sont des cordes destinées à être interprétées comme des flotteurs, mais vous devez les interpréter à chaque fois que vous y accédez, et c'est précisément le problème ici.

Fondamentalement, vous avez deux options. Soit vous utilisez un algorithme de tri standard de tourbière et programmez simplement votre routine de comparaison de sorte qu'elle effectue Float.parseFloat() avant de calculer quoi que ce soit avec ses arguments - ou vous effectuez une analyse une fois dans une étape séparée et stockez le numérique. Valeurs quelque part, vous n'avez donc pas à le refaire.

De toute évidence, quelle solution dépend mieux de la fréquence à laquelle le comparateur sera appelé. Si votre liste est dans un ordre aléatoire, la plupart des valeurs seront probablement touchées plus d'une fois pendant le tri, de manière pré-calculer les valeurs que vous souhaitez réellement comparer. Mais si la liste a tendance à être quasi triée ou si l'analyse de flotteur est trop bon marché pour vous inquiéter (par rapport aux efforts de programmation d'ajout d'une étape de transformation), alors faites-le à la volée pendant chaque comparaison est meilleure.

Comme toujours, lequel est le meilleur en général On ne peut pas répondre et lequel est meilleur dans votre cas On ne peut pas répondre sans profiler les deux solutions. Ne présumez jamais - toujours mesurer.

5
Kilian Foth

Selon la taille de la taille d'un tableau avec vous, vous pouvez appliquer le motif de programmation fonctionnelle de programmation "Trier trier de trier" (qui est une forme de mémoisation ).

Séfonçant constamment la chaîne dans des pièces obtenant sa longueur puis jeter les données peut être inefficace, après tout, la chaîne ne change pas pendant que vous le triez, il vous suffit de le faire une fois.

Dans Perl, il est connu sous le nom de Transformer Schwartzian - Le code à titre ressemble à:

#!/usr/bin/Perl
use strict;
my @data = qw ( 1.34 1.15 1.1 1.2 1.26 1.10 1.20 1.17 );
my @sorted =
    map { $_->[0] }
    sort { $a->[1] <=> $b->[1] or $a->[2] <=> $b->[2]  }
    map {
      my @l = split(/\./);
      [$_, length($l[1]), $l[1]] }
    @data;
print join(',', @sorted),"\n";

Le truc est que le changement de "1,15" à ["1,15", 2, 15] n'est fait qu'une fois, puis le tri fonctionne de ces valeurs. Pour les tableaux de petite taille, il s'agit d'un boost mineur de performance. Pour les grandes matrices, cela peut être très important.

Dans des langues qui manquent de "viennent de lancer des choses dans un tableau et de la trier cependant", cela devient un peu plus complexe. Vous auriez besoin de créer un objet qui possède les données d'origine et les composants et le tri. Ceci est légèrement plus facile à travailler, car vous pourriez le mettre dans le constructeur.

En Java, on pourrait utiliser l'approche suivante:

public class DecimalSortable implements Comparable<DecimalSortable> {
    private int len;
    private int dec;
    private String data;
    public DecimalSortable(String data) {
        this.data = data;
        String[] array = data.split("\\.");
        len = array[1].length();
        dec = Integer.parseInt(array[1]);
    }

    @Override
    public int compareTo(DecimalSortable o) {
        int rv = Integer.compare(len, o.len);
        if(rv == 0) {
            rv = Integer.compare(dec, o.dec);
        }
        return rv;
    }

    public String getData() {
        return data;
    }
}

Notez l'appel unique à scinder et à extraire des valeurs qui sont ensuite triées. Oui, c'est un peu plus de frais généraux que les langues plus dynamiques, mais vous obtenez l'idée de la façon dont cela fonctionne et comment éviter les appels répétés à Split (et la création associée de nouveaux objets de chaîne pour chaque comparaison).

3
user40980