8
votes

signé à un hachage positif presque parfait

J'ai un type d'entier, disons long , dont les valeurs sont entre long.min_value = 0x80 ... 0 (-2 ^ 63) et long .Max_value = 0x7f ... f (2 ^ 63 - 1). Je veux hacher avec environ 50% de collision à un entier positif du même type (c'est-à-dire entre 1 et long.max_value ) de manière propre et efficace.

Mes premières tentatives étaient quelque chose comme:

  • math.abs (x) + 1
  • (x & long.max_value) + 1

    Mais ces approches et des approches similaires ont toujours des problèmes de certaines valeurs, c'est-à-dire lorsque x est 0 / long.min_value / / .Max_value . Bien sûr, la solution naïve est d'utiliser 2 déclarations, mais je cherche quelque chose de nettoyant / plus court / plus rapide. Des idées?

    Remarque: supposons que je travaille en Java où il n'y a pas de conversion implicite en sémantique booléenne et décalée est définie.


0 commentaires

9 Réponses :


0
votes

Juste pour m'assurer que vous avez un long et que vous voulez le hisser à un int?

vous pourriez faire ... p> xxx pré>

si vous voulez garder une longue chose que vous pourriez faire ... p> xxx pré>

si obtenir un 0 n'est pas bon ... p> xxx pré>

à fort ... p> xxx pré>

Je pense que vous allez avoir besoin d'être correct avec 75% ou d'obtenir un peu laids: P>

(x > 0) ? x : (x < 0) ? x & Long.MAX_VALUE : 7


3 commentaires

Non, la codomaine de hachage est à nouveau longue - mais elle doit être> 0. Je vais mettre à jour le message pour le rendre plus précis.


Notez que dans l'exemple "laid" 0 collide avec 7.


L'exemple "laid" mappe Min_Value sur 0. et obtient 0 n'est pas bon.



3
votes

En supposant que vous souhaitiez réduire toutes les valeurs dans l'espace positif, pourquoi pas seulement zéro le piqué de signalisation?

Vous pouvez le faire avec un seul bitwise en profitant du fait que max_value est juste un bit de signe zéro suivi par celles par exemple xxx

ou pour les longs: xxx

si vous voulez un "meilleur" hachage avec des qualités pseudo-aléatoires avec des qualités pseudo-aléatoires , vous voulez probablement PSS la valeur à travers une autre fonction de hachage d'abord. My Favor Fast Hays est le Famille Xorshift de George Marsaglia. Ceux-ci ont la belle propriété qu'ils mappent parfaitement l'espace int / long numéro sur lui-même. Vous obtiendrez donc exactement 50% de collision après la mise à zéro du pancarte.

Voici une implémentation rapide de Xorshift en Java: < / p> xxx


1 commentaires

Cela s'effondre à l'espace non négatif, j'ai besoin de s'effondrer de manière positive.



10
votes

L'approche la plus simple consiste à zéro le piqué de signalisation, puis à mapper zéro sur une autre valeur: xxx pré>

Ceci est simple, utilise un seul opérateur si / ternaire et donne environ 50% de collision taux en moyenne. Il y a un inconvénient: il mesure 4 valeurs différentes (0, 42, min_value, min_value + 42) à une seule valeur (42). Donc, pour cette valeur, nous avons 75% de collisions, tandis que pour d'autres valeurs - exactement 50%. P>

Il peut être préférable de distribuer des collisions plus uniformes: P>

Long y = x + 1 + ((x >> 63) << 1);
Long z = y - (y >> 63);
return z & Long.MAX_VALUE;


3 commentaires

Variation, si vous avez la foi dans l'optimiseur: retour (ABS (x) == 0)? 42: ABS (x)


@Richardsitze: Il y a un petit problème avec math.abs (). Il donne un résultat négatif pour long.min_value. Mais op a besoin d'un entier positif.


retour (math.abs (x) <1)? 42: math.abs (x)



1
votes

de la vue théorique des informations, vous avez 2 ^ 64 pour mapper dans 2 ^ 63-1 valeurs.

En tant que tel, la cartographie est triviale avec l'opérateur du module, car elle a toujours un résultat non négatif: xxx

Cela pourrait être assez coûteux, alors qu'est-ce que c'est possible. ?

Le math simple 2 ^ 64 = 2 * (2 ^ 63 - 1) + 2 Nous aurons deux valeurs de source de mappage de la valeur cible, sauf dans deux cas spéciaux , où trois iront un. Pensez à ceux-ci comme deux valeurs spéciales 64 bits, appelez-les x1 et x2 , que chacun partagez une cible avec deux autres valeurs de source. Dans l'expression mod ci-dessus, cela se produit par "Emballage". Les valeurs cibles y = 2 ^ 31-2 et y = 2 ^ 31-3 ont trois mappages. Tous les autres ont deux. Puisque nous devons utiliser quelque chose de plus complexe que mod de toute façon, recherchons un moyen de cartographier les valeurs spéciales partout où nous aimons à faible coût

pour illustration travaillant avec la cartographie un 4- Bit signé INT x dans [-8..7] sur y dans [1..7], plutôt que l'espace 64 bits.

Un cours facile est d'avoir x dans la carte [1..7], puis le problème diminue à la mappage x dans [- 8..0] à y dans [1..7]. NOTE Il y a 9 valeurs source ici et seulement 7 cibles comme indiqué ci-dessus.

Il existe évidemment de nombreuses stratégies. À ce stade, vous pouvez probablement voir une gazzilion. Je vais décrire un seul qui est particulièrement simple.

let y = 1 - x pour toutes les valeurs sauf cas spéciaux x1 == -8 et < Code> x2 == -7 . Toute la fonction de hachage devient donc xxx

ici s (x) est une fonction simple qui dit où x1 et < code> x2 sont mappés. Choisissez s en fonction de ce que vous connaissez des données. Par exemple, si vous pensez que les valeurs cibles élevées sont improbables, cartographiez-les sur 6 et 7 avec S (x) = -1 - x .

Le mappage final est: xxx

prenant cette logique jusqu'à l'espace 64 bits, vous auriez xxx

de nombreux autres types de réglage sont possibles dans ce cadre.


1 commentaires

Vous et moi pensons un lot terrible ... j'ai déjà eu des chiffres [-8..7] dans une liste de jouer avec. :)



1
votes

Je choisirais la version la plus simple, mais pas totalement Temps de gaspillage: xxx

Cette implémentation paie une opération conditionnelle pour tous les deux les entrées possibles: 0 et min_value. Ces deux sont attribués différentes mappages de valeur avec la deuxième condition. Je doute que vous obtenez une meilleure combinaison de la simplicité (code) et de la complexité (calculationnelle).

Bien sûr, si vous pouvez vivre avec une distribution pire, il est beaucoup plus simple . En limitant l'espace à 1/4 au lieu de 1/2 -1, vous pouvez obtenir: xxx


0 commentaires

1
votes

Si la valeur est positive, elle peut probablement être utilisée directement, sinon, toutes les bits: xxx

Cependant, vous devez brouiller cette valeur un peu plus si les valeurs de x est corrélé (signification: objets similaires produisent des valeurs similaires pour x ), peut-être avec xxx

pour certaines constantes positive A et B , où a doit être assez volumineux et B empêche que 0 est toujours mappé à 1 . Cela change également le tout à [1, long.max_value] au lieu de [0, long.max_value]. En modifiant les valeurs de A et B Vous pouvez également mettre en œuvre des fonctionnalités de hachage plus complexes telles que Cooko Hashing a besoin de deux fonctions de hachage différentes.

Une telle solution doit certainement être préférable au lieu d'une "distribution étrange de la collision" pour les mêmes valeurs à chaque fois qu'elle est utilisée.


0 commentaires

1
votes

Vous pouvez le faire sans conditionnels et dans une seule expression en utilisant l'opérateur de décalage non signé: xxx


1 commentaires

Probablement le meilleur moyen d'éviter les conditionnels. Si l'effondrement 4 valeurs dans une seule n'est pas souhaitable, cela peut être prétraité par x + = x >>> 31 .



0
votes

Cela semble le plus simple de tous:

(x % Long.MAX_VALUE) + 1


0 commentaires

0
votes

juste et votre valeur d'entrée avec long.max_value et ou avec 1. rien d'autre n'était nécessaire.

ex: xxx


2 commentaires

Bonne et simple approche. Seul problème est que trois valeurs très similaires (-1, 0, 1) sont toujours mappées sur la même valeur unique (1).


Je pense que cela reste plus que qualifié pour la collision approximative de 50%, comme indiqué dans la question initiale, oui?