Falkor a écrit : ↑21 juil. 2021 11:07
Bon déjà pas de bol, le simple fait d'appeler la fonction random_seed fait qu'ensuite tout appel à la fonction random() renvoie toujours le même nombre
. On arrive à avoir des résultats différents en ne l'appelant pas. (bon why not...). Bref.
C'est probablement que tu n'as pas eu de chance dans le choix de tes graines seed1..3 et es tombé sur un point fixe. Par hasard tu n'aurais pas passé des zéros là dedans.
Mon souci est que la fonction random() me renvoie un entier signé 8 bits, donc entre -128 et +127, et que j'aimerai pouvoir "affiner" la plage de rendu des valeurs pour obtenir des entiers signés entre A et B. (toujours dans la plage entier signé 8 bits).
C'est classique. A et B sont inclus je présume ?
Les solutions trouvées lors de mes recherches faisaient quasi toutes appel à des flottants, genre :
Ca n'est pas du flottant mais du modulo entier qui est une opération couteuse qui en plus apporte un léger biais dans la mesure où suivant les valeurs de primalité entre (B-A) et 256, le modulo va en favoriser certains.
Une autre façon de faire, plus homogène est en fait de tirer un nombre au pif entre 0.0 et 1.0 de multiplier par (B-A) et ajouter A: A + (B-A)*u() avec un un flottant aléatoire entre 0 et 1.
Oui mais c'est du flottant ça et il n'y en a pas sur Vectrex (ou alors c'est très lent). C'est pas grave: la solution est de passer par des nombres en point fixes. On remplace u() par un appel à urand()/255 où urand() retourne un nombre entier entre 0 et 255 (urand() = rand()+128 mais (unsigned)rand() convient aussi). La formule devient: A + ((B-A)*urand())/255 et là (B-A) et urand() sont tout deux des entiers 8 bits. C'est un produit de deux 8 bits ce que le mc6809 sait très bien faire.
Oui mais il reste la division entière par 255. C'est vrai. C'est une opération couteuse... mais on remarque que grosso-modo diviser en entier par 255 ne donne pas un résultat très loin d'une division en entier par 256.. En effet, au plus on aura B-A=255 et urand()=255, soit un produit n de 65025. Or n/255 = 255 et (n + n/256)/256=255 aussi
Code : Tout sélectionner
* Divise D (255*255 max) par 255
DIV255
STA ,-S
CLR ,-S
ADDD ,S++
TFR A,B
CLRA
RTS
Au final la routine pour avoir un nombre aléatoire entre A et B (tous les deux inclus) devient
Code : Tout sélectionner
* Nombre aléatoire entre valeur registre A et valeur de registre B
randAB:
STA ,-S
SUBB ,S
STB ,-S ; on stocke B-A
JSR rand ; on traitera le retour sur B comme un entier non signé
LDA ,S ; A = "B-A", B = urand()
MUL ; D = (B-A)*urand()
STA ,S
CLR ,-S
ADDD ,S++ ; D + D/256
TFR A,B
CLRA ; D = (D + D/256)/256
ADDB ,S+ ; D = A + (B-A)*urand()/255
RTS
Il semble en plus que le générateur de nombres aléatoires du bios de la Vectrex ne soit pas si bon que ça.
C'est possible. Perso j'ai trouvé que les générateurs "
mutliply with carry de Marsaglia sont petits et très rapides sur le 6809.
Code : Tout sélectionner
seed0 FCB 0 ; important : toujours garder à 0 car on fait un ADDD
seed1 FCB 123
seed2 FCB 234
mwcRand8:
LDA #249 ; entier a qui a la plus longe période (31 871) pour du 8 bits
LDB seed2 ; b=x (sortie précédente)
MUL ; D=ax
ADDD seed0 ; D=ax+c
STA seed1 ; c=int((ax+c)/256) (*)
STB seed2 ; x=dernier résultat (*)
CLRA ; pour avoir un D entre 0 et 255 (optionnel car souvent on trash A juste après)
RTS
Attention à ne jamais avoir seed1 et seed2 à 0 car là le générateur ne produit que des 0 (point fixe).
__
(*) NOTE: les STA seed1/STB seed2 peuvent être combinés en un seul STD seed1, mais ici je veux faire simple et compréhensible.
Après il y a les
XORSHIFT qui sont aussi rapides et facile à implémenter sur 68000 (ou 8 bits si on est familier avec l'arithmétique multi-précision) et ont une super longue période et passent pas mal de tests statistiques.
PS: code source non testé et donné à titre indicatif.