Save and restart random chain (drand48) from breakpoint in C

I am trying to write a program that gives the same result if it runs completely or if it is stopped and restarted from some breakpoint. To do this, I need to be able to repeat exactly the same sequence of random numbers in any scenario. So here is the code snippet where I tried to do it, but of course I was unsuccessful. Could you help me fix this code?

int main(){
 int i;
 long int seed;

 // Initial seed
 srand48(3);

 // Print 5 random numbers
 for(i=0;i<5;i++)  printf("%d %f\n",i,drand48());

 // CHECKPOINT: HOW TO PROPERLY SET seed?
 seed=mrand48(); // <--- FIXME

 // 5 numbers more
 for(i=5;i<10;i++) printf("%d %f\n",i,drand48());

 // Restart from the CHECKPOINT.
 srand48(seed);

 // Last 5 numbers again
 for(i=5;i<10;i++) printf("%d %f\n",i,drand48());

}

      

+3


source to share


1 answer


If you need to resume a sequence of random numbers, you cannot afford to drand48()

hide the initial values โ€‹โ€‹from you, so you need to use various functions from the package. Specifically, you should call:

double erand48(unsigned short xsubi[3]);

      

instead:

double drand48(void);

      

and you will store an array of 3 unsigned short

values โ€‹โ€‹around and at each breakpoint you will write their values โ€‹โ€‹as part of the state. If you need to resume when you stopped, you restore the values โ€‹โ€‹from the saved state to your array, and then continue on your fun way.

It is also how you write library code that does not interfere with other code using random number generators and does not interfere with other code using random number generators.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    unsigned short seed[3] = { 0, 0, 3 };

    // Print 5 random numbers
    for (int i = 0; i < 5; i++)
        printf("%d %f\n", i, erand48(seed));

    // CHECKPOINT
    unsigned short saved[3];
    memmove(saved, seed, sizeof(seed));

    // 5 numbers more
    for (int i = 5; i < 10; i++)
        printf("%d %f\n", i, erand48(seed));

    // Restart from the CHECKPOINT.
    memmove(seed, saved, sizeof(seed));

    // Last 5 numbers again
    for (int i = 5; i < 10; i++)
        printf("%d %f\n", i, erand48(seed));

    return 0;
}

      

Execution example:



0 0.700302
1 0.122979
2 0.346792
3 0.290702
4 0.617395
5 0.059760
6 0.783933
7 0.352009
8 0.734377
9 0.124767
5 0.059760
6 0.783933
7 0.352009
8 0.734377
9 0.124767

      

It is clear how you set the initial array first is completely up to you. You can easily allow the user to provide an initial value and report the seed you are using so they can do that. For example, you can use some items from the PID or time of day, and the subnetting component as the default seed. Or you can access a random number device, for example /dev/urandom

, and get 6 bytes of a random value from the one to be used as a sample.


How can I allow the user to specify an initial value using only long int

? In this approach, it seems like the user needs to define 3 numbers, but I would like to only specify 1 number (like a safe stroke) in the input file.

You can take one number and split it in any way you want. I have a program that takes an option -s

to print a random seed, -s

to set the seed from long

, and sometimes splits the values long

by 3 unsigned short

when using a random Gaussian distribution generator.I'm basically working on 64-bit systems, so I just split long

the 16- bit component; the code also compiles easily under 32-bit systems, but leaves the third number in the seed as 0. Like this:

        case 'q':
            qflag = true;
            break;
        case 'r':
            check_range(optarg, &min, &max);
            perturber = ptb_uniform;
            break;
        case 's':
            sflag = true;
            break;
        case 't':
            delim = optarg;
            break;
        case 'S':
            seed = strtol(optarg, 0, 0);
            break;
        case 'V':
            err_version("PERTURB", &"@(#)$Revision: 1.6 $ ($Date: 2015/08/06 05:05:21 $)"[4]);
            /*NOTREACHED*/
        default:
            err_usage(usestr);
            /*NOTREACHED*/
        }
    }

    if (sflag)
        printf("Seed: %ld\n", seed);

    if (gflag)
    {
        unsigned short g_seed[3] = { 0, 0, 0 };
        g_seed[0] = (unsigned short)(seed & 0xFFFF);
        g_seed[2] = (unsigned short)((seed >> 16) & 0xFFFF);
        if (sizeof(seed) > 4)
        {
            /* Avoid 32-bit right shift on 32-bit platform */
            g_seed[1] = (unsigned short)(((seed >> 31) >> 1) & 0xFFFF);
        }
        gaussian_init(&g_control, g_seed);
    }
    else
        srand48(seed);

    filter_anon(argc, argv, optind, perturb);
    return 0;
}

      

For my purposes, it's ok (not ideal, but OK) to have even more limited seeding values โ€‹โ€‹for 32-bit ones. Yes, I could use unsigned long long

and strtoull()

etc. Instead, to get 64-bit numbers even on a 32-bit platform (although I would have to convert that to long

to satisfy srand48()

). the alternative I was considering is to accept the argument -S xxxx:yyyy:zzzz

with three separate seed components to be set separately. Then I will need to change the seed print code as well as the parsing code. I am using a separate program randseed

to read numbers from /dev/urandom

and format the result so that it can be passed to programs that need a random seed:

$ randseed -b 8
0xF45820D2895B88CE
$

      

+3


source







All Articles