J'essaye de travailler avec des fractions en Java.
Je souhaite implémenter des fonctions arithmétiques. Pour cela, je vais d'abord demander un moyen de normaliser les fonctions. Je sais que je ne peux pas ajouter 1/6 et 1/2 avant d'avoir un dénominateur commun. Je devrai ajouter 1/6 et 3/6. Une approche naïve me ferait ajouter 2/12 et 6/12 puis réduire. Comment puis-je obtenir un dénominateur commun avec la moindre pénalité de performance? Quel algorithme est le meilleur pour cela?
Version 8 (merci à hstoerr ):
Les améliorations comprennent:
- la méthode equals () est désormais cohérente avec la méthode compareTo ()
final class Fraction extends Number {
private int numerator;
private int denominator;
public Fraction(int numerator, int denominator) {
if(denominator == 0) {
throw new IllegalArgumentException("denominator is zero");
}
if(denominator < 0) {
numerator *= -1;
denominator *= -1;
}
this.numerator = numerator;
this.denominator = denominator;
}
public Fraction(int numerator) {
this.numerator = numerator;
this.denominator = 1;
}
public int getNumerator() {
return this.numerator;
}
public int getDenominator() {
return this.denominator;
}
public byte byteValue() {
return (byte) this.doubleValue();
}
public double doubleValue() {
return ((double) numerator)/((double) denominator);
}
public float floatValue() {
return (float) this.doubleValue();
}
public int intValue() {
return (int) this.doubleValue();
}
public long longValue() {
return (long) this.doubleValue();
}
public short shortValue() {
return (short) this.doubleValue();
}
public boolean equals(Fraction frac) {
return this.compareTo(frac) == 0;
}
public int compareTo(Fraction frac) {
long t = this.getNumerator() * frac.getDenominator();
long f = frac.getNumerator() * this.getDenominator();
int result = 0;
if(t>f) {
result = 1;
}
else if(f>t) {
result = -1;
}
return result;
}
}
J'ai supprimé toutes les versions précédentes. Mes remerciements à:
Réponses:
Il se trouve que j'ai écrit une classe BigFraction il n'y a pas si longtemps, pour des problèmes de Project Euler . Il garde un numérateur et un dénominateur BigInteger, donc il ne débordera jamais. Mais ce sera un peu lent pour beaucoup d'opérations dont vous savez qu'elles ne déborderont jamais ... de toute façon, utilisez-la si vous le voulez. Je meurs d'envie de montrer ça d'une manière ou d'une autre. :)
Edit : La dernière et la meilleure version de ce code, y compris les tests unitaires, est désormais hébergée sur GitHub et également disponible via Maven Central . Je laisse mon code d'origine ici pour que cette réponse ne soit pas qu'un lien ...
la source
BigInteger
pour stocker des valeurs arbitrairement précises. Sinonlong
, alors , qui a une mise en œuvre plus facile;Number
;Comparable<T>
;equals()
ethashCode()
;String
;toString()
; etSerializable
.En fait, essayez ceci pour la taille. Il fonctionne mais peut avoir quelques problèmes:
La sortie est:
la source
Apache Commons Math a une classe Fraction depuis un certain temps. La plupart du temps, la réponse à "Boy, je souhaite que Java ait quelque chose comme X dans la bibliothèque principale!" peut être trouvé sous l'égide de la bibliothèque Apache Commons .
la source
Veuillez en faire un type immuable! La valeur d'une fraction ne change pas - une moitié ne devient pas un tiers, par exemple. Au lieu de setDenominator, vous pourriez avoir withDenominator qui renvoie une nouvelle fraction qui a le même numérateur mais le dénominateur spécifié.
La vie est beaucoup plus facile avec les types immuables.
Remplacer equals et hashcode serait également judicieux, il peut donc être utilisé dans les cartes et les ensembles. Les points d'Outlaw Programmer concernant les opérateurs arithmétiques et le formatage des chaînes sont également bons.
En règle générale, jetez un œil à BigInteger et BigDecimal. Ils ne font pas la même chose, mais ils sont assez similaires pour vous donner de bonnes idées.
la source
Eh bien, d'une part, je me débarrasserais des setters et rendrais Fractions immuables.
Vous voudrez probablement aussi des méthodes pour ajouter, soustraire, etc., et peut-être un moyen d'obtenir la représentation dans divers formats de chaîne.
EDIT: Je marquerais probablement les champs comme `` finaux '' pour signaler mon intention mais je suppose que ce n'est pas un gros problème ...
la source
la source
Pas strictement nécessaire. (En fait, si vous voulez gérer l'égalité correctement, ne comptez pas sur double pour fonctionner correctement.) Si b * d est positif, a / b <c / d si ad <bc. S'il y a des entiers négatifs impliqués, cela peut être géré de manière appropriée ...
Je pourrais réécrire comme:
L'utilisation de
long
ici est de s'assurer qu'il n'y a pas de débordement si vous multipliez deux grandesint
s. handle Si vous pouvez garantir que le dénominateur est toujours non négatif (s'il est négatif, niez simplement le numérateur et le dénominateur), alors vous pouvez vous débarrasser de la nécessité de vérifier si b * d est positif et économiser quelques étapes. Je ne sais pas quel comportement vous recherchez avec un dénominateur nul.Je ne sais pas comment les performances se comparent à l'utilisation de doubles pour comparer. (Autrement dit, si vous vous souciez autant des performances) Voici une méthode de test que j'ai utilisée pour vérifier. (Semble fonctionner correctement.)
(ps vous pourriez envisager de restructurer pour implémenter
Comparable
ouComparator
pour votre classe.)la source
Une amélioration très mineure pourrait potentiellement être de sauvegarder la double valeur que vous calculez afin de ne la calculer qu'au premier accès. Ce ne sera pas une grande victoire à moins que vous n'accédiez beaucoup à ce numéro, mais ce n'est pas trop difficile à faire non plus.
Un point supplémentaire pourrait être la vérification d'erreur que vous faites dans le dénominateur ... vous changez automatiquement 0 en 1. Je ne sais pas si cela est correct pour votre application particulière, mais en général, si quelqu'un essaie de diviser par 0, quelque chose ne va pas. . Je laisserais cela lancer une exception (une exception spécialisée si vous pensez que c'est nécessaire) plutôt que de changer la valeur d'une manière apparemment arbitraire qui n'est pas connue de l'utilisateur.
Contrairement à d'autres commentaires, sur l'ajout de méthodes pour ajouter une soustraction, etc ... puisque vous n'avez pas mentionné le besoin, je suppose que vous ne le faites pas. Et à moins que vous ne construisiez une bibliothèque qui sera vraiment utilisée dans de nombreux endroits ou par d'autres personnes, optez pour YAGNI (vous n'en aurez pas besoin, donc elle ne devrait pas être là.)
la source
Il existe plusieurs façons d'améliorer ce type de valeur ou tout autre type de valeur:
En gros, jetez un œil à l'API pour d'autres classes de valeur comme Double , Integer et faites ce qu'elles font :)
la source
Si vous multipliez le numérateur et le dénominateur d'une fraction par le dénominateur de l'autre et vice versa, vous vous retrouvez avec deux fractions (qui sont toujours les mêmes valeurs) avec le même dénominateur et vous pouvez comparer les numérateurs directement. Par conséquent, vous n'avez pas besoin de calculer la valeur double:
la source
comment j'améliorerais ce code:
la source
Vous avez déjà une fonction compareTo ... J'implémenterais l'interface Comparable.
Cependant, peu importe ce que vous allez en faire.
la source
Si vous vous sentez aventureux, jetez un œil à JScience . Il a une
Rational
classe qui représente des fractions.la source
Je dirais de lancer une ArithmeticException pour diviser par zéro, car c'est vraiment ce qui se passe:
Au lieu de «Diviser par zéro», vous pouvez faire en sorte que le message dise «Diviser par zéro: le dénominateur de la fraction est zéro».
la source
Une fois que vous avez créé un objet fraction, pourquoi voudriez-vous autoriser d'autres objets à définir le numérateur ou le dénominateur? Je pense que ceux-ci devraient être en lecture seule. Cela rend l'objet immuable ...
Aussi ... définir le dénominateur à zéro devrait lancer une exception d'argument non valide (je ne sais pas ce que c'est en Java)
la source
Timothy Budd a une belle implémentation d'une classe Rational dans ses "Structures de données en C ++". Langage différent, bien sûr, mais il porte très bien sur Java.
Je recommanderais plus de constructeurs. Un constructeur par défaut aurait numérateur 0, dénominateur 1. Un seul constructeur arg supposerait un dénominateur de 1. Pensez à la manière dont vos utilisateurs pourraient utiliser cette classe.
Pas de chèque pour le dénominateur zéro? La programmation par contrat vous obligerait à l'ajouter.
la source
Je vais troisième ou cinquième ou quelle que soit la recommandation pour rendre votre fraction immuable. Je vous recommande également d'étendre la classe Number . Je regarderais probablement la classe Double , car vous allez probablement vouloir implémenter plusieurs des mêmes méthodes.
Vous devriez probablement également implémenter Comparable et Serializable puisque ce comportement sera probablement attendu. Ainsi, vous devrez implémenter compareTo (). Vous devrez également surcharger equals () et je ne saurais trop insister sur le fait que vous surchargez également hashCode (). C'est peut-être l'un des rares cas où vous ne voulez pas que compareTo () et equals () soient cohérents car les fractions réductibles l'une à l'autre ne sont pas nécessairement égales.
la source
Une pratique de nettoyage que j'aime est de n'avoir qu'un seul retour.
la source
Utilisez la classe Rational de la bibliothèque JScience . C'est la meilleure chose que j'ai vue pour l'arithmétique fractionnaire en Java.
la source
J'ai nettoyé la réponse de Cletus :
valueOf(String)
par leBigInteger(String)
qui est à la fois plus flexible et plus rapide.la source
Remarque initiale:
N'écrivez jamais ceci:
Ceci est vraiment mieux
Créez simplement pour créer une bonne habitude.
En rendant la classe immuable comme suggéré, vous pouvez également profiter du double pour effectuer les opérations equals et hashCode et compareTo
Voici ma version rapide et sale:
À propos de la méthode de fabrique statique, cela peut être utile plus tard, si vous sous-classez la Fraction pour gérer des choses plus complexes, ou si vous décidez d'utiliser un pool pour les objets les plus fréquemment utilisés.
Ce n'est peut-être pas le cas, je voulais juste le souligner. :)
Voir le premier élément Java efficace .
la source
Cela pourrait être utile pour ajouter des choses simples comme rendre la pareille, obtenir du reste et se guérir.
la source
Même si vous disposez des méthodes compareTo (), si vous souhaitez utiliser des utilitaires comme Collections.sort (), vous devez également implémenter Comparable.
Aussi, pour un joli affichage, je recommande de remplacer toString ()
Et enfin, je rendrais la classe publique afin que vous puissiez l'utiliser à partir de différents packages.
la source
Cette fonction simplifier l'utilisation de l'algorithme euclédien est assez utile lors de la définition des fractions
la source
Pour une implémentation Fraction / Rational de niveau industriel, je l'implémenterais afin qu'elle puisse représenter NaN, l'infini positif, l'infini négatif et éventuellement le zéro négatif avec une sémantique opérationnelle exactement la même que les états standard IEEE 754 pour l'arithmétique en virgule flottante (cela facilite également la conversion vers / à partir de valeurs à virgule flottante). De plus, comme la comparaison avec zéro, un et les valeurs spéciales ci-dessus ne nécessite qu'une comparaison simple, mais combinée du numérateur et du dénominateur par rapport à 0 et 1 - j'ajouterais plusieurs méthodes isXXX et compareToXXX pour faciliter l'utilisation (par exemple, eq0 () utilisez numérateur == 0 && dénominateur! = 0 dans les coulisses au lieu de laisser le client comparer avec une instance de valeur zéro). Certaines valeurs prédéfinies statiquement (ZERO, ONE, TWO, TEN, ONE_TENTH, NAN, etc.) sont également utiles, puisqu'ils apparaissent à plusieurs endroits comme des valeurs constantes. C'est la meilleure façon IMHO.
la source
Fraction de classe:
Le programme principal:
la source