Funzione map non restituisce limite superiore

Buongiorno, stavo studiando la funzione map vorrei generare i numeri della
roulette, ma il numero 36 non esce mai per farlo uscire devo impostare 37 come limite max
lo zero funziona.

/*
  Simulatore roulette
*/

#include "pRNG.h"
pRNG prng;
unsigned int rnd;
void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(0));
}
void loop() {

  for (int c = 0; c <= 100; c++) {
    rnd = prng.getRndInt();
    rnd = map(rnd,0,65535,0,36);
    Serial.println(rnd);
  }
}

ciao...la funzione map() è una cosa del genere:

long map(long x, long in_min, long in_max, long out_min, long out_max) {

      return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

    }

quindi per avere in ritorno 36 la variabile rnd deve essere esattamente 65535.

Se non sbaglio, per suddividere il più equamente possibile il range 0..65535 in 37 intervalli (da 0 a 36) si deve scrivere:

map(rnd, 0, 65536, 0, 37);

dove il valore 37 non uscirà mai perché il valore di rnd prodotto da prng non sarà mai superiore a 65535.

Non essendo però 37 intervalli un sottomultiplo esatto di 65536 valori possibili, la suddivisione non sarà perfetta.

Io userei l'operatore modulo piuttosto che la funzione map().

uint8_t rnd = prng.getRndInt() % 37;

2 Likes

Perché non usi semplicemente rnd(0, 37); ?

randomSeed(analogRead(0));

loop:
uint8_t numero = rnd(0,37);

oppure, dopo aver visualizzato un messaggio di benvenuto e atteso la pressione di un pulsante (che avverrà in un istante imprevedibile):

randomSeed(millis()+analogRead(0));

loop:
uint8_t numero = rnd(0,37);

Ciao rispondo a tutti, il mio dubbio è perchè devo usare un numero superiore al limite massimo, in questo caso 37, mi sarei aspettato come limite max coincidesse con numero che voglio ottenere e non uno in più.
Vedo che è stato risposto, devo solo mettermi a studiare.
Grazie

Perché è un intervallo aperto a destra, che si estende fino al valore massimo meno un infinitesimo: non va da 0 a 36, cioè da 0 a 36,000000, ma da 0 a 36,999999, cioè da 0 a 37 escluso.

Quindi il fatto di mettere un numero in più non è sbagliato?
Sto usando una libreria che genera numeri casuali molto più
attendibili della funzione rnd basica.
E' molto lenta ma è molto più attendibile.

"numeri casuali molto più attendibili" è un ossimoro!
:grin:

Lo so, ci vorrebbe una schedina esterna, ma se uso la sua funzione base ogni volta che lo accendo senza fare altro, pulsati o attese mi da sempre gli stessi numeri. Se uso quella libreria fatta da un frequentatore del forum alcuni anni fa, e che avuto molti complimenti, genera numeri "moooolto" più casuali.
Comunque non devo fare nessun progettino , era solo a scopo didattico.

Devi seminare!
Che farà mai quella libreria, se non seminare all'inizio?
Qual è?

#include "pRNG.h"`
qui la discussione
[Nuova libreria pRNG]

Per seminare, puoi memorizzare qualcosa nella EEPROM durante l'uso e utilizzarlo all'accensione successiva. Per esempio, puoi memorizzare millis() alla prima pressione di un tasto.

1 Like

Si lo so che ci sono tanti metodi, il dubbio era perchè per
mappare da 0 a 36 bisogna scrivere 0,37 :raised_hand:

Beh è strano perché la analogRead(A0) dovrebbe fluttuare se il pin viene lasciato libero. Perché tu l'hai lasciato libero, ossia non connesso a nulla (e magari anche con un pezzetto di filo tanto per fare da "antenna"), vero?... :wink: Non è che l'hai messo a massa o collegato a qualcosa?
Se poi ci facessi vedere il codice (quello che non ti piace che dà sempre gli stessi valori)...

Si, ho visto quella discussione ma usare persino Galois per avere un polinomio ed un algoritmo di generazione di numeri casuali migliori mi pare eccessivo, soprattutto per gli scopi del 99.99% delle applicazioni con Arduino: conosco bene LFSR o registri a scorrimento lineare di Galois, l'ho studiato per lavoro per un progetto di qualche anno fa, e qui non stiamo facendo crittografia né gestiamo "soldi"...
L'algoritmo di Arduino va benissimo, a patto che tu dia un "seed" (quello che dai con "randomSeed()") sufficientemente variato. Tempo fa ne parlai anche qui nel forum, comunque ora vado "a braccio" perché non lo ritrovo.

Se leggi da "analogRead()" tu avrai sempre un seed tra 0 e 1023. Ci siamo fin qui? Ok. Ora, questo significa che tu avrai fino a 1024 sequenze diverse di numeri (pseudo) casuali, ma di fatto sono molte meno perché un pin analogico lasciato a fluttuare ha una distribuzione di valori non omogenea nell'intervallo. Il che restringe il numero di possibili sequenze (pseudo, ok, non lo ripeto più) casuali, diciamo all'incirca alla metà. Se per gli scopi che ti prefiggi ti bastano, ok, altrimenti devi usare qualcosa di leggermente differente. Come vedi, in pratica (soprattutto per un progettino didattico e non per far volare un aereo) è solo una questione di "seed" come ti ha già evidenziato correttamente anche @Datman.

Sapendo che la "randomSeed()" accetta a parametro un "unsigned long", ossia un intero 32 bit, dargli in pasto un "int" per di più "tagliato" a soli 10 bit è uno spreco.
Quello che ho fatto per un progettino di tempo fa, nel quale volevo ben più di quelle circa 500 possibili sequenze diverse, è stato combinare più letture analogiche "floating" per comporre un valore più ampio. Una cosa di questo tipo, simile a quello che feci anni fa:

void setup()
{
  Serial.begin(115200);
}

unsigned long vMin = 10000000, vMax = 0;

void loop()
{
  Serial.print(bestSeed(A0));
  Serial.print(" ");Serial.print(vMin);
  Serial.print(" ");Serial.println(vMax);
  delay(100);
}

unsigned long bestSeed(byte pin) {
  analogRead(pin);
  delay(50);
  unsigned long seed = analogRead(pin);
  for (unsigned long n=100; n < 100000; n *= 10) {
    seed += analogRead(pin) * n;
    delay(10);
  }
  if (seed < vMin) vMin = seed;
  if (seed > vMax) vMax = seed;
  return seed;
}

Da un breve test ottengo valori in un range di ampiezza all'incirca pari a 10.000.000, quindi teoricamente fino a 10 milion di possibili diverse sequenze. Direi più che sufficiente.

2 Likes

Si è vero se lascio
randomSeed(analogRead(0)); e con l'antenna ottengo numeri diversi.
Ho fatto confusione

Ok può capitare, tranquillo.

Però comunque se vuoi maggiore variabilità usa la funzione che ti ho scritto, la "bestSeed()" così:

/*
  Simulatore roulette
*/

unsigned int rnd;
void setup() {
  Serial.begin(9600);
  randomSeed(bestSeed(A0));
}
void loop() {
  for (int c = 0; c <= 100; c++) {
    rnd = random(0,37);
    Serial.println(rnd);
  }
delay(1000);
}

unsigned long bestSeed(byte pin) {
  analogRead(pin);
  delay(50);
  unsigned long seed = analogRead(pin);
  for (unsigned long n=100; n < 100000; n *= 10) {
    seed += analogRead(pin) * n;
    delay(10);
  }
  return seed;
}
1 Like

In questo caso il doppio analogRead si può omettere, perché non serve precisione.

1 Like

Grazie :waving_hand:

Per semplicità, immagina di volere dei numeri da 0 a 8 invece che da 0 a 36. Map fa questo:

Il valore 8 lo ottieni solo per 65535.

Mentre quello che volevi tu era questo: