Generating a random number from date in a reproducive manner in PHP - php

I have been struggling with a problem for a while now.
Namely I need to generate a number in range 1 - 50 using a date. What I mean is if I run a some piece of code withing a specific date, it should produce me the same number for that day. If the date changes it should produce me a different number in defined range and preferably that does not show pattern with numbers generated in previous dates (constantly rising for example). I believe it can't be done with some mathematical linear function since the graph of that function would always produce me a pattern how the numbers are changing in time. Perhaps there is some function that is used in cryptography that would help?

seed your random generator via srand
If the input number should does not change for the day then your random returns will be predictable for that day.
You can use the number of days since 1970, or the day of the month as a seed value

Just use a hashing algorithm on your date convert it to a number and define an upper bound using modulo e.g.:
$yournumber = hexdec( sha1($date) ) % 50;

Related

Anti-forgery unique serial number generation

I am trying to generate a random serial number to put on holographic stickers in order to let customers check if the purchased product is authentic or not.
Preface:
Once you input that and query that code it will be nulled, so next time you do it again you receive a message that the product might be fake because the code is already used.
Considering that I should make this system for a factory that produces no more than 2/3 millions pieces a year, for me is a bit hard understand how to set up everything, at least the 1st time…
I thought about 20 digits code in 4 groups (no letters because must be very easy for the user read and input the code)
12345-67890-98765-43210
This is what I think is the easiest way to do everything:
function mycheckdigit()
{
...
return $myserial;
}
$mycustomcode="123";
$qty=20000;
$myfile = fopen("./thefile.txt","w") or die("Houston we got a problem here");
//using a txt file for a test, should be a DB instead...
for($i=0;$i<=$qty;$i++) {
$txt = date("y").$mycustomcode.str_pad(gettimeofday()['usec'],6,STR_PAD_LEFT).random_int(1000000,9999999). "\n";
//here the code to make check digits
mycheckdigit($txt);
fwrite($myfile,$myserial);
}
fclose($myfile);
The 1st group identifying something like year: 18 and 3 custom code
The 2nd group include microtime (gettimeofday()['usec'])
The 3rd completely random
last group including 3 random number and a check digit for group 1 and a check digit for group 2
in short:
Y= year
E= part of the EAN or custom code
M= Microtime generated number (gettimeofday()['usec'])
D= random_int() digits
C= Check Digit
YYEEE-MMMMM-MDDDD-DDDCC
In this way, I have a prefix that changes every year, I can recognize what brand is the product (so I could use one DB source only) and I still have enough random digits to be - maybe - quite unique if I consider that I will “pick-up” only a portion of the numbers from 1,000,000 and 9,999,999 and split it following using above sorting
Some questions for you:
Do you think I have enough combinations to not generate same code in one year considering 2 million codes? I would not use a lookup in the DB for the same code if it is not really necessary because could slow down batch generation (executed in batch during production process)
Could be better put some also unique identifier, like a day of the year (001-365) and make random_int() 3 digits shorter? Please Consider that I will generate codes monthly and not daily (but I think there is no big change in uniqueness)
Considering that backend in PHP I am thinking to use mt_rand() function, could be a good approach?
UPDATE: After the #apokryfos suggestion, I read more about UUID generation and similar I found a good compromise using random_int() instead.
Because I just need digits, so HEX hashes are not useful for my needs and making things more complicated
I would avoid using complex cryptographic things like RSA keys and so on…
I don’t need that level of security and complexity, I just need a way to generate a unique serial number, most unique as possible that is not easy to be guessed and nulled if you don’t scratch the sticker (so number creation should not be made A to Z, but randomly)
You can play with 11 random digits per year so that's 11 digit numbers 1 to 99999999999 (99.9 billion is a lot more than 2 million) so w.r.t. enough combinations I think you're covered.
However using mt_rand you're likely to get collisions. Here's a way to plan your way to 2 million random numbers before using the database:
<?php
$arr = [];
while (count($arr) < 1000000) {
$num = mt_rand(1, 99999999999);
$numStr = str_pad($num,11,0,STR_PAD_LEFT); //Force 11 digits
if (!isset($arr[$numStr])) {
$arr[$numStr] = true;
}
}
$keys= array_keys($arr);
The number of collisions is generally low (the first collision occurs at at about 300 000 - 500 000 numbers generated so it's pretty rare.
Each value in the array $keys is an 11 digit number which is random and unique.
This approach is relatively fast but be aware it will need quite a bit of memory (more than 128MB).
This being said, a more generally used method is to generate a universally unique identifier (UUID) which is a lot more likely to be unique and will therefore does not really need checking for uniqueness.

Generate random number that changes every month

I need to generate a random number within a given range. The number must be different for every new month of the year, and it should not be the same (although theoretically it is possible) as the number generated the year before for that given month.
I was thinking of using the php rand($min, $max) function in conjunction with date("w"), I am struggling with the part where I need to get different results for every year.
To illustrate what I mean, check this:
$numbers = range(1, 100); //our random array
echo $numbers[date("w")]; //date("w") returns number of week
There are 2 problems here: 1) it is not really random, 2) the number will be the same every year and 3) it does not reflect the whole size of the array as date("w") returns a number between 1 and 52.
Do you think this is accomplishable in pure PHP? Or do I need to make a cronjob for this?
Thank you for any help.
You could use every month the same number in the seed. Just to be clear the seed in the function srand ([ int $seed ] ).
You can do something like this:
srand(100);
echo rand();
and every time it returns the same number. So every month you can do something like this:
$min = 1;
$max = 100;
date_default_timezone_set('UTC');
$seed = date('Ym'); // it is the date as an integer
srand($seed);
echo rand($min, $max);
Pay attention to set the default time zone. If you don't, the result will depend to the server configuration.
-- UPDATE --
As #StevenSmith and #axiac stated you're not 100% sure the random generated number is unique.
In other words this means that wider the range (min-max) higher the probability you'll have every time a different number.
Use date('Ym') to generate an unique identifier for each (year, month) combination, use this value to initialize the pseudo-random generator's engine then generate one random number:
srand(date('Ym'));
$value = rand(1, 100);
Adjust the second line to match your needs. Now it generates an integer number between (and including) 1 and 100.
The value returned by date('Ym') is the same during a month. Every time you call srand() with a certain argument, the pseudo-random generator is reinitialized using that value and then it produces the same sequence of pseudo-random numbers when rand() is invoked (several times).
This ensures the generation of the same value for you during the entire month. During the next month and during the same month of the subsequent years the pseudo-random number generator is initialized with a different value and it generates a different sequence of pseudo-random numbers.
Depending on the values you use in your call to rand(), it might happen that the value returned by it this month is returned again in a different month. Use a large range for its $min and $max arguments to minimize this possibility.
You could generate a random number, select the closest multiple of 12, and then add the current month number.
For example, if the random number is 616, the closest multiple of 12 is 612. Add 0 (since it's currently January) giving you a final answer of 612. This will ensure that the number is unique for each month.

Does seeding mt_rand with a true random number make the result of mt_rand truely random as well?

If I have a truely random number and I use mt_srand to seed mt_rand with the truly random number before I use mt_rand, will this mean the result of mt_rand is now also truly random rather than pseudorandom?
In otherwords, would the below code produce a truly random integer between the given minimum and maximum value
function RandomInteger($min, $max)
{
$trueRandomNumber = GetTrueRandomNumber();
mt_srand($trueRandomNumber);
return mt_rand($min, $max);
}
Secondly, should the true random number used to seed mt_srand be of 32 integers?
Looking at your code, my guess is that you are getting some value from GetTrueRandomNumber() (code is missing) but then you want that number to be in a specific range of values. So you are taking that output and inputting it into mt_rand() because it has a method of generating a number in a specific range.
While not a direct answer to your question, a better solution is to first figure out the range of values you want (i.e. as if the input $min was 0 and $max was $max - $min). Then, figure out the maximum number of bits that are required to obtain a value in that range. Then, extract that number of bits from the output of GetTrueRandomNumber(). If the value is within the range, return the number + the original $min value. If the value isn't within the range, get more bits of data. The key is to throw away bits until you get some in the desired range.
If need example source code for this, try:
http://barebonescms.com/documentation/csprng/
You should be able to put something similar together. I'd be wary of using mt_rand() for anything like this but it might be okay in this very specific instance. It depends on what you plan on using it for. It also depends highly on how well distributed the first number from Mersenne Twister actually is. I don't think anyone's done any work on that. MT is intended to be seeded one time - who knows what the distribution pattern is for repeatedly seeding it. Also, if other code uses mt_rand(), you risk exposure of your function's state based on later values that might get generated by later mt_rand() calls.
No. Mersenne twisters are always pseudorandom. The seed only determines where in the sequence it starts.

Solr: calculating the difference between two given dates when one of the values is *

I'm working on something that when a range filter is active, the range facets will update accordingly. If no filter is given, I'm using a default range & gap. If a filter is set I do a second solr call to recalculate the facet range gap.
So for example at first I show range facets of a gap of 10YEARS. When you filter one of those, it shows 10 range facets of 1YEAR. If you filter again, it'll show 12 buckets with a 1 month range etc...
It kind of works, but I'm having trouble when the range filter includes a *. Solr knows how to correctly filter results when solrfield:[1960-01-01T00:00:00Z TO *] is given, but I don't know how I recalculate the date range facet gaps based on *. Right now I calculate the difference between the two dates as a unix timestamp and calculate the range gap based on that.
Also, is there a name for what I'm trying to do here? I'd call it 'variable range gaps', but I'm not sure if that's correct.
I'm not even sure if what I'm doing is the best approach. Any advice is welcome.
Edit: what my question comes down to is: I would like to calculate difference (as unix timestamp) between the highest and lowest date of a field in every solr query.
Edit 2: Every solr call I do takes about 300ms (on a relatively small index). That's quite a bit in case I do 2 extra calls with a date sort to find the highest and lowest date value in the query. Then performing the 4th solr call with the correct date gap (and from/to values), would get a bit slow.
You can probably turn it into familiar "limited date range" case. If it is impossible for your data to have datetime set in the future, your can replace * with NOW always receiving the same resultset (as no date can be later than current moment) and calculate gaps basing on current timestamp.

Generating 'word of the day' via PHP Random Number

I'm stuck with a small issue and don't want to generate my own algo for random number.
I've to Display 'word of the day' on website, it has to change only once per day and all data is stored in XML. At pageload I read xml file by simpleXml Parser in php and then generate a random number between 0, length of array and output a term + definition.
But i dont want it to change with every refresh, nor do I want to save it on server in database.
So how can I generate a random number between 0 to N, which would give same value for a span of 24 hours.
Just set the current date as Seed without hours, minutes and seconds.
srand(mktime(0, 0, 0));
$wordIndex = rand(0, $wordCount);
It will return the same number for one Day.
Option 1: No random numbers, just increase the index by one every day. It will look random enough since no one knows your file. If that is not good enough, randomize the input file (shuffle it once and safe it out again).
Option 2: Use today's date as the seed for the random number generator.
<?
srand(date("ymd"));
echo rand();
?>
If you dont want to store it, then use something, that is connected to daily cycle, for example, the date, or weekday.

Categories