Comme je ne comprenais pas bien le principe de la méthode géométrique, je me suis mis à explorer cette voie à ma manière.
J'obtiens un algorithme fort proche de celui proposé par gégé, sans toute fois passer par les coordonnées logarithmes.
Certes les logarithmes interviennent, mais pas dans le calcul des nombres, uniquement dans la détermination des limites de chaque "tétraèdres".
Ce qui fait, que je peux déterminer les nombres de Hamming au-delà de la précision de ma calculette, car le code n'utilise que les coordonnées géométriques (x,y,z) dans le repère (Ox>,(Oy>, (Oz> correspondant respectivement aux puissances de 2,3 et 5.
Comme pour la méthode volumétrique, je cherche à dénombrer les nombres de Hamming par intervalles successifs ] (2^k)/2 , 2^k ]
Pour k=0 le seul point possible est (0,0,0) c'est à dire H(1)=1
Pour k=1 le seul point possible est (1,0,0) c'est à dire H(2)=2
Pour k=2 deux points sont dénombrés (2,0,0) et (0,1,0) c'est à dire 4 et 3.
En fait, les points possibles pour le tétraèdre de niveau général k sont les points de coordonnées positives (ou nulles) vérifiant :
y*log(3) + z*log(5) <= x*log(2)
Ici, la fonction log peut être naturelle, décimale ou d'une base quelconque. Cela revient à 'quantifier' la vitesse à laquelle les sous-branches des n-multiples se développent.
Ce qui est intéressant, c'est que si un point (x,y,z) appartient au tétraèdre de niveau (k-1) alors le point (x+1,y,z) appartient au tétraèdre de niveau k, car les points se projettent orthogonalement sur le plan (Ox>
Par exemple, 3 et 4 appartiennent au tétraèdre k=2. On a donc les points 6 et 8 qui appartiennent au tétraèdre k=3
En effet, les nombres de Hamming de l'intervalle ]4,8] sont dans l'ordre 5 6 8
Dans la copie d'écran suivante, les nombres de Hamming déduits du tétraèdre précédant par multiplication sont indiqués entre parenthèses:
Code : Tout sélectionner
run
? 20
TETRA: tot MEM:
2^ 0 : 1 t 1 1:
2^ 1 : (2) t 2
2^ 2 : (4)
3 t 4 2:
2^ 3 : (8) 5 3:
(6) t 7
2^ 4 :(16) (10) 3:
(12) 15 4:
9 t 12 5:
2^ 5 :(32) (20) 25 6:
(24) (30)
(18)
27 t 19 7:
2^ 6 :(64) (40) (50)
(48) (60)
(36) 45 8:
(54) t 27
LIST MEM 8:
64 60 54 50 48 45 40 36
^
t27 t26 t25 t24 t23 t22 t21 t20
RESULTAT :
2 2 0
H = 2 .3 .5 = 36 0' 1.4s
20
ready.
Pour cela je parcours les y et z possibles qui vérifient y*log(3)+z*log(5)<=x*log(2) - J'ai mis des log dans chaque terme, comme cela on utilise indifféremment log népérien, décimaux ou particuliers.
Le compteur n% est incrémenté pour chaque 'nouveau' nombre, c'est à dire une combinaison (y,z) qui n'existait pas dans le tétraèdre précédant; n% est donc utilisé comme indice mémoire étant donné qu'une fois qu'un couple de coordonnées (x,y,z) entre dans le tétraèdre 2^k, les points (1+x,y,z), (2+x,y,z), etc... seront également dans les tétraèdres suivants k+1, k+2, etc...
D'ailleurs, le code n'a pas de variable k%, c'est directement x% qui est utilisée.
Le total t% donne le nombre de Nombres de Hamming depuis le premier tétraèdre. On parcourt donc séquentiellement les tétraères jusqu'à obtenir un total t% au moins égal à l'indice recherché.
A l'issue de cette première partie, on obtient donc la liste des n% nombres de Hamming de l'intervalle ] (2^k)/2,2^k ]
Pour en déduire le r-ième, il est nécessaire de les trier.
C'est à dire de chainer la liste dans l'ordre décroissant. On sait que le premier nombre est le plus grand car il correspond à 2^k. Donc , il suffit d'insérer dans la liste les suivants afin qu'il forment une liste ordonnée.
C'est à cela que sert la valeur du pointeur a%() qui indique l'indice mémoire du Nombre de Hamming immédiatement inférieur (ou contient zéro pour marquer la fin de la liste).
Pour gagner du temps, la valeur (même approchée) du nombre est mémorisé dans le tableau N() ce qui facilite les nombreux tests pour le tri.
La dernière partie se contente de dérouler cette liste chainée ordonnée afin d'atteindre le nombre recherché d'indice r.
Code : Tout sélectionner
run
? 10
2 1 0
h = 2 .3 .5 = 12 0' .7"
10
2 2 0
h = 2 .3 .5 = 36 0' 1.4"
20
9 1 0
h = 2 .3 .5 = 1536 0' 5.1"
100
14 0 5
h = 2 .3 .5 = 51200000 0'50.9"
1000
5 10 16
h = 2 .3 .5 = 2.88325196e+17 12'59.5"
10000
ready.
Et le listing :
Code : Tout sélectionner
10 rem ---------------------------- initiate
20 l2=log(2):l3=log(3):l5=log(5)
30 m%=52*36/2:dim n(m%),n%(52,36),x%(m%),y%(m%),z%(m%),a%(m%)
40 x%=-1:n%=0:t%=0:input r% : t0=timer
100 rem --------------------------- scan tetra and build list
110 do
120 : x%=x%+1
130 : for y=0 to x%*l2/l3
140 : : for z=0 to (x%*l2-y*l3)/l5
150 : : : if n%(y,z)=0 then n%=n%+1:n%(y,z)=n%:x%(n%)=x%:y%(n%)=y:z%(n%)=z
160 : : next z
170 : next y
180 : t%=t%+n%
190 loop while t%<r%
200 rem --------------------------- sort list of h() in ] 2^x-1 , 2^x ]
210 n(1)=2^x%
220 for i=2 to n%:n(i)=int(2^(x%-x%(i)))*int(3^y%(i))*int(5^z%(i)):a%=1
230 : do while n(i)<n(a%(a%)):a%=a%(a%): loop :a%(i)=a%(a%):a%(a%)=i
240 next i
300 rem --------------------------- display result
310 a%=1:do while r%<t%:t%=t%-1:a%=a%(a%): loop
320 i=a%:print "[dwn]h[dwn][lft]";t%;"[up]= 2[up][lft]";x%-x%(i)"[dwn][lft]
.3[up][lft]"y%(i)"[dwn][lft].5[up][lft]"z%(i)"[dwn]="n(i);
400 t1=int((timer-t0)/6):t1%=t1/600:t1=t1-600*t1%:print tab(35)t1%".'"t1/10".s[dwn]"
ready.
N() Nombre de hamming pour le tri (ligne 200-240) puis affichage (ligne 320)
a%() Pointeur de liste chainée désignant le nombre immédiatement inférieur (ou zéro si dernier de la liste)
x%() y%() z%() coorodnnées du nombre de hamming dans le repère Oxyz correspondant aux expansion x2 x3 et x5
n%( y , z ) marqueur pour découverte des nouveaux points - ce tableau contient l'indice du nombre. Mais celui-ci n'est pas utilisé (sauf que les points non encore découverts ont un indice nul). On pourrait remplacer ce tableau par les fonctions graphiques PSET RPOINT d'un Pocket en allument le pixel (y,z) et en en lisant la valeur pour le test de la ligne 150.
La dimenson du tableau permet en théorie d'atteindre 3^52 ou 5^36, c'est à dire des nombres de Hamming au-delà de 1.E+24