Get the state (seed) of a distribution of random numbers - c++

See the following program for g++.
#define seed1 0
#include <iostream>
#include <random>
int main()
{
double mean = 0.0;
double stddev = 1.0;
std::mt19937 generator1 (seed1);
std::normal_distribution<double> normal(mean, stddev);
std::cerr << "Normal: " << normal(generator1) << std::endl;
}
I want to get the state of generator1 (as a seed) and remove generator1 for later
instantiate again the distribution with the new seed and go on in the place I left I want to put this code in a function and call it to generate Gaussian points in the start state I want. And at the end of the function save the state as a seed.

save the state as a seed
That will never happen, the state of a generator is much more than its seed.
However, generators (and distributions, which you ignored in your question) do provide functionality to store and retrieve their state through the << / >> operators respectively on streams:
stream << generator1 << normal;
And later:
mt19937 generator;
stream >> generator;
normal_distribution<double> distribution;
stream >> distribution;

Let me also add, that generators and distributions are also constructible and copyable from itself, if there is a need to reuse it later.
#define seed1 0
#include <iostream>
#include <random>
int main()
{
double mean = 0.0;
double stddev = 1.0;
std::mt19937 generator1 (seed1);
std::normal_distribution<double> normal(mean, stddev);
std::cerr << "Normal: " << normal(generator1) << std::endl;
std::mt19937 generator2 = generator1;
std::normal_distribution<double> normal2 = normal;
std::cerr << "Normal: " << normal(generator1) << std::endl;
std::cerr << "Normal2: " << normal2(generator2) << std::endl;
// I want to get the state of generator1 (as a seed) and remove generator1 for later
//instantiate again the distribution with the new seed and go on in the place I left
// I want to put this code in a function and call it to generate Gaussian points in
// the start state I want. And at the end of the function save the state as a seed.
}
So last lines print the same result, as states were initialized to equal values.

There is one way you could try. This involves saving the original seed, and counting how many times the generator is called. To restore the state, seed with the original seed and then call std::mt19937::discard()
Example:
#define seed1 0
#include <cassert>
#include <iostream>
#include <random>
// spoof a generator that counts the number of calls
class my_mt19937 : public std::mt19937 {
public:
result_type operator()() {
++call_count_;
return std::mt19937::operator()();
}
void seed(result_type value = default_seed) {
original_seed_ = value;
std::mt19937::seed(value);
}
void discard(unsigned long long z) {
call_count_ += z;
std::mt19937::discard(z);
}
unsigned long long call_count() const noexcept { return call_count_; }
result_type original_seed() const noexcept { return original_seed_; }
private:
result_type original_seed_ = default_seed;
unsigned long long call_count_ = 0;
};
int main() {
double mean = 0.0;
double stddev = 1.0;
my_mt19937 gen1;
gen1.seed(seed1);
const size_t N = 10'000;
for (size_t i = 0; i < N; ++i) {
my_mt19937 gen2;
gen2.seed(gen1.original_seed());
gen2.discard(gen1.call_count());
if (gen2() != gen1()) {
std::cout << "failed for i = " << i << "\n";
return 1;
}
}
// this extneds to distribution objects that
// use the generators
std::normal_distribution<double> normal1;
std::normal_distribution<double> normal2;
for (size_t i = 0; i < N; ++i) {
my_mt19937 gen2;
gen2.seed(gen1.original_seed());
gen2.discard(gen1.call_count());
if (normal1(gen1) != normal2(gen2)) {
std::cout << "failed for i = " << i << "\n";
return 1;
}
}
std::cout << "Success! Tested " << N << " values\n";
return 0;
}
You can play further with the code on godbolt: https://godbolt.org/z/8j9onsezs

Related

PageRank algorithm Runtime Errors on FPGA and GPUs using sycl

I'm having troubles doing experiments on fpga and gpu using sycl.
I'm working online with intel devcloud and I get these 2 runtime errors when performing the executables:
On FPGA:
terminate called after throwing an instance of 'sycl::_V1::runtime_error'
what(): Invalid device program image: size is zero -30 (PI_ERROR_INVALID_VALUE)
on GPU:
terminate called after throwing an instance of 'sycl::_V1::runtime_error'
what(): Native API failed. Native API returns: -1 (PI_ERROR_DEVICE_NOT_FOUND) -1 (PI_ERROR_DEVICE_NOT_FOUND)
Here's the main Codes:
#include <sycl/sycl.hpp>
#include <sycl/ext/intel/fpga_extensions.hpp>
// #include <oneapi/mkl/blas.hpp>
#include <cmath>
#include <chrono>
#include <iostream>
#include <vector>
#include <cmath>
#include "guideline.h"
#include "print_vector.h"
#include "print_time.h"
#include "read_graph.h"
#include "flatVector.h"
using namespace sycl;
int main(int argc, char* argv[]){
// Check Command Line
if(argc < 6){
// NOT ENOUGH PARAMS BY COMMAND LINE -> PROGRAM HALTS
guideline();
}
else{
// Command Line parsing
int device_selected = atoi(argv[1]);
std::string csv_path = argv[2];
double threshold = atof(argv[3]);
double damping = atof(argv[4]);
int verbose;
try{verbose = atoi(argv[5]);}
catch (exception const& e) {verbose = 0;}
device d = device(default_selector());
// Queue
queue q(d);
std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n"; // print del device
// Reading and setup Time Calculation
auto start_setup = std::chrono::steady_clock::now();
// Graph Retrieval by csv file
std::vector<std::vector<int>> graph = Read_graph(csv_path);/*Sparse Matrix Representation with the description of each Edge of the Graph*/
std::vector<int> flatGraph = flatten<int>(graph);
// Calculation of the # Nodes
int numNodes = countNodes(graph);
// Calculation of the Degree of each node
std::vector<int> degreesNodes = getDegrees(graph, numNodes+1);
auto end_setup = std::chrono::steady_clock::now();
// Setup Execution Time print
std::cout << "TIME FOR SETUP" << "\n";
print_time(start_setup, end_setup);
// Check Print
//printVector<int>(degreesNodes);
//Creation of Initial and Final Ranks' vectors of PageRank [R(t); R(t+1)]
std::vector<double> ranks_t(numNodes, (double)(1.0/ (double)(numNodes)));
std::vector<double> ranks_t_plus_one(numNodes, 0.0);
std::vector<double> ranksDifferences(numNodes, 0.0);
// PageRank Execution Time calculation
auto start = std::chrono::steady_clock::now();
buffer<int> bufferEdges(flatGraph.data(),flatGraph.size());
buffer<double> bufferRanks(ranks_t.data(),ranks_t.size());
buffer<int> bufferDegrees(degreesNodes.data(),degreesNodes.size());
buffer<double> bufferRanksNext(ranks_t_plus_one.data(),ranks_t_plus_one.size());
buffer<double> bufferRanksDifferences(ranksDifferences.data(),ranksDifferences.size());
double distance = threshold + 1;
int graph_size = flatGraph.size();
int T = 1;
while (distance > threshold) {
q.submit([&](handler &h){
accessor Edges(bufferEdges,h,read_only);
accessor Ranks(bufferRanks,h,read_only);
accessor Degrees(bufferDegrees,h,read_only);
accessor RanksNext(bufferRanksNext,h,write_only);
accessor RanksDifferences(bufferRanksDifferences,h,write_only);
h.parallel_for(range<1>(numNodes),[=] (id<1> i){
RanksNext[i] = (1.0 - damping) / numNodes;
int index_node_i;
int index_node_j;
for (int j = 0; j<graph_size;j+=2) {
index_node_i = j;
index_node_j = j + 1;
if (Edges[index_node_j] == i) {
RanksNext[i] += damping * Ranks[Edges[index_node_i]] / Degrees[Edges[index_node_i]];
}
}
RanksDifferences[i] = (RanksNext[i] - Ranks[i]) * (RanksNext[i] - Ranks[i]);
});
}).wait();
distance = 0;
for (int i = 0; i < numNodes; i++) {
distance += ranksDifferences[i];
ranks_t[i] = ranks_t_plus_one[i];
ranks_t_plus_one[i] = 0.0;
}
distance = sqrt(distance);
std::cout<< "Time:\t" << T << "\tEuclidian Distance:\t" << distance << std::endl;
T++;
}
auto end = std::chrono::steady_clock::now();
// PageRank Results Printing
if(verbose == 1){
for(int i = 0;i<ranks_t.size();i++){
std::cout<<"Final Vector" << i<< "-th component:\t"<<ranks_t[i]<<std::endl;
}
}
std::cout<<std::endl<<std::endl<<std::endl;
std::cout<<"Final Norm:\t"<<distance<<std::endl;
// PageRank Execution Time Printing
std::cout << "TIME FOR PAGERANK" << "\n";
print_time(start, end);
}
return 0;
}
flatVector.h
#include <iostream>
#include <vector>
template<typename T>
std::vector<T> flatten(const std::vector<std::vector<T>>& nestedVector) {
std::vector<T> flatVector;
for (const auto& subVector : nestedVector) {
for (const auto& element : subVector) {
flatVector.push_back(element);
}
}
return flatVector;
}
guideline.h
#include <iostream>
void guideline(){
std::cout<<"Not enough input parameters!\n\n";
std::cout<<"Usage guide:\n\n";
std::cout<<"First parameter:\tDevice code (as int number)\n";
std::cout<<"\t\t1: CPU\n";
std::cout<<"\t\t2: GPU\n";
std::cout<<"\t\t3: FPGA\n";
std::cout<<"Second parameter:\tCsv path of the dataset\n";
std::cout<<"Available Ones:\n\n";
std::cout<<"\t\t\"datasets/cit-Patents.csv\""<<std::endl;
std::cout<<"\t\t\"datasets/soc-LiveJournal1.csv\""<<std::endl;
std::cout<<"\t\t\"datasets/twitter-2010.csv\""<<std::endl;
std::cout<<"\t\t\"datasets/web-uk-2005-all.csv\""<<std::endl;
std::cout<<"Third parameter:\tThreshold (double value)\n";
std::cout<<"Fourth parameter:\tDamping (double value)\n";
std::cout<<"Fifth parameter:\tVerbose (int value)\n";
std::cout<<"Prints all the ranks of each node\n";
std::cout<<"Watch out! The print is huge\n";
}
printTime.h
#include <iostream>
#include <chrono>
void print_time(std::chrono::time_point<std::chrono::steady_clock> start, std::chrono::time_point<std::chrono::steady_clock> end){
std::cout << "Elapsed time in nanoseconds: " << std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() << " ns" << std::endl;
std::cout << "Elapsed time in microseconds: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << " µs" << std::endl;
std::cout << "Elapsed time in milliseconds: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << std::endl;
std::cout << "Elapsed time in seconds: " << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << " sec" << std::endl;
}
printVector.h
#include <iostream>
#include <vector>
template <typename T>
void printVector(std::vector<T> vector_like_var){
for(int i = 0; i < vector_like_var.size(); i++){
std::cout<< "element " <<i+1 << "of vector:\t" << vector_like_var[i] <<std::endl;
}
}
read_graph.h
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
// #include "print_vector.h"
std::vector<int> getDegrees(const std::vector<std::vector<int>> &graph, int numNodes) {
std::vector<int> degrees(numNodes);
for (auto &edge : graph) {
++degrees[edge[0]];
++degrees[edge[1]];
}
return degrees;
}
std::vector<std::vector<int>> Read_graph(std::string file_name){
// Apertura del file
std::ifstream file(file_name);
if (!file.is_open()) {
std::cerr << "Impossibile aprire il file" << std::endl;
return {};
}
// Lettura del file riga per riga
std::string line;
std::vector<std::vector<int>> graph;
while (getline(file, line)) {
std::stringstream ss(line);
std::string cell;
std::vector<int> edge;
while (getline(ss, cell, ',')) {
edge.push_back(stoi(cell));
}
graph.push_back(edge);
}
file.close();
return graph;
}
int countNodes(std::vector<std::vector<int>> graph){
int numNodes = 0;
for(auto &i : graph){
for(auto &j : i){
numNodes = std::max(numNodes, j);
}
}
return numNodes;
}
I know that it runs on CPU, because I've done experiments and it prints out the ranks.
I tried executing on different devices the experiment, for example by using gen 9 GPUs of intel, but it was no use.

How to create an array of normal distributed random number generators in c++?

I need a series of normally distributed random numbers, with different mean and variance, I know how to create one series with a particular mean and variance but can I have like an array of generators?
like for 1 series we have
#include <random>
#include <iostream>
using namespace std;
int main()
{
random_device rd;
mt19937 gen(rd());
normal_distribution<double> d(0.0, 1.0);
for (int i = 0; i < 5000; i++)
cout << " " << d(gen) << "\n";
return 0;
}
and this gives me a series of normally distributed random numbers, and i know i can create another d with another mean and variance for another series but is there any way to have a lot of such normal_distribution d together in an array so that i can choose a particular generator by simply choosing an element in the array.
I have tried a version where
#include <random>
#include <iostream>
using namespace std;
int main()
{
random_device rd;
mt19937 gen(rd());
normal_distribution<double> d(0.0, 1.0),d2(0.0,1.0);
normal_distribution<double> D[]={d,d2};
for (int i = 0; i < 5000; i++)
cout << " " << D[0](gen) << "\n";
system("pause");
return 0;
}
but I want to initialize it directly with the array like D0 some thing like that so that i can put it in a loop
Sure you can
#include <iostream>
#include <vector>
#include <random>
int main() {
std::vector<std::normal_distribution<double>> D{
std::normal_distribution<double>{0.0, 1.0 },
std::normal_distribution<double>{0.0, 2.0 } };
std::random_device rd;
std::mt19937 gen(rd());
std::cout << D[0](gen) << "\n";
std::cout << D[1](gen) << "\n";
return 0;
}
As you know C++ provide a functions for random numbers and we can also create and initialize array as given below. I hope it will helpful if not comment below.
const int nrolls=10000; // number of experiments
const int nstars=100; // maximum number of stars to distribute
std::default_random_engine generator;
std::normal_distribution<double> distribution(5.0,2.0);
int p[10]={};
for (int i=0; i<nrolls; ++i) {
double number = distribution(generator);
if ((number>=0.0)&&(number<10.0)) ++p[int(number)];
}
std::cout << "normal_distribution (5.0,2.0):" << std::endl;
for (int i=0; i<10; ++i) {
std::cout << i << "-" << (i+1) << ": ";
std::cout << std::string(p[i]*nstars/nrolls,'*') << std::endl;
}

Save and Load Random Number Generator State in C++11

This question has been asked before (stackoverflow) but the (accepted) answer is not satisfactory.
The following example saves and loads the state but depending on the number of generated values it works or it doesn't:
#include <fstream>
#include <iostream>
#include <random>
#include <cassert>
int main()
{
const int preN = 4;
const int middleN = 0;
// initialize randGen
std::mt19937 randGen1;
std::normal_distribution<double> distribution1;
// print some initial random numbers
for (int i=0;i<preN;++i)
std::cout << distribution1(randGen1)<<" ";
// save state
std::cout << std::endl << "Saving...\n";
{
std::ofstream fout("seed.dat");
fout << randGen1;
}
// maybe advance randGen1
for (int i=0;i<middleN;++i)
std::cout << distribution1(randGen1)<<" ";
// load saved state into randGen2
std::cout << std::endl << "Loading...\n";
std::ifstream fin("seed.dat");
std::mt19937 randGen2;
fin >> randGen2;
std::normal_distribution<double> distribution2;
// are both randGen equal?
assert(randGen1 == randGen2);
// print numbers from both generators
std::cout << "Generator1\tGenerator2\n";
std::cout << distribution1(randGen1) << "\t"
<< distribution2(randGen2) << "\n";
return 0;
}
With these parameters it works like expected. However, if I set preN=3 the output looks like:
0.13453 -0.146382 0.46065
Saving...
Loading...
Generator1 Generator2
-1.87138 0.163712
Why did the assert not apply? Now I set preN=3 and middleN=1 and the output is
0.13453 -0.146382 0.46065
Saving...
-1.87138
Loading...
Generator1 Generator2
0.163712 0.163712
If I set middleN to anything larger than 1 the assert applies.
Can anyone explain what is going on? What am I doing wrong or not understanding?
Tested with GCC5.4.0 and CLANG3.8.0 on Linux
The problem is not your random number generator's state. The problem is your distribution's state. Yes, distributions can have state too.
You can get the same values by resetting the normal distribution's state with reset. Alternatively, you can preserve and reconstitute the distribution's state too, using << and >>.
Thanks to the answer from Nicol Bolas above, I can add the corrected code below:
#include <fstream>
#include <iostream>
#include <random>
#include <cassert>
int main()
{
const int preN = 7;
const int middleN = 0;
// initialize another randGen
std::mt19937 randGen1;
std::normal_distribution<double> distribution1;
// print some initial random numbers
for (int i=0;i<preN;++i)
std::cout << distribution1(randGen1)<<" ";
// save state
std::cout << std::endl << "Saving...\n";
{
std::ofstream fout("seed.dat");
fout << randGen1;
fout.close();
std::ofstream fout2("distribution.dat");
fout2 << distribution1;
fout2.close();
}
// maybe advance randGen
for (int i=0;i<middleN;++i)
std::cout << distribution1(randGen1)<<" ";
// load saved state into randGen2
std::cout << std::endl << "Loading...\n";
std::mt19937 randGen2;
std::normal_distribution<double> distribution2;
{
std::ifstream fin("seed.dat");
fin >> randGen2;
fin.close();
std::ifstream fin2("distribution.dat");
fin2 >> distribution2;
fin2.close();
}
// are both randGen equal?
assert(randGen1 == randGen2);
assert(distribution1 == distribution2);
// print numbers from both generators
std::cout << "Generator1\tGenerator2\n";
std::cout << distribution1(randGen1) << "\t"
<< distribution2(randGen2) << "\n";
return 0;
}
Here is how one can save and restore seed forrandom numbers of double float precision. It should be similar for integers - use jrand48 instead of erand48.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
unsigned short savedseed[3];
unsigned short currentseed[3];
double x;
/*-- initialize ramdom seed to whatever --*/
currentseed[0]= 23;
currentseed[1]= 45;
currentseed[2]= 67;
printf("\n");
/*-- generate three random numbers --*/
x = erand48(currentseed); printf("%g\n", x);
x = erand48(currentseed); printf("%g\n", x);
x = erand48(currentseed); printf("%g\n", x);
printf("\n");
/*-- save seed --*/
memcpy(savedseed, currentseed, 3*sizeof(unsigned short));
/*-- generate next three random numbers --*/
x = erand48(currentseed); printf("%g\n", x);
x = erand48(currentseed); printf("%g\n", x);
x = erand48(currentseed); printf("%g\n", x);
printf("\n", x);
/*-- restore seed --*/
memcpy(currentseed, savedseed, 3*sizeof(unsigned short));
/*-- generate the same three random numbers again --*/
x = erand48(currentseed); printf("%g\n", x);
x = erand48(currentseed); printf("%g\n", x);
x = erand48(currentseed); printf("%g\n", x);
printf("\n");
}

How to print a number with precision given by the user in c++

How to print a number with precision given by the user. And how to print precision after decimal point up to 100 digits.
Suppose A,B,C be three numbers and take the value of three numbers from the user and I have to print the answer of A/B ( A divided by B ) to C floating points.
If A=22, B=7 , C=25, that means I have to print the result of 22/7 to 25 floating points.
3.1428571428571428571428571 , this is the answer of 22/7 , 25 digits after decimal point.
As many have pointed out, use std::fixed and std::setprecision
#include <iostream>
#include <iomanip>
int main()
{
float x = 22.0/7.0;
std::cout << std::fixed << std::setprecision(25) << x;
return 0;
}
But when you run this you will see the output is
3.1428570747375488281250000 when it should be
3.1428571428571428571428571. What gives?
Well, floats can only hold so much before having to give up and say, "Best I can do, mate." Rumour has it, this point is about 7 digits. Based on a quick count that looks about right.
So what say we try a double. They're twice the size!
#include <iostream>
#include <iomanip>
int main()
{
double x = 22.0/7.0;
std::cout << std::fixed << std::setprecision(25) << x;
return 0;
}
gives us
3.1428571428571427937015414. Still not
3.1428571428571428571428571, but closer. What have we got that's bigger than double? Just tried long double. No dice on my computer. Looks like we're going to have to go looking for a good high precision floating point library.
Bummer. Well, there is one in Boost. I someday expect to see boost::kitchen_sink.
To print a float with a precision "n" you should write :
printf("%.nf"); // like printf("%.3f")
there you are a c++ full code
#include <stdio.h>
#include <stdlib.h>
#include <strstream>
#include <iostream>
int main()
{
// print a float with precision '4'
printf("%.4f",10.125f);
// print a float with precision 'n'
char * buffer = new char[100];
int n;
std::strstream ss; // like cout but outputs to a string buffer
ss<<"%.";
std::cout<<"Enter precision : ";
std::cin>>n;
ss<<n<<"f";
printf(ss.str(),10,125); // ss.str() to get buffer content
delete[] buffer;
return 0;
}
but very simply you still can write
std::cout << std::setprecision(n) << float_var;
EDIT :
you can do you own division ! i mean you can simulate the devision of the processor and get whatever precision you want , till inifinity ! , there you are the amazing code that i wrote for my friend one time for fun :
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <chrono>
int main()
{
std::cout.sync_with_stdio(false); // accelerate output
register int x,y;
char opp;
std::cout<<"Enter x/y : ";
std::cin>>x>>opp>>y;
std::cout<<"deviding "<<x<<" by "<<y<<std::endl;
register int precision;
std::cout<<"Enter precision : ";
std::cin>>precision;
register int precision_counter = 0;
typedef std::chrono::high_resolution_clock Time;
typedef std::chrono::nanoseconds ms;
typedef std::chrono::duration<float> fsec;
auto t0 = Time::now();
std::cout <<"Result = ";
std::cout<<x/y;
// check if there will be a float point result to print a point
if(x<y || x%y != 0)
{
std::cout<<".";
x%=y; // remove what we printed
register int counter = 0;
// print digts that are after the float point
while (precision_counter<precision )
{
x*=10;
while (x>=y)
{
x-= y;
counter++;
}
std::cout<<counter;
counter = 0;
precision_counter++;
}
/*
optimized loop :
while (precision_counter<precision )
{
x*=10;
std::cout<<x/y;
x%=y;
precision_counter++;
}
**/
}
auto t1 = Time::now();
fsec fs = t1 - t0;
std::cout<<"\n";
ms d = std::chrono::duration_cast<ms>(fs);
std::cout << fs.count() << "s\n";
std::cout << d.count() << " nanosecond\n";
std::cout<<std::endl;
system("pause");
return 0;
}
#include <iostream>
#include <string>
#include <sstream>
std::string f(int A, int B, int C){//A: numerator, B:denominator, C:digits after decimal point
std::stringstream ss;
int count = 0;
if(A < 0){
A = -A;
++count;
}
if(B < 0){
B = -B;
++count;
}
if(count == 1)
ss << '-';
ss << A / B << '.';
A %= B;
A *= 10;
count = C;
while(count--){
ss << A / B;
A %= B;
A *= 10;
}
return ss.str();
}
int main(void){
std::cout << f(22, 7, 25) << std::endl;
return 0;
}

std::random_shuffle not being seeded

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
int main() {
std::vector<short> a(256);
for (short x = 0; x != 256; ++x) {
a[x] = x;
}
for (auto x : a) { std::cout << x << ' '; } std::cout << std::endl;
std::cout << std::endl;
std::srand(11);
std::random_shuffle(a.begin(), a.end());
for (auto x : a) { std::cout << x << ' '; } std::cout << std::endl;
std::cout << std::endl;
for (short x = 0; x != 256; ++x) {
a[x] = x;
}
for (auto x : a) { std::cout << x << ' '; } std::cout << std::endl;
std::cout << std::endl;
std::srand(11);
std::random_shuffle(a.begin(), a.end());
for (auto x : a) { std::cout << x << ' '; } std::cout << std::endl;
}
So, here is my code. What I expect is, obviously, the same shuffle both times. What I get is, while the shuffles are consistent between launches, they are different and seem to ignore srand! What am I doing wrong here?
Note that for std::random_shuffle what random number generator is used is implementation-defined, it's not guaranteed to use std::rand.
You could use std::shuffle instead and pass it a random number generator:
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(a.begin(), a.end(), g);
LIVE
First note that the version of std::random_shuffle you use has been deprecated.
Also note (from the previous reference link)
...the function std::rand is often used.
The keyword here being often and not always.
If you want to make sure the same sequence is always created, then you should use one of the alternatives, passing either a specific random-number function, or use the std::shuffle function passing a generator (from the C++11 "new" PRNG classes).