2017-08-11 3 views
4

Comment trier une tranche d'entiers par le premier chiffre de chaque int?Obtention du premier chiffre d'un int

Je suis en train d'écrire mon propre tri personnalisé:

type ByFirstDigit []int 

func (s ByFirstDigit) Len() int { 
    return len(s) 
} 

func (s ByFirstDigit) Swap(i, j int) { 
    s[i], s[j] = s[j], s[i] 
} 

func (s ByFirstDigit) Less(i, j int) bool { 
    return s[i][0] < s[j][0] 
} 

Mais je reçois cette erreur:

s[j][0] (type int does not support indexing)

+0

-ce que les chiffres être négatif? – Akavall

Répondre

5

@RayfenWindspear a le plus facile à utiliser et à lire la réponse, mais est correct au sujet de la baisse de performance. Si la performance est plus importante que la maintenabilité, vous pouvez faire la même chose en utilisant la division itérative pour obtenir la base 10 chiffres les plus significatifs:

var i int 
for i = n; i >= 10; i = i/10 {} 
// i == most significant digit 

Notez que vous devez déclarer i en dehors de la boucle pour pouvoir utiliser il après la boucle trouve le chiffre le plus significatif. Je comparerais également les deux avec votre propre ensemble de données pour voir quel est l'impact réel sur les performances dans votre situation particulière.

Full playground example, courtesy of Rayfen.

+0

Génial! Je ne sais pas pourquoi je n'y ai pas pensé. Ma première pensée a été les opérateurs de bits, mais évidemment, cela ne fonctionnerait pas, alors je suis allé directement pour la réponse facile. – RayfenWindspear

+1

Je préfère généralement la réponse facile, car il est plus clair ce qui se passe. Ensuite, lorsque la mise en œuvre est terminée, si les performances ne répondent pas aux exigences, étalonner, profiler et optimiser. L'optimisation prématurée est la racine de tous les maux :) – Adrian

+1

BTW, cela a un bug dans le fait qu'il ne tient pas compte du nombre '10'. '10' devrait être la première valeur. Voir ici https://play.golang.org/p/eYZMVXQURS – RayfenWindspear

1

Vous pouvez les convertir en chaînes dans votre fonction de tri puis d'index le premier caractère. Pour []int, vous pouvez utiliser https://godoc.org/strconv#Itoa. Malheureusement, cette méthode va prendre un énorme coup de performance pour la conversion de chaîne.

https://play.golang.org/p/S4j3NlfinD

type ByFirstDigit []int 

func (s ByFirstDigit) Len() int { 
    return len(s) 
} 

func (s ByFirstDigit) Swap(i, j int) { 
    s[i], s[j] = s[j], s[i] 
} 

func (s ByFirstDigit) Less(i, j int) bool { 
    si := strconv.Itoa(s[i]) 
    sj := strconv.Itoa(s[j]) 
    return si[0] < sj[0] 
} 
+0

Le paquet 'strconv' a les fonctions' Append * 'pour le formatage sans la conversion de chaîne supplémentaire. – JimB

+0

@JimB mais le résultat serait un [] octet, qui devrait être converti en 'string' pour l'utiliser quand même. Je ne sais pas comment les fonctions 'Append *' seraient meilleures. – RayfenWindspear

+0

non, vous n'avez pas besoin de le formater comme une chaîne pour obtenir le premier chiffre, le premier «byte» dans la tranche est le premier chiffre. C'est plus rapide, car il n'a pas besoin d'allouer la nouvelle chaîne à chaque fois (les formats FormatInt internes en un '[] byte) d'abord les appels justes' string (s) '), et vous pouvez réutiliser le tampon si vous êtes vraiment essayer de sauver. Cependant, il sera encore plus lent que la solution directe de trouver seulement le premier chiffre comme dans la solution d'Adrian. – JimB

0

Si vous voulez une solution avec beaucoup de fonctions mathématiques de haut niveau qui peuvent ou peuvent ne pas fonctionner correctement:

package main 

import "sort" 
import "fmt" 
import "math" 

type ByFirstDigit []int 

func (s ByFirstDigit) Len() int { 
    return len(s) 
} 
func (s ByFirstDigit) Swap(i, j int) { 
    s[i], s[j] = s[j], s[i] 
} 
func (s ByFirstDigit) Less(i, j int) bool { 
    return firstDigit(s[i]) < firstDigit(s[j]) 
} 

func firstDigit(x int) int { 
    return int(math.Abs(float64(x))/math.Pow(10, float64(numDigits(x) - 1))) 
} 

func numDigits(x int) int { 
    if (x == 0) { return 1 } 
    return int(math.Floor(math.Log10(math.Abs(float64(x))))) + 1 
} 

func main() { 
    ints := []int{3, 20, 400, -500, 101} 
    sort.Sort(ByFirstDigit(ints)) 
    fmt.Println(ints) 
} 

est ici un terrain de jeu (même code): https://play.golang.org/p/uLyBMlra2N

+1

J'oublie toujours, est-ce que la précision d'un 'float64' est telle qu'il est capable de contenir __any nombre entier__ dans la plage' int64' sans qu'il y ait une troncature? c'est-à-dire qu'il y a la possibilité de 'int64 (float64 (n))! = n' où' n' est à l'origine 'int64'? – RayfenWindspear

+1

Non, les bits sont-ils dans Go 64 bits? (Je n'ai pas utilisé Go avant). Plage d'un int64: 2^64.La gamme int parfaite d'un float64: 2^53, parce que vous obtenez 52 bits parfaits de la mantisse, et un de l'exposant. – boxtrain

+1

Le type 'int' est basé sur l'architecture du système, mais est toujours son propre type. Sur x64 ce sera 'int64' mais cela ne veut pas dire que les types' int' et 'int64' sont les mêmes, un cast est nécessaire. – RayfenWindspear