I am writing a program for card games. There can be several game players (say, from 2 to 7). A deck consists of 54 cards. I need to distribute/deal cards to the players randomly.
We can consider the deck of 54 cards as a char array of 54 elements. Let us suppose that in a certain game each player must be given with 6 cards. The number of players is 2. So, it is necessary to generate two arrays, each of them consists of 6 elements selected from a "big" array of 54 elements. Moreover, in those two generated arrays there should not be shared/duplicate elements.
I tried a recursive algorithm to obtain a sequence of m unique random numbers from 0 to (m - 1).
X(n+1) = (a * X(n) + c) mod m
You need to set the parameters:
m -- module, m > 0
a -- factor, 0 <= a < m
c -- increment, 0 <= c < m
X(0) -- initial value , 0 <= X(0) < m
Numbers c and m must be coprime.
(a - 1) is divisible by p for each prime p that is a divisor of m
If m is divisible by 4 then (a - 1) must be divisible by 4.
Here's the code for this algorithm. As you can see, the parameters a, c, m and X(0) satisfy the mentioned conditions.
int a = 13,
c = 11,
m = 54, // because the total number of cards is 54
x0 = 1;
int x[100];
x[0] = x0;
cout << x[0] << " ";
for (int i = 1; i < m; i++)
{
x[i] = (a * x[i - 1] + c) % m;
cout << x[i] << " ";
}
The result is: 1 24 53 52 39 32 49 0 11 46 15 44 43 30 23 40 45 2 37 6 35 34 21 14 31 36 47 28 51 26 25 12 5 22 27 38 19 42 17 16 3 50
13 18 29 10 33 8 7 48 41 4 9 20. Do you think it is random?
What can you say about this algorithm? In general, what should be the idea of a random distribution of cards for each player?
You see, if I integrate this algorithm to my program, it will deal the same sequence of cards as it is shown above each time you launch the program (because the parameters do not change). So I will need to change a, m, c and X(0) between launches of my program. Then I will have another problem: how to set these parameters automatically (and randomly, too) so that they satisfy the necessary conditions (see the bulleted list above).
It seems to me like you're making an unnecessarily complex system.
A much simpler approach is to create an array of all of your elements, shuffle it, and then just remove elements one at a time.
A simple and efficient way of shuffling is to use a Fisher-Yates shuffle:
//Initialize an array/vector/etc. with all the possible values
for (int i = NUMBER_OF_ELEMENTS-1; i >= 0; i--)
{
//Pick a random integer j between 0 and i (inclusive)
//Swap elements i and j
}
Now, you can just iterate through the shuffled array, picking the next element every time you need a new card.
int pos = 0; //The position of the next card in the deck
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < NUMBER_OF_PLAYERS; j++)
{
player[j].addCard(deck[pos++])
}
}
Ideally, you would probably want to wrap some of this into classes, but I've left that out for brevity.
You cannot guarantee randomness the way you put it. It is a generated sequence with low informational enthropy - in other words it is easily hacked.
You can simply use standard rand() from stdlib http://www.cplusplus.com/reference/cstdlib/rand/.
I'd recommend using mt19937 comes with std in c++11 http://www.cplusplus.com/reference/random/mt19937/ or boost one as mentioned in comments.
another way to do it, could be to randomize the action of taking a card instead of a shuffle the container.
something like this :
// first step
// init and fill container
std::vector<int> v;
for (int i = 0; i < 54; ++i)
v.push_back(i);
// second step
// take a random card
srand(time(NULL)); // init seed
int i = std::rand() % v.size();
int card = v[i]; // get card
v.erase(vec.begin() + i); // remove card from deck
return card;
for the second step, you need <ctime> and <cstdlib>. I am not sure it is better than the other solution. Just my two cents.
Related
Various signal towers are present in a city.Towers are aligned in a straight horizontal line(from left to right) and each tower transmits a signal in the right to left direction.Tower A shall block the signal of Tower B if Tower A is present to the left of Tower B and Tower A is taller than Tower B. So,the range of a signal of a given tower can be defined as :
{(the number of contiguous towers just to the left of the given tower whose height is less than or equal to the height of the given tower) + 1}.
#include <iostream>
#include <vector>
using namespace std;
vector<int> res;
void recursion(int a[],int x)
{
if (x >= 0)
{// Taking the last element of the array as the max element
int max = a[x], count = 0;
for (int i = x; i >= 0; i--)
{//Comparing the max with all the elements in the array
if (max >= a[i])
{
count++;
}
else
{
break;
}
}
//Pushing the count of the current element in the vector.
res.push_back(count);
x = x - 1;
recursion(a, x);
}
}
int main() {
int TestCase, n;
cin >> TestCase;
for (int l = 0; l < TestCase; l++)
{
cin >> n;
int * arr = new int[n];
//Getting the elements
for (int j = 0; j < n; j++)
{
cin >> arr[j];
}
recursion(arr, n-1);
//Iterating through the vector in reverse manner and printing
//the result.
for (auto it = res.rbegin(); it != res.rend(); ++it)
{
cout << *it << " ";
}
delete[] arr;
}
return 0;
}
First line contains an integer T specifying the number of test cases.
Second line contains an integer n specifying the number of towers.
Third line contains n space separated integers(H[i]) denoting the height
of each tower.
Print the range of each tower (separated by a space).
Sample Input:
1
7
100 80 60 70 60 75 85
Sample Output:
1 1 1 2 1 4 6
My solution is correct but the time complexity is the issue. Is there any way to reduce time complexity?
To calculate the range of each tower which emits signals to the left, you need to use a stack structure.
We go from left to right in the array and we will store each element in the stack. Now, we insert towers in the stack.
Whenever we get to the next tower, we keep popping towers from the stack whose height is less than the current tower. Once done, we will insert the current tower in the stack and so on for subsequent towers.
Important thing to note here is that you will need to store the no. of towers beaten by the current tower too when you insert them in the stack.
Answer for each tower(Except the base case) is no. of towers beaten + 1.
The integer inside {} below is the no. of towers beaten by the current tower.
Example:
100 80 60 70 60 75 85
^
Stack is empty when we are at 100, so we insert it into the stack and print answer for it as 1 considering that as the base answer.
Current stack: 100{0}
80 60 70 60 75 85
^
Now, let's test for 80. When tower 80 emits signals to the left, we keep pooping all elements from the stack which are less than 80 and stop when we get a block(that is, a tower with same or higher height). In this case, we stop at 100 itself. So, the distance covered by the signal is 1.
Current stack: 100{0} 80{0}
60 70 60 75 85
^
Now, the answer for 60 is 1 again.
Current stack: 100{0} 80{0} 70{1}
70 60 75 85
^
For 70, signals beat 60 and stop at 80, so answer for 70 is no. of towers beaten + 1, so 1 + 1 = 2.
Current stack: 100{0} 80{0} 70{1} 60{0}
60 75 85
^
60 beats nobody, so 0 + 1 = 1.
Current stack: 100{0} 80{0} 75{3}
75 85
^
75 beats 60 and 70, but we get 3 towers beaten since we add no. of towers beaten by the smaller towers + that tower itself as well. So, to put it in simple words,
60{0} is 1(60 itself) + 0(no. of towers beaten by 60 itself) + 1 (70 itself) + 1(no. of towers beaten by 70 itself) = 1 + 1 + 1 = 3. Answer for 75 is 3 + 1 = 4.
Current stack: 100{0} 85{5}
85
^
Answer for 85 is 5 + 1 = 6 => 1(of 75) + 3(of 75 beats) + 1(of 80) + 0 (of 80 beats).
Hope this answers your question.
I have a word corpus of say 3000 words such as [hello, who, this ..].
I want to find the nth 3 word combination from this corpus.I am fine with any order as long as the algorithm gives consistent output.
What would be the time complexity of the algorithm.
I have seen this answer but was looking for something simple.
(Note that I will be using 1-based indexes and ranks throughout this answer.)
To generate all combinations of 3 elements from a list of n elements, we'd take all elements from 1 to n-2 as the first element, then for each of these we'd take all elements after the first element up to n-1 as the second element, then for each of these we'd take all elements after the second element up to n as the third element. This gives us a fixed order, and a direct relation between the rank and a specific combination.
If we take element i as the first element, there are (n-i choose 2) possibilities for the second and third element, and thus (n-i choose 2) combinations with i as the first element. If we then take element j as the second element, there are (n-j choose 1) = n-j possibilities for the third element, and thus n-j combinations with i and j as the first two elements.
Linear search in tables of binomial coefficients
With tables of these binomial coefficients, we can quickly find a specific combination, given its rank. Let's look at a simplified example with a list of 10 elements; these are the number of combinations with element i as the first element:
i
1 C(9,2) = 36
2 C(8,2) = 28
3 C(7,2) = 21
4 C(6,2) = 15
5 C(5,2) = 10
6 C(4,2) = 6
7 C(3,2) = 3
8 C(2,2) = 1
---
120 = C(10,3)
And these are the number of combinations with element j as the second element:
j
2 C(8,1) = 8
3 C(7,1) = 7
4 C(6,1) = 6
5 C(5,1) = 5
6 C(4,1) = 4
7 C(3,1) = 3
8 C(2,1) = 2
9 C(1,1) = 1
So if we're looking for the combination with e.g. rank 96, we look at the number of combinations for each choice of first element i, until we find which group of combinations the combination ranked 96 is in:
i
1 36 96 > 36 96 - 36 = 60
2 28 60 > 28 60 - 28 = 32
3 21 32 > 21 32 - 21 = 11
4 15 11 <= 15
So we know that the first element i is 4, and that within the 15 combinations with i=4, we're looking for the eleventh combination. Now we look at the number of combinations for each choice of second element j, starting after 4:
j
5 5 11 > 5 11 - 5 = 6
6 4 6 > 4 6 - 4 = 2
7 3 2 <= 3
So we know that the second element j is 7, and that the third element is the second combination with j=7, which is k=9. So the combination with rank 96 contains the elements 4, 7 and 9.
Binary search in tables of running total of binomial coefficients
Instead of creating a table of the binomial coefficients and then performing a linear search, it is of course more efficient to create a table of the running total of the binomial coefficient, and then perform a binary search on it. This will improve the time complexity from O(N) to O(logN); in the case of N=3000, the two look-ups can be done in log2(3000) = 12 steps.
So we'd store:
i
1 36
2 64
3 85
4 100
5 110
6 116
7 119
8 120
and:
j
2 8
3 15
4 21
5 26
6 30
7 33
8 35
9 36
Note that when finding j in the second table, you have to subtract the sum corresponding with i from the sums. Let's walk through the example of rank 96 and combination [4,7,9] again; we find the first value that is greater than or equal to the rank:
3 85 96 > 85
4 100 96 <= 100
So we know that i=4; we then subtract the previous sum next to i-1, to get:
96 - 85 = 11
Now we look at the table for j, but we start after j=4, and subtract the sum corresponding to 4, which is 21, from the sums. then again, we find the first value that is greater than or equal to the rank we're looking for (which is now 11):
6 30 - 21 = 9 11 > 9
7 33 - 21 = 12 11 <= 12
So we know that j=7; we subtract the previous sum corresponding to j-1, to get:
11 - 9 = 2
So we know that the second element j is 7, and that the third element is the second combination with j=7, which is k=9. So the combination with rank 96 contains the elements 4, 7 and 9.
Hard-coding the look-up tables
It is of course unnecessary to generate these look-up tables again every time we want to perform a look-up. We only need to generate them once, and then hard-code them into the rank-to-combination algorithm; this should take only 2998 * 64-bit + 2998 * 32-bit = 35kB of space, and make the algorithm incredibly fast.
Inverse algorithm
The inverse algorithm, to find the rank given a combination of elements [i,j,k] then means:
Finding the index of the elements in the list; if the list is sorted (e.g. words sorted alphabetically) this can be done with a binary search in O(logN).
Find the sum in the table for i that corresponds with i-1.
Add to that the sum in the table for j that corresponds with j-1, minus the sum that corresponds with i.
Add to that k-j.
Let's look again at the same example with the combination of elements [4,7,9]:
i=4 -> table_i[3] = 85
j=7 -> table_j[6] - table_j[4] = 30 - 21 = 9
k=9 -> k-j = 2
rank = 85 + 9 + 2 = 96
Look-up tables for N=3000
This snippet generates the look-up table with the running total of the binomial coefficients for i = 1 to 2998:
function C(n, k) { // binomial coefficient (Pascal's triangle)
if (k < 0 || k > n) return 0;
if (k > n - k) k = n - k;
if (! C.t) C.t = [[1]];
while (C.t.length <= n) {
C.t.push([1]);
var l = C.t.length - 1;
for (var i = 1; i < l / 2; i++)
C.t[l].push(C.t[l - 1][i - 1] + C.t[l - 1][i]);
if (l % 2 == 0)
C.t[l].push(2 * C.t[l - 1][(l - 2) / 2]);
}
return C.t[n][k];
}
for (var total = 0, x = 2999; x > 1; x--) {
total += C(x, 2);
document.write(total + ", ");
}
This snippet generates the look-up table with the running total of the binomial coefficients for j = 2 to 2999:
for (var total = 0, x = 2998; x > 0; x--) {
total += x;
document.write(total + ", ");
}
Code example
Here's a quick code example, unfortunately without the full hardcoded look-up tables, because of the size restriction on answers on SO. Run the snippets above and paste the results into the arrays iTable and jTable (after the leading zeros) to get the faster version with hard-coded look-up tables.
function combinationToRank(i, j, k) {
return iTable[i - 1] + jTable[j - 1] - jTable[i] + k - j;
}
function rankToCombination(rank) {
var i = binarySearch(iTable, rank, 1);
rank -= iTable[i - 1];
rank += jTable[i];
var j = binarySearch(jTable, rank, i + 1);
rank -= jTable[j - 1];
var k = j + rank;
return [i, j, k];
function binarySearch(array, value, first) {
var last = array.length - 1;
while (first < last - 1) {
var middle = Math.floor((last + first) / 2);
if (value > array[middle]) first = middle;
else last = middle;
}
return (value <= array[first]) ? first : last;
}
}
var iTable = [0]; // append look-up table values here
var jTable = [0, 0]; // and here
// remove this part when using hard-coded look-up tables
function C(n,k){if(k<0||k>n)return 0;if(k>n-k)k=n-k;if(!C.t)C.t=[[1]];while(C.t.length<=n){C.t.push([1]);var l=C.t.length-1;for(var i=1;i<l/2;i++)C.t[l].push(C.t[l-1][i-1]+C.t[l-1][i]);if(l%2==0)C.t[l].push(2*C.t[l-1][(l-2)/2])}return C.t[n][k]}
for (var iTotal = 0, jTotal = 0, x = 2999; x > 1; x--) {
iTable.push(iTotal += C(x, 2));
jTable.push(jTotal += x - 1);
}
document.write(combinationToRank(500, 1500, 2500) + "<br>");
document.write(rankToCombination(1893333750) + "<br>");
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm trying to produce an array with 4 random generated [srand seeded rand()] without any repeats. I'm using a for loop to:
Select a position in the array,
Generate a number,
Assign the number to the currently highlighted position
Check that the assigned number is not equal to a previous entry, as per the following pseudocode.
if no -
Then select the next position in the array and generate a new number
if yes -
Do not move to the next array position and generate a new number again.
repeat until array position 3
This was my attempt:
int operator_selection;
int operator_index[3];
int random_value;
for (operator_selection = 0; operator_selection < 4; operator_selection++)
{
random_value = rand() %4 + 1;
if (random_value = operator_index[0] || operator_index[1] || operator_index[2])
{
(operator_selection - 1);
}
operator_index[operator_selection] = random_value;
cout<<operator_index[operator_selection]<<" ";
if (operator_selection == 3)
{
cout<<endl;
}
}
However when I run the executable I always end up with repeats, so I'm pretty sure the logic behind my first 'if statement' is flawed.
I'm a c++ beginner and this is my 3rd attempt at writing a source file from scratch, so apologies if I've made a silly mistake.
I see several problems in your posted code.
Problem 1
The line
if (random_value = operator_index[0] || operator_index[1] || operator_index[2])
does not do what you are hoping to do. You need to use:
if ( (random_value == operator_index[0]) ||
(random_value == operator_index[1]) ||
(random_value == operator_index[2]) )
Problem 2
Comparing random_value against operator_index[0] and operator_index[1] and operator_index[2] is incorrect. You only need to compare up to operator_index[operator_selection-1].
Problem 3
The line
(operator_selection - 1);
does not change the value of operator_selection. It just evaluates the expression and discards the value.
What you need is a statement that decrements the value of operator_selection. E.g.
--operator_selection;
Problem 4
You need to continue to the next iteration of the loop when you find an existing value.
Here's an updated version of the loop:
for (operator_selection = 0; operator_selection < 4; operator_selection++)
{
random_value = rand() %4 + 1;
bool matchFound = false;
for ( int i = 0; i < operator_selection-1; ++i )
{
if ( random_value == operator_index[i] )
{
matchFound = true;
break;
}
}
if ( matchFound )
{
--operator_selection;
continue;
}
operator_index[operator_selection] = random_value;
cout<<operator_index[operator_selection]<<" ";
}
// Move this out of the loop.
cout<<endl;
Here is a version using std::array and std::random_shuffle:
#include <iostream>
#include <array>
#include <algorithm>
#include <random>
int main()
{
std::array<int, 4> a = {1, 2, 3, 4};
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(a.begin(), a.end(), g);
for(auto& i : a)
std::cout << i << " ";
}
Live Demo
This version is more readable and more efficient.
Update: this does not answer the question as it is and does not fit if it's a home-work. But I would leave it here just in the case OP is interested in a better alternative.
First, you need to make your array bigger. As already mentioned, it would be nicer to use a std::array, but I'll stick with the old style one. You define operator_index with a dimension of 3 which only allows for 3 elements (with offsets from 0 to 2). So this needs a dimension of 4.
You should also initialize the contents of the array (or ensure that you never read from uninitialized elements).
Next, for the first random number there is no possibility of a collision. So you can put it directly into the array.
operator_index[0] = rand() %4 + 1;
You can then loop from 1 to 3 for the remaining 3 entries.
You can even go a little further than this. When you have filled the first 3 entries you can directly calculate the last,
operator_index[3] = 10 - operator_index[2] - operator_index[1] - operator_index[0];
(the sum from 1 to 4 is 10, so the last element is 10 - the sum of the first three)
The main problem with your code is this
if (random_value = operator_index[0] || operator_index[1] || operator_index[2])
{
(operator_selection - 1);
}
This does an assignment, not an equality check. It is assigned the logical OR of the first 3 elements. Since you do not initialize the array you will be reading garbage, and the result is probably going to be that random_value will be set to one and the condition will evaluate to true.
(operator_selection - 1) is an operator without side effects. It does not modify operator_selection. Also once you have found a duplicate, you want to start your loop again.
Here's a version that minimizes the looping.
#include <iostream>
#include <cstdlib>
#include <ctime>
int main()
{
int operator_selection;
int operator_index[4] = {0};
int random_value;
srand(time(0));
operator_index[0] = rand() %4 + 1;
for (operator_selection = 1; operator_selection < 3; operator_selection++)
{
random_value = rand() %4 + 1;
if (operator_index[0] == random_value || operator_index[1] == random_value)
{
--operator_selection;
continue;
}
operator_index[operator_selection] = random_value;
}
operator_index[3] = 10 - operator_index[2] - operator_index[1] - operator_index[0];
for(auto& elem : operator_index)
std::cout << elem << " ";
std::cout << "\n";
}
All that said, I still prefer the std::random_shuffle approach, which I would also suggest.
Another trick to is to reverse the lopp one step back if the conditions are not met.
#include <iostream>
#include <ctime>
using namespace std;
int main(void)
{
const int size=100 ;
int arr[100] ;
int i=0;
srand(time(0));
for ( i=0;i<size;i++) {
arr[i]=rand() % size;
for(int j=0; j < i ; j++) if (arr[j] == arr[i]) i--;
}
cout<<" \n\n\n ";
// Loop to display the array arr[ ]
for ( i=0;i<size;i++) cout<<""<<arr[i]<<"\t";
cout<<" \nPress any key to continue\n";
cin.ignore();
cin.get();
return 0;
}
output:
91 71 14 65 12 25 64 98 83 28
99 9 5 0 89 36 95 55 73 90
78 2 52 70 39 63 17 50 7 58
34 84 40 51 20 31 38 32 35 49
61 66 72 92 6 59 41 13 22 23
81 56 1 16 21 62 57 10 11 54
77 86 76 93 4 96 8 33 94 67
29 48 15 82 97 37 26 46 43 80
68 85 60 30 42 53 18 69 45 88
47 79 75 44 24 27 74 3 19 87
Press any key to continue
This question already has answers here:
Get `n` random values between 2 numbers having average `x`
(5 answers)
Closed 6 years ago.
Problem: Getting a set of random numbers between two values that will have a certain mean value.
Let say we getting n number of random number where the number will be between 1 and 100. We have a mean of 25.
My first approach is to have 2 modes where we have aboveMean and belowMean where the first random number is the initial range 1 and 100. Every subsequent number will check the total sum. If the total sum is above the mean, we go to case aboveMean which is then get a random number between 1 and 25. If the total sum is below the mean, we do case belowMean then get a random number between 26 and 100.
I need some idea on how to approach this problem beside the crude get a random number to add it to the total then get the average. If it above the mean in question, we get a random number below the mean and so forth. While it does work, doesn't seem to be the best method.
I'm guessing I should brush up on probability to approach this random number generator.
Let us divide the range into left and right portions. Use a value from the portion at a frequency corresponding to the relative width of the other half.
int Leruce_rand(int min, int mean, int max) {
int r = rand()%(max - min + 1);
if (r < mean) {
// find number in right half
return rand()%(max - mean + 1) + mean;
} else {
// find number in left half
return rand()%(mean - min) + min;
}
Assumes mean is part of the right half. This quick solution likely has small bias.
Given OP's values, roughly, the average of the left half is 12.5 and called 75% of the time. Average of the right is 62.5 called 25% of the time: average 25.
This approach differs from OP's which "Every subsequent number will check the total sum. If the total sum is above the mean, we go to case aboveMean which is then get a random number between 1 and 25." As that absolutely prevents a set of occurrences above or below the mean. With RNG, the value generated should not be biased on the history of previous generated values.
There's literally an infinite number of ways to achieve this. For instance, generate 3 random numbers between 1 and 100 (std::uniform_int_distribution) and take the minimum of those (std::min(a,b,c)).
Obviously, for a mean of 75 you'll need to pick the maximum of 3 numbers.
The benefit of this method is that each outcome is independent of the previous ones. It's completely random.
Take some good distribution and use it. Say, Binomial distribution. Use B(99,24/99),
so sampled values are in the range 0...99, with parameter p equal to 24/99.
So if you have routine which sample from B, then all you need is to add 1
to be in he range 1...100
Mean value for binomial would be p*n, in this case equal to 24. Because you're adding 1, your mean value would be 25 as required. C++11 has binomial RNG in the
standard library
Some code (not tested)
#include <iostream>
#include <random>
int main() {
std::default_random_engine generator;
std::binomial_distribution<int> distribution(99, double(24)/double(99));
for (int i=0; i != 1000; ++i) {
int number = distribution(generator) + 1;
std::cout << number << std::endl;
}
return 0;
}
Assume a fair random(a,b) function (this question should not be about which random function is better) then simply just restrcting ithe ranges that is piced from should be a good start, like;
const int desiredCount = 16;
const int deiredMean = 25;
int sumValues = random(a,b);
int count = 1;
while (count < desriredCount - 1) {
int mean = sumValue/count;
int nextValue = 0;
if (mean < desiredMean) // Too small, reduce probablity of smaller numbers
nextValue = random(a+(desiredMean-mean)/(desriredCount-count),b);
else //too large, reduce probability of larger numbers
nextValue = random(a,b-(mean-desiredMean)/(desriredCount-count));
sumValue += nextValue;
count += 1;
}
int lastValue = desiredMean*desriredCount - sumValue/count;
sumValue += lastValue;
count += 1;
Note: The above is not tested, and my thinking is that the trimming of the upper and lower bound may not be sufficently aggressive to do the trick, but I hope that i will get you going.
Some boundary conditions, such as if you only want 2 numbers and a means of 25 from numbers between 0 and 100, the initial random number cannot be larger than 50, since that makes it impossible to pick the second (last) number -- so if you want the algo to give you exact mean values under all circumstances, then a bit more tweaking is needed.
OP's wants a set of numbers meeting certain criteria.
Consider generating all possible sets of n numbers in the range [min max] and then eliminating all sets but those with the desired mean. Now randomly select one of those sets. This would meet OP's goal and IMO would pass fair randomness tests. Yet this direct approach is potentially a huge task.
Alternatively, randomly generate lots of sets until one is found that meets the mean test.
The below meets OP's requirement of a specified mean without directly biasing the random numbers selected. Certainly not an efficient method when the desired mean is far from the min/max average.
#include <stdio.h>
#include <stdlib.h>
void L_set(int *set, size_t n, int min, int mean, int max) {
assert(n > 0);
assert(min >= 0);
assert(mean >= min);
assert(max >= mean);
size_t i;
long long diff;
long long sum_target = n;
unsigned long long loop = 0;
sum_target *= mean;
int range = max - min + 1;
do {
loop++;
long long sum = 0;
for (i = 1; i < n; i++) {
set[i] = rand() % range + min;
sum += set[i];
}
diff = sum_target - sum; // What does the final number need to be?
} while (diff < min || diff > max);
set[0] = (int) diff;
printf("n:%zu min:%d mean:%2d max:%3d loop:%6llu {", n, min, mean, max, loop);
for (i = 0; i < n; i++) {
printf("%3d,", set[i]);
}
printf("}\n");
fflush(stdout);
}
int main(void) {
int set[1000];
L_set(set, 10, 1, 2, 4);
L_set(set, 16, 1, 50, 100);
L_set(set, 16, 1, 25, 100);
L_set(set, 16, 1, 20, 100);
return 0;
}
Output
n:10 min:1 mean: 2 max: 4 loop: 1 { 4, 2, 4, 3, 2, 1, 1, 1, 1, 1,}
n:16 min:1 mean:50 max:100 loop: 2 { 45, 81, 24, 50, 93, 65, 70, 52, 28, 91, 25, 36, 21, 45, 11, 63,}
n:16 min:1 mean:25 max:100 loop: 3257 { 52, 1, 15, 70, 66, 30, 1, 4, 26, 1, 16, 4, 48, 42, 19, 5,}
n:16 min:1 mean:20 max:100 loop:192974 { 24, 10, 13, 3, 3, 53, 22, 12, 29, 1, 7, 6, 90, 11, 20, 16,}
you have to go into some probabilities theory. there are a lot of methods to judge on random sequence. for example if you lower the deviation you will get triangle-looking-on-a-graph sequence, which can in the end be proven not trully random. so there is not really much choice than getting random generator and discarding the sequences you don't like.
EDIT: this generates numbers in the range 1..100 with a theoretical mean of 25.25. It does this by using a random modulus in the range 1..100. Note that the required mean is 25, which is not exactly a quarter of the range 1..100.
OP wanted a way of varying the next number chosen according to whether the mean is less than or more than 25, but that lends some predictabilty - if the mean is more than 25 then you know the next "random" number will be less than 25.
The random calculation in the code is a very simple one line.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define RUNS 10000000
#define MAXN 100
int main() {
int n, i, sum = 0, min = MAXN, max = 0;
int freq[MAXN+1] = {0};
srand((unsigned)time(NULL));
for(i = 0; i < RUNS; i++) {
n = 1 + rand() % (1 + rand() % 100); // average modulus is (1 + MAX) / 2
if(max < n) {
max = n; // check that whole range is picked
}
if(min > n) {
min = n;
}
freq[n]++; // keep a tally
sum += n;
}
// show statistis
printf("Mean = %f, min = %d, max = %d\n", (double)sum / RUNS, min, max);
for(n = MAXN; n > 0; n--) {
printf("%3d ", n);
for(i = (freq[n] + 5000) / 10000; i > 0; i--) {
printf("|");
}
printf("\n");
}
return 0;
}
Program output showing distribution / 10000:
Mean = 25.728128, min = 1, max = 100
100
99
98
97
96 |
95 |
94 |
93 |
92 |
91 |
90 |
89 |
88 |
87 |
86 ||
85 ||
84 ||
83 ||
82 ||
81 ||
80 ||
79 ||
78 |||
77 |||
76 |||
75 |||
74 |||
73 |||
72 |||
71 ||||
70 ||||
69 ||||
68 ||||
67 ||||
66 ||||
65 ||||
64 |||||
63 |||||
62 |||||
61 |||||
60 |||||
59 |||||
58 ||||||
57 ||||||
56 ||||||
55 ||||||
54 ||||||
53 ||||||
52 |||||||
51 |||||||
50 |||||||
49 |||||||
48 |||||||
47 ||||||||
46 ||||||||
45 ||||||||
44 ||||||||
43 |||||||||
42 |||||||||
41 |||||||||
40 |||||||||
39 ||||||||||
38 ||||||||||
37 ||||||||||
36 ||||||||||
35 |||||||||||
34 |||||||||||
33 |||||||||||
32 ||||||||||||
31 ||||||||||||
30 ||||||||||||
29 |||||||||||||
28 |||||||||||||
27 |||||||||||||
26 ||||||||||||||
25 ||||||||||||||
24 ||||||||||||||
23 |||||||||||||||
22 |||||||||||||||
21 ||||||||||||||||
20 ||||||||||||||||
19 |||||||||||||||||
18 |||||||||||||||||
17 ||||||||||||||||||
16 |||||||||||||||||||
15 |||||||||||||||||||
14 ||||||||||||||||||||
13 |||||||||||||||||||||
12 ||||||||||||||||||||||
11 |||||||||||||||||||||||
10 ||||||||||||||||||||||||
9 |||||||||||||||||||||||||
8 ||||||||||||||||||||||||||
7 |||||||||||||||||||||||||||
6 |||||||||||||||||||||||||||||
5 |||||||||||||||||||||||||||||||
4 |||||||||||||||||||||||||||||||||
3 |||||||||||||||||||||||||||||||||||||
2 ||||||||||||||||||||||||||||||||||||||||||
1 ||||||||||||||||||||||||||||||||||||||||||||||||||||
OP did not state what kind of distribution was wanted, for example two straight lines pivoting at 25, or perhaps equal distribution each side of 25. However this solution is very simple to implement.
Is there efficient way to downscale number of elements in array by decimal factor?
I want to downsize elements from one array by certain factor.
Example:
If I have 10 elements and need to scale down by factor 2.
1 2 3 4 5 6 7 8 9 10
scaled to
1.5 3.5 5.5 7.5 9.5
Grouping 2 by 2 and use arithmetic mean.
My problem is what if I need to downsize array with 10 elements to 6 elements? In theory I should group 1.6 elements and find their arithmetic mean, but how to do that?
Before suggesting a solution, let's define "downsize" in a more formal way. I would suggest this definition:
Downsizing starts with an array a[N] and produces an array b[M] such that the following is true:
M <= N - otherwise it would be upsizing, not downsizing
SUM(b) = (M/N) * SUM(a) - The sum is reduced proportionally to the number of elements
Elements of a participate in computation of b in the order of their occurrence in a
Let's consider your example of downsizing 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 to six elements. The total for your array is 55, so the total for the new array would be (6/10)*55 = 33. We can achieve this total in two steps:
Walk the array a totaling its elements until we've reached the integer part of N/M fraction (it must be an improper fraction by rule 1 above)
Let's say that a[i] was the last element of a that we could take as a whole in the current iteration. Take the fraction of a[i+1] equal to the fractional part of N/M
Continue to the next number starting with the remaining fraction of a[i+1]
Once you are done, your array b would contain M numbers totaling to SUM(a). Walk the array once more, and scale the result by N/M.
Here is how it works with your example:
b[0] = a[0] + (2/3)*a[1] = 2.33333
b[1] = (1/3)*a[1] + a[2] + (1/3)*a[3] = 5
b[2] = (2/3)*a[3] + a[4] = 7.66666
b[3] = a[5] + (2/3)*a[6] = 10.6666
b[4] = (1/3)*a[6] + a[7] + (1/3)*a[8] = 13.3333
b[5] = (2/3)*a[8] + a[9] = 16
--------
Total = 55
Scaling down by 6/10 produces the final result:
1.4 3 4.6 6.4 8 9.6 (Total = 33)
Here is a simple implementation in C++:
double need = ((double)a.size()) / b.size();
double have = 0;
size_t pos = 0;
for (size_t i = 0 ; i != a.size() ; i++) {
if (need >= have+1) {
b[pos] += a[i];
have++;
} else {
double frac = (need-have); // frac is less than 1 because of the "if" condition
b[pos++] += frac * a[i]; // frac of a[i] goes to current element of b
have = 1 - frac;
b[pos] += have * a[i]; // (1-frac) of a[i] goes to the next position of b
}
}
for (size_t i = 0 ; i != b.size() ; i++) {
b[i] /= need;
}
Demo.
You will need to resort to some form of interpolation, as the number of elements to average isn't integer.
You can consider computing the prefix sum of the array, i.e.
0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10
yields by summation
0 1 2 3 4 5 6 7 8 9
1 3 6 10 15 21 28 36 45 55
Then perform linear interpolation to get the intermediate values that you are lacking, like at 0*, 10/6, 20/6, 30/5*, 40/6, 50/6, 60/6*. (Those with an asterisk are readily available).
0 1 10/6 2 3 20/6 4 5 6 40/6 7 8 50/6 9
1 3 15/3 6 10 35/3 15 21 28 100/3 36 45 145/3 55
Now you get fractional sums by subtracting values in pairs. The first average is
(15/3-1)/(10/6) = 12/5
I can't think of anything in the C++ library that will crank out something like this, all fully cooked and ready to go.
So you'll have to, pretty much, roll up your sleeves and go to work. At this point, the question of what's the "efficient" way of doing it boils down to its very basics. Which means:
1) Calculate how big the output array should be. Based on the description of the issue, you should be able to make that calculation even before looking at the values in the input array. You know the input array's size(), you can calculate the size() of the destination array.
2) So, you resize() the destination array up front. Now, you no longer need to worry about the time wasted in growing the size of the dynamic output array, incrementally, as you go through the input array, making your calculations.
3) So what's left is the actual work: iterating over the input array, and calculating the downsized values.
auto b=input_array.begin();
auto e=input_array.end();
auto p=output_array.begin();
Don't see many other options here, besides brute force iteration and calculations. Iterate from b to e, getting your samples, calculating each downsized value, and saving the resulting value into *p++.