This always creates confusion, perhaps because of the way they try to teach it in schools and books.
What you want is not to generate random numbers: you want a
permutation of the original values.
Here is the common path in these hours:
- confusing permutation with drawing the programmer writes
n
calls to rand()
for example and draw the n
numbers
- frustrated to realize that the numbers repeat the programmer creates a loop and an array and marks the numbers that already came out
- eventually discovers that for a "draw" of 100 numbers called as 6,000 times such a
rand()
This is as if in the church bingo the guys return the numbers drawn to the globe at each draw. And someone was controlling if the number has already come out.
In bingo the numbers don’t come back, as they don’t come back in the lottery draws. These are on display on a channel for everyone to check, and of course they won’t repeat. The first thing auditors do is check to see if you have one and one ball with each number.
This is the "algorithm" Fisher-Yates
That algorithm Fisher-Yates
If I were one of those, Fisher or Yates :D would not want my name and all this aura of sophistication to describe such an "algorithm". Here’s what this algorithm is, with its example of the colors of its own C code++:
string cores[] = {"amarelo","rosa","roxo","verde","azul",};
There’s five colors there. In C++
int total = sizeof(cores)/sizeof(string)
Perhaps it would be simpler to use a vector. C++ is a language much more expressive than C. And vectors already have a size, and can increase dynamically without having to allocate memory and things like that
Back to the algorithm, step by step
This "algorithm" is the expression of the obvious and it ends up looking complex, especially because when you think about a draw it seems that you always start to think about random numbers and not about a permutation.
Think of the Caixa Economica lottery draw : There are 60 identical balls of Mega Sena. In your case are 5 colors
Choose one of the colors, for example using rand()
as it did. Comes a value between 0
and RAND_MAX
. Then you take this value, divide by the number of colors and take the rest: it will of course be a number between 0 and 4.
Use this as an index to pick a color... 4, for example: then take color 4, "azul"
. Left 4 colors. Use rand()
and take another color: rand() % 4
. 3 for example, and strip the "verde"
3 colors remain. rand() % 3
comes 0 for example and takes the first color, "amarelo"
.
2 colors remain. rand() % 2
comes 0 for example and takes the first color: "rosa"
1 colour remains, the "roxo"
and ended the draw.
That’s the Fisher-Yates algorithm, what a child would do with a deck, what the CEF does with the balls.
Because the child successfully uses this "algorithm"? 'Cause she’s taking her cards out of the bag and into style Highlander, at the end only one remains.
Because the Caixa Econômica and church bingo use this "algorithm" successfully? Because the drawn balls come out of the drawing globe and have one and one ball with each number. And they are equal, have the same size and weight.
Example in C++, with twice the same draw
I’ll show you an example in C++ with its colors. I don’t recommend writing like this, it’s just a step-by-step example. If you are not familiar with the functions used here is a minimal summary: push_back()
insert into the vector, erase()
erases, size()
returns the size of the vector. A vector can have anything inside. In this case it is the strings. You do not need to use this. I did so because it is simple to see. As I said, C++ is not C. There are many better ways to do this.
It takes some kind of representation for the algorithm, that abstraction. And what you do is you use a numbered vector, just like the CEF balls. And do the draws using this vector of "balls". This is what should be there in the link I showed above. The vector in C++ is already half way because it can access by index and remove from the middle :). But it doesn’t even need that.
EXAMPLE IN C++
The Exit
Sao 5 cores no vetor de strings
Aqui RAND_MAX vale 32767
rand() % 5 = 4
rand() % 4 = 3
rand() % 3 = 0
rand() % 2 = 0
5 cores no inicio: amarelo rosa roxo verde azul
saiu 4 = azul, restaram 4: amarelo rosa roxo verde
saiu 3 = verde, restaram 3: amarelo rosa roxo
saiu 0 = amarelo, restaram 2: rosa roxo
saiu 0 = rosa, restaram 1: roxo
e acabou...
Novo sorteio usando um loop
5 cores no inicio: amarelo rosa roxo verde azul
saiu 4 = azul, restaram 4: amarelo rosa roxo verde
saiu 3 = verde, restaram 3: amarelo rosa roxo
saiu 0 = amarelo, restaram 2: rosa roxo
saiu 0 = rosa, restaram 1: roxo
o "sorteio" afinal: azul verde amarelo rosa roxo
The program
#include <cctype>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;
string letras[] = {
"matheus", "rayane", "nelson", "diogo", "jeferson",
};
string cores[] = {
"amarelo", "rosa", "roxo", "verde", "azul",
};
vector<string> v_cores = {
"amarelo", "rosa", "roxo", "verde", "azul",
};
int main()
{
int um = 1;
srand(210803);
int total = sizeof(cores)/sizeof(string);
cout << "Sao " << total << " cores no array de strings\n";
cout << "Sao " << v_cores.size() << " cores no vetor de strings\n";
cout << "Aqui RAND_MAX vale " << RAND_MAX << endl;
cout << "rand() % 5 = " << rand() % 5 << endl;
cout << "rand() % 4 = " << rand() % 4 << endl;
cout << "rand() % 3 = " << rand() % 3 << endl;
cout << "rand() % 2 = " << rand() % 2 << "\n\n\n";
// fisher-yates :) usando o vetor v_cores
srand(210803); // tudo de novo
cout << v_cores.size() << " cores no inicio: ";
for (auto cor : v_cores) cout << cor << " ";
cout << endl;
um = rand() % v_cores.size();
cout << "saiu " << um << " = " << v_cores[um];
v_cores.erase(v_cores.begin() + um);
cout << ", restaram " << v_cores.size() << ": ";
for (auto cor : v_cores) cout << cor << " ";
cout << endl;
um = rand() % v_cores.size();
cout << "saiu " << um << " = " << v_cores[um];
v_cores.erase(v_cores.begin() + um);
cout << ", restaram " << v_cores.size() << ": ";
for (auto cor : v_cores) cout << cor << " ";
cout << endl;
um = rand() % v_cores.size();
cout << "saiu " << um << " = " << v_cores[um];
v_cores.erase(v_cores.begin() + um);
cout << ", restaram " << v_cores.size() << ": ";
for (auto cor : v_cores) cout << cor << " ";
cout << endl;
um = rand() % v_cores.size();
cout << "saiu " << um << " = " << v_cores[um];
v_cores.erase(v_cores.begin() + um);
cout << ", restaram " << v_cores.size() << ": ";
for (auto cor : v_cores) cout << cor << " ";
cout << endl;
cout << " e acabou...\n\n";
// agora a mesma coisa num loop
// e aproveitando para colocar as
// cores sorteadas em um outro vetor
cout << "Novo sorteio usando um loop\n\n\n";
vector<string> sorteio{}; // vazio claro
vector<string> novas_cores = {
"amarelo", "rosa", "roxo", "verde", "azul",
};
// fisher-yates :) usando o vetor v_cores
srand(210803); // tudo de novo
cout << novas_cores.size() << " cores no inicio: ";
for (auto cor : novas_cores) cout << cor << " ";
cout << endl;
// enquanto tiver alguma cor sorteia e coloca
// claro no vetor 'sorteio'
while (novas_cores.size() > 1)
{
um = rand() % novas_cores.size();
cout << "saiu " << um << " = " << novas_cores[um];
// o obvio: pega o sorteado e coloca no outro vetor
sorteio.push_back(novas_cores[um]);
// claro, tira do original
novas_cores.erase(novas_cores.begin() + um);
cout << ", restaram " << novas_cores.size() << ": ";
for (auto cor : novas_cores) cout << cor << " ";
cout << endl;
}; // while()
// fim do loop entao tem sobrou so uma bolinha
// pra sortear
sorteio.push_back(novas_cores[0]); // a ultima bolinha
cout << "\n\no \"sorteio\" afinal: ";
for (auto cor : sorteio) cout << cor << " ";
cout << endl;
return 0;
}
Another example only with the final draw :D
#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
int um = 1;
srand(210803);
cout << "sorteio usando um loop\n\n\n";
vector<string> sorteio{}; // vazio claro
vector<string> novas_cores = {
"amarelo", "rosa", "roxo", "verde", "azul",
};
// fisher-yates :) usando o vetor v_cores
cout << novas_cores.size() << " cores no inicio: ";
for (auto cor : novas_cores) cout << cor << " ";
cout << endl;
// enquanto tiver alguma cor sorteia e coloca
// claro no vetor 'sorteio'
while (novas_cores.size() > 1)
{
um = rand() % novas_cores.size();
cout << "saiu " << um << " = " << novas_cores[um];
// o obvio: pega o sorteado e coloca no outro vetor
sorteio.push_back(novas_cores[um]);
// claro, tira do original
novas_cores.erase(novas_cores.begin() + um);
cout << ", restaram " << novas_cores.size() << ": ";
for (auto cor : novas_cores) cout << cor << " ";
cout << endl;
}; // while()
// fim do loop entao tem sobrou so uma bolinha
// pra sortear
sorteio.push_back(novas_cores[0]); // a ultima bolinha
cout << "\n\no \"sorteio\" afinal: ";
for (auto cor : sorteio) cout << cor << " ";
cout << endl;
return 0;
}
And this shows
sorteio usando um loop
5 cores no inicio: amarelo rosa roxo verde azul
saiu 4 = azul, restaram 4: amarelo rosa roxo verde
saiu 3 = verde, restaram 3: amarelo rosa roxo
saiu 0 = amarelo, restaram 2: rosa roxo
saiu 0 = rosa, restaram 1: roxo
o "sorteio" afinal: azul verde amarelo rosa roxo
I suggest running both on your machine and trying to understand the mechanism. Write back.
It was not I who denied, but I have another idea to suggest. : ) Use the fisher-yates algorithm to shuffle only the color vector and then use the corresponding indexes themselves to associate each color to a specific user. So you don’t need to create a means to ensure duplicate draws (which are totally possible and very frequent - by the way, it’s the lack of this mechanism that creates this flaw in your solution). This works in your case since the number of elements of the two vectors is the same.
– Luiz Felipe
If the number of elements is different between the two vectors, you can use a basic modular arithmetic to do the matching of indices, but this time shuffling the longest array.
– Luiz Felipe
Good Trade @Luizfelipe I even read about the algorithms you mentioned, but I’m very new to this part so I get lost in logic, let’s say I’m learning in "race". Not wanting to abuse... but could you help me a little more explaining how the algorithm works and what functions I use to implement it in my code ? Thanks for your help.
– Isabella Moreira
I didn’t understand very well, you want the letters (names) and colors appear only once? If so, a suggestion would be to replace the arrays with Vectors and delete the already chosen elements from the array.
– Vander Santos