[Tutoriel] L'aléatoire en C/C++
Posté le 25/02/2014 18:17
Besoin d'un peu d'aléatoire dans tes programmes C/C++ ? Ce tuto est fait pour toi !
Prérequis :
Tout d'abord, la génération de nombres aléatoires de manière "classique" en C/C++ repose sur les fonctions
rand() et
srand(). Celles-ci sont incluses dans la bibliothèque
stdlib.h, il sera donc nécessaire de l'inclure dans le header de notre projet.
Générer des nombres aléatoires :
Pour générer un nombre aléatoire, le microprocesseur est incapable d'en déterminer un lui même. Il faut l'aider un peu, et c'est ce que l'on a fait en rentrant dans la calculatrice une fonction de ce type :
static unsigned int lastrandom=0x12345678;
unsigned int random(int seed = 0)
{
if(seed) lastrandom = seed;
lastrandom = ( 0x41C64E6D * lastrandom ) + 0x3039;
return (lastrandom >> 16);
}
Bref, lors de son premier appel, la fonction
rand() retournera alors le nombre de seed 0, puis celui dont le seed est la dernière valeur générée. On aura donc, par exemple
-> 0x 52 07 EB F8
-> 0x 72 65 C8 62
-> Etc.
Le problème, c'est que du coup, à chaque lancement du programme on aura les mêmes valeurs, puisque le seed original est le même...
Pour palier à cela, on détermine plus ou moins aléatoirement le seed avant le premier appel de la fonction avec
srand(int position). On ne l'utilisera qu'une seule fois, au tout début du programme.
Le plus dur dans tout ça, c'est de trouver un nombre qui soit suffisamment "aléatoire" pour qu'il soit différent à chaque fois. Pour cela, nous allons utiliser le syscall
RTC_getTicks().
RTC_getTicks() est une fonction très pratique qui retourne le nombre de ticks, c'est à dire de fractions (1/128) de seconde qui se sont écoulés depuis une certaine date (inconnue). Autant dire que la probabilité que le programme soit lancé deux fois en 1/128 seconde est minimale.
Voici le code à inclure :
static int SysCallCode[] = {0xD201422B,0x60F20000,0x80010070};
static int (*SysCall)(int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
int RTC_getTicks()
{
return (*SysCall)(0, 0, 0, 0, 0x3B);
}
Pour résumer, générer un nombre aléatoire se fait de cette façon :
1) On initialise avec
srand(RTC_getTicks());
2) On récupère le nombre avec
rand() :
int nbAlea = rand();
Traitement du nombre aléatoire :
C'est bien joli, mais
rand() ne retourne qu'un nombre compris entre 0 et 2^32-1 = 4294967295. Et nous voudrions un nombre entier entre 0 et 10, par exemple. C'est pour cela que nous allons modifier ce nombre :
Nombre entier entre 0 et max :
Nous allons utiliser le modulo (%), opération très pratique qui retourne le résultat de la division euclidienne de a par b (a%b). Or, puisque que ce reste n'est jamais supérieur à b, il est forcément égal à 0, 1, 2, 3, ..., b-1. Nous pouvons donc faire, pour générer un nombre entre 0 (inclus) et max (exclus) :
int rand_int(int max)
{
return rand() % max;
}
Nombre entier entre min et max :
Toujours en utilisant le modulo, il est possible de bidouiller la fonction ci-dessus pour inclure le fait d'avoir un minimum :
int rand_int_ab(int min, int max)
{
return rand() % (max - min) + min;
}
On a donc un nombre entier aléatoire entre min (inclus) et max (exclus).
Nombre décimal entre min et max :
Le problème du modulo, c'est qu'il ne permet que de travailler sur des entiers. Nous allons donc devoir nous en passer pour la génération de nombre décimaux (sous forme de float).
Nous avons vu que
rand() retournait un nombre entre 0 et 2^32. Donc en supposant que
RAND_MAX = 2^32,
rand() / RAND_MAX est compris entre 0 et 1. Sachant que
RAND_MAX est déjà défini dans "time.h", on peut l'utiliser tel quel. On en déduit alors la fonction suivante :
float rand_float_ab(float min, float max)
{
return ( rand() / RAND_MAX ) * (max - min) + min;
}
Conclusion et annexes :
Ce tuto touche à son terme, j'espère qu'il t'aura été utile, et que tu as compris les bases de la fonction
rand().
Si toutefois tu souhaite plus d'informations, ou que tu as une question, n'hésite-pas à laisser un commentaire ci-dessous !
De plus, tu trouvera peut-être ton bonheur sur l'ancien Site du Zéro, et plus particulièrement
ici.
A bientôt sur Planète Casio !
Citer : Posté le 17/06/2015 11:39 | #
Merci pour le tutoriel Dark Storm ! /D
Toutefois, sauriez-vous pourquoi j'ai exactement la même erreur qu'une incompatibilité SH4 sur l'émulateur du SDK officiel avec ce petit code :
[brown]#include [gray]"stdlib.h"[/gray][/brown]
static [purple]int[/purple] SysCallCode[] = {[maroon]0[/maroon]xD201422B,[maroon]0[/maroon]x60F20000,[maroon]0[/maroon]x80010070};
static [purple]int[/purple] (*SysCall)(int R4, [purple]int[/purple] R5, [purple]int[/purple] R6, [purple]int[/purple] R7, [purple]int[/purple] FNo ) = (void*)&SysCallCode;
[purple]int[/purple] AddIn_main(int isAppli, unsigned short OptionNum)
{
[purple]unsigned int[/purple] key;
[purple]unsigned char[/purple] *pic;
[purple]int[/purple] d=[maroon]0[/maroon];
[purple]int[/purple] i=[maroon]1[/maroon];
[purple]int[/purple] x, y, l, v;
[b][blue]for[/blue][/b] (i; i<100000; i++)
{
x = RanFloat(0, [maroon]1[/maroon]);
y = RanFloat(0, [maroon]1[/maroon]);
l = x*x+y*y;
[b][blue]if[/blue][/b] (l<1)
{
d++;
}
v = [maroon]4[/maroon]*d/i;
sprintf(pic,(const unsigned char*)[gray]"%d"[/gray],v);
PrintMini(30, [maroon]30[/maroon], (unsigned [purple]char[/purple] *)pic, [maroon]1[/maroon]);
Bdisp_AllClr_DDVRAM();
}
[b][blue]while[/blue][/b](1){
GetKey(&key);
}
[b][blue]return[/blue][/b] 1;
}
[purple]int[/purple] RTC_getTicks()
{
[b][blue]return[/blue][/b] (*SysCall)(0, [maroon]0[/maroon], [maroon]0[/maroon], [maroon]0[/maroon], [maroon]0[/maroon]x3B);
}
RanFloat(float min, [purple]float[/purple] max)
{
[b][blue]return[/blue][/b] ( rand() / RAND_MAX ) * (max - min) + min;
}
[brown]#pragma section _BR_Size[/brown]
unsigned long BR_Size;
[brown]#pragma section[/brown]
[brown]#pragma section _TOP[/brown]
[purple]int[/purple] InitializeSystem(int isAppli, unsigned short OptionNum)
{
[b][blue]return[/blue][/b] INIT_ADDIN_APPLICATION(isAppli, OptionNum);
}
#pragma section
Pong400
PierrePaCiseaux (CP400)
Les Triangles
Menu
ASCII
Nombres premiers
Citer : Posté le 17/06/2015 14:19 | #
T'es pas en train d'implémenter la méthode de monte-carlo là ? >_<
{
return ( rand() / RAND_MAX ) * (max - min) + min;
}
C'est pas bon ça. rand() et RAND_MAX sont des int, il va te tronquer le tout à 0. Multiplie avant de diviser ou force le calcul flottant en castant, mais là tu vas avoir min tout le temps.
En plus v est un int, tu vas avoir droit à π = 3...
Et pic est non initialisé.
Citer : Posté le 17/06/2015 14:36 | #
T'es pas en train d'implémenter la méthode de monte-carlo là ? >_<
Si. Et les noms de variables sont nuls, je sais.
Ok merci je vois ça.
Pong400
PierrePaCiseaux (CP400)
Les Triangles
Menu
ASCII
Nombres premiers
Citer : Posté le 20/06/2015 13:57 | #
static int SysCallCode[] = {0xD201422B,0x60F20000,0x80010070};
static int (*SysCall)(int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
int RTC_getTicks()
{
return (*SysCall)(0, 0, 0, 0, 0x3B);
}
int rand_int(max)
{
return rand() % max;
}
Si je comprend bien, en mettant ce code dans mon code, je vais avoir un nombre aléatoire entre 0 et max(20) ?
Citer : Posté le 20/06/2015 14:07 | #
Tu aura un nombre entier aléatoire appartenant à l'intervalle [[ 0 ; max [[
Citer : Posté le 20/06/2015 14:12 | #
merci et il sera stocké dans quoi ?
Citer : Posté le 20/06/2015 14:24 | #
Ben dans la variable que tu veux
Citer : Posté le 20/06/2015 14:32 | #
merci
Citer : Posté le 08/05/2016 16:53 | #
Quand j'essaie de passer ca :
static int (*SysCall)(int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
-Mon Fall Down
-Mon jeu de mains
-Mon starwars
-Mon dessinatout
-Mon niaiseux version 2.0
-Mon niaiseux version 3.0
-Inferno
-Mon super labyrinthe (en cours)
-Mon call of duty en 3D
-Casion (avec Az)
Citer : Posté le 08/05/2016 16:56 | #
Tu voudrais pas essayer de mettre des déclarations C ?
static int SysCallCode[] = {0xD201422B,0x60F20000,0x80010070};
static int (*SysCall)(int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
}
Citer : Posté le 08/05/2016 16:59 | #
j'ai testé et ca me sort toujours la même erreur :/
-Mon Fall Down
-Mon jeu de mains
-Mon starwars
-Mon dessinatout
-Mon niaiseux version 2.0
-Mon niaiseux version 3.0
-Inferno
-Mon super labyrinthe (en cours)
-Mon call of duty en 3D
-Casion (avec Az)
Citer : Posté le 08/05/2016 17:00 | #
Bon ben pas de problème, on va caster puisqu'il joue comme ça :
Citer : Posté le 08/05/2016 17:18 | #
Mtn il me sort ca ** L2300 (E) Duplicate symbol "_RTC_getTicks"
Pourtant la seule autre fois ou il y a RTC_getTicks c'est dans : srand(RTC_getTicks()); :/
-Mon Fall Down
-Mon jeu de mains
-Mon starwars
-Mon dessinatout
-Mon niaiseux version 2.0
-Mon niaiseux version 3.0
-Inferno
-Mon super labyrinthe (en cours)
-Mon call of duty en 3D
-Casion (avec Az)
Citer : Posté le 08/05/2016 17:27 | #
Ah ben si RTC_getTicks() n'apparaissait que là il se plaindrait que tu ne l'as pas défini, là il est défini deux fois !
Vérifie l'ensemble de tes fichiers, il doit y être deux fois Au besoin, supprime le dossier Debug et recompile tout.
Citer : Posté le 08/05/2016 17:31 | #
Xd tu as raison c'est tout bête c'était déjà implémenté dans C-engine x)
Merci de m'avoir aidé
-Mon Fall Down
-Mon jeu de mains
-Mon starwars
-Mon dessinatout
-Mon niaiseux version 2.0
-Mon niaiseux version 3.0
-Inferno
-Mon super labyrinthe (en cours)
-Mon call of duty en 3D
-Casion (avec Az)
Citer : Posté le 05/12/2018 13:33 | #
Bonjour, j'utilise fxlib sur Linux mais la fonction RTC_getTicks() fait planter ma Casio
Pour info quand je passe par le SDK de Windows puis par le convertisseur SH3-SH4 il n'y a pas de problème
Théoriquement je n'ai pas besoin du convertisseur quand je compile sous Linux, j'ai quand même essayé mais ça n'arrange pas le blème
(Pour être clair, toutes les compilations se passent bien c'est juste l'exécution qui foire)
Citer : Posté le 05/12/2018 13:34 | #
Tu le déclares comment le syscall ?
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 05/12/2018 13:53 | #
Je vais avouer que je grille les étapes, dans la fonction rand(seed), je vire le seed en argument et dans la fonction je fais int seed = (*SysCall)(0, 0, 0, 0, 0x3B);
Je fais correctement les static int avant (je fais tout ça dans un .c qui n'est pas le main). Je vais tout renoter pour que ça soit plus clair.
Ajouté le 05/12/2018 à 14:20 :
Bon c'est vraiment le RTC_getTicks() qui foire le tout, peut-être modifier les static int mais j'y comprends rien personnellement...
Citer : Posté le 05/12/2018 14:21 | #
Essaie la procédure assembleur (cf mon tuto : https://www.planet-casio.com/Fr/forums/lecture_sujet.php?id=14992)
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 05/12/2018 14:33 | #
Alors, peux tu me dire ce qu'il faut faire précisément pour ce cas et comment on se débrouille pour compiler tout ça ?
Citer : Posté le 05/12/2018 16:03 | #
Tu crées un fichier syscalls.s similaire à celui là : https://github.com/Zezombye/casiopy/blob/master/ports/minimal/syscalls.s
Ensuite il te suffit de l'ajouter à tes sources.
Ecrivez vos programmes basic sur PC avec BIDE