PHP - Generate random numbers with no duplicates - php

I'm building an advent calendar in PHP 7 and want to show dates 1 - 24 in a random order.
I've got a jquery plugin which can randomise my div elements, but it's not very good, and I want to know how to do it in PHP.
My code to output the dates looks (in simplified terms) like this:
for ($d = 1; $d <= 24; $d++) {
echo $d;
}
My plan was to instead use rand(1, 24) then store any numbers that had been generated in an array, e.g.
$date = rand(1, 24);
$used_dates[] = $date;
Then check $used_dates when picking a new date, e.g.
$unique_date = false;
while (!$unique_date) {
$date = rand(1, 24);
if (!in_array($date, $used_dates)) {
$used_dates[] = $date;
$unique_date = true;
}
}
This seems inefficient though. Are there better ways?

In your case, you need create range array of numbers, and then simply shuffle them
$days = range(1,24);
shuffle($days);
Thats all

You can use shuffle() method.
Shuffle() take an array and order rows randomly.
$array = array("a", "b", "c");
shuffle($array);
// OUTPUT is random

$numbers = range(1, 24);
shuffle($numbers);

Related

How to subtract time from time array using php?

Hello seniors I have a question related to some PHP script.
I have an array containing time => ['12:10', '4:16', '2:5'] and have one html form containing input field of type 'number'.
I want when I enter some value in my form input field for example I enter 7, so after submitting the form in back-end the number 7 which i enter in input field is subtracted from the array which I mentioned above and I will get the result array like:
['5:10', '4:16', '2:5']
I have tried something like that but not able to implement my logic
$val = array(1, 0, 2, 1, 1);
$subtract = 3.5;
foreach ($val as $key => $item) {
if ($subtract >= $item) {
$subtract -= $item;
$val[$key] = 0;
} else {
$val[$key] -= $subtract;
$subtract = 0;
}
}
Any kind of help is highly appreciated
You can use Carbon library for date/time manipulation:
<?php
use Carbon\Carbon;
$times = ['17:46', '03:05', '21:56'];
$timeshift = 3;
$new_times = array_map(
fn($t) => Carbon::createFromFormat('H:i', $t)
->subHours($timeshift)
->format('H:i'),
$times
);
Test Carbon library online
No need for library, just convert your first array to seconds: 1 hour = 3600 ; 1 minute = 60 ; 12:10 is 12 x 3600 + 10 x 60, then you do the same thing to your $_POST value, then use gmdate() to retrieve the original format of your array
$myTimes=array('12:10', '4:16', '2:5');
//do the math
$splittedTime = explode(":", $myTimes[0]); //in your case
$timeInSeconds = $splittedTime[0] * 3600 + $splittedTime[1] * 60 ;
//do the same thing to your your $_POST value if needed or simply
$totalReduceby = 7 * 3600;
// get new total of seconds
$newTime= $timeInSeconds - $totalReduceby;
$result = ltrim(gmdate("H:i", $newTime),0); //ltrim to remove the leading 0
$myTimes=array($result, '4:16', '2:5');
//print_r($myTimes);
time => ['12:10', '4:16', '2:5']
[...]
the number 7 which i enter in input field is subtracted from the array
I will get the result array like: ['5:10', '4:16', '2:5']
Your example is a little ambiguous. Do you only want to subtract the field value from the first element of the array, always? Or only from those elements which are greater than the submitted value?
It's pretty straightforward to subtract minutes from a mm:ss time string; simplest is probably to generalize so that the amount to subtract is also allowed to be mm:ss instead of always being a whole number of minutes. I would just explode both of them, turn them into total seconds (minutes*60+seconds), subtract those, and then turn back into mm:ss. Both conversions might be worth their own functions:
function mmssToSeconds($timeStr) {
if (str_contains($timeStr, ':')) {
list($min, $sec) = explode(':', $timeStr);
} else {
list($min, $sec) = array($timeStr, 0);
}
if ($min < 0) {
return 60*$min - $sec;
} else {
return 60*$min + $sec;
}
}
function secondsToMmss($seconds) {
$abs = abs($seconds);
$sgn = $seconds / $abs;
$min = floor($abs / 60);
$sec = $abs % 60;
return ($sgn < 0 ? '-' : '').sprintf('%d:%02d', $min, $sec);
}
And then the subtraction is easy:
function subtractMinutes($from, $delta) {
return secondsToMmss(mmssToSeconds($from) - mmssToSeconds($delta));
}
If you want to subtract from each element that is big enough, you could use a loop like this:
foreach ($ary['time'] as $i => $t) {
if ((int)$t > $subtract) {
$ary['time'][$i] = subtractMinutes($t, $subtract);
}
}
The comparison works because the cast from string to int ignores everything after the first non-digit, so '12:10' just becomes 12, which is > 7.

Array range within a range of only chosen numbers PHP

I am a a beginner and i made this:
<?php
$numbers = range(1, 100);
shuffle($numbers);
foreach ($numbers as $number) {
echo $number . " ";
}
?>
I thought i could make it work with the out commented code but i don't know what to do anymore after searching so much online, i thought i could copy it(for) and make it work but it copied the same shuffle range instead of separate shuffle numbers.
I want the range from 1 until 100 with numbers only between 1 and 6. That's it.
Thanks for your time.
Simple loop adding 100 random values to array:
$random_ints = [];
while (count($random_ints) < 100) {
$random_ints[] = random_int(1, 6);
// or
// $random_ints[] = mt_rand(1, 6);
}
echo implode($random_ints);

doing maths with php array based on excel function

I am new to PHP and stackoverflow and try to figure things out for myself before asking but I am having a little trouble doing some maths on an array I have pulled from a database with PHP.
So far I have an array of numbers called $array['sn']
I have created a function in excel that does the maths and works well in excel but I cant figure out a way to do it in PHP.
the excel function is =QUOTIENT(E32,65536)&QUOTIENT(E32-F34*65536,256)&(G33-G35*256)
E32 being the value I start with i.e $sn
F34 being the answer to the first quotient
G35 being the answer to the second quotient
G33 being E32-F34*65536
I want to take a number e.g. 3675177 divide it by 65536 but without the remainder which is 56, then multiply 56 by 65536 which equals 3670016, then find the difference between 3670016 and 3675177 which is 5161. Then divide 5161 by 256 with no remainder which is 20 then multiply 20 by 256 and subtract 5161 which is 41.
The end result from 3675177 should be 562041. I want to do this calculation on every number in the $array['sn'], any help would be appreciated.
The calculation and formatting of the output would be like this:
$n = 3675177;
$const = 65536;
$const2 = 256;
$a = intval($n / $const); // intval returns only the integer part of a number
$x = $n % $const; // $n % $const means "the remainder of $n / $const"
$b = intval($x / $const2);
$c = $x % $const2;
// Two options to handle values of $c < 10:
// if ($c < 10) $c = "0$c";
// $c = str_pad($c, 2, "0", STR_PAD_LEFT);
echo "$a$b$c";
I would recommend using array_map to apply the calculation to your array of values.
There are php arithmetic operations you can use.
I would do something like this:
$initialNumber = //the initial number, wherever you get it from
$entireDivision = ceil($initialNumber/65536)-1;
$remainder = $initialNumber%65536;
$remainderMultiplied = $remainder * 56;
$difference = $initialNumber - $remainderMultiplied;
$differenceDivided = ceil($difference/256)-1;
$differenceMultipliedAndSubstracted = ($differenceDivided * 256) - $difference;
Maybe I used too many variables, this is to be a bit more easy to understand for you. Maybe I did some operation wrong, check it out too. But this is the idea of mathematic operations in php. Maybe you should put this inside a php function with parameters, so your code gets cleaner if you use multiple times.
EDIT: You should put this code inside a function, then run a foreach loop in your array running this function taking as parameter the value of the array position.
$results = array();
foreach ($array['sn'] as $key => $a) {
$b = intval($a / 65536);
$c = ($a - $b * 65536);
$d = intval($c / 256);
$e = $c - $d * 256;
$results[$key] = $b . $d . $e;
}
var_dump($results);

avoid duplicates amongst creating random numbers

if I want to create some variables that contain random numbers that differ from each other, right now this is the method i'm using(in this case i'm using PHP to set example):
while($rand1==$rand2 or $rand2==$rand3 or $rand3==$rand4 or $rand4==$rand5 or
rand1==$rand3 or $rand2==$rand4 or $rand3==$rand5 or $rand5==$rand4 or
$rand1==$rand4 or $rand2==$rand5 or $rand4==$rand3 or $rand5==$rand3 or
$rand1==$rand5 or $rand3==$rand2 or $rand4==$rand1 or $rand5==$rand2 or
$rand2==$rand1 or $rand3==$rand1 or $rand4==$rand2 or $rand5==$rand1):
$rand1=rand(1,1000);
$rand2=rand(1,1000);
$rand3=rand(1,1000);
$rand4=rand(1,1000);
$rand5=rand(1,1000);
endwhile;
so, if I want to create 100 variables all with differing numbers,I wouldn't want to use this method for obvious reason. Any better alternatives?
put them in an array and search the array.
$arr = array();
$arr[] = rand(1,1000);
You can just loop through as many as you like
This gives you 5 unique randoms in the given range:
$a = range(1, 1000);
shuffle($a);
$randoms = array_slice($a, 0, 5);
(I assume performance is not critical).
In a more efficient way,
$randoms = array();
while(count($randoms) < 5)
$randoms[rand(1, 1000)] = 1;
$randoms = array_keys($randoms);
From your code I assume you need numbers from 1 to 1000 and you don't want duplicates. Here's a way not to loop through your random numbers every time since it's time consuming if you need 100 random numbers:
$pool = range(1, 1000);
$numbers = array();
for ($i = 0; $i < 5; $i++) {
$random = rand(0, count($pool) - 1);
$numbers[$i] = $pool[$random];
$pool[$random] = array_pop($pool);
}
Just change 5 to whatever you need or use a variable.
This works if you have a pool of numbers to extract from, that's why we pop the last element and we replace the number we extracted already to avoid duplicates. We have to count the elements in the pool each time because it shrinks by one element on each iteration. You could probably save the initial value and decrement it based on $i if your pool is not a native array so you don't waste time counting and you just do basich math.

PHP Generate x amount of random odd numbers within a range

I need to generate x amount of random odd numbers, within a given range.
I know this can be achieved with simple looping, but I'm unsure which approach would be the best, and is there a better mathematical way of solving this.
EDIT: Also I cannot have the same number more than once.
Generate x integer values over half the range, and for each value double it and add 1.
ANSWERING REVISED QUESTION: 1) Generate a list of candidates in range, shuffle them, and then take the first x. Or 2) generate values as per my original recommendation, and reject and retry if the generated value is in the list of already generated values.
The first will work better if x is a substantial fraction of the range, the latter if x is small relative to the range.
ADDENDUM: Should have thought of this approach earlier, it's based on conditional probability. I don't know php (I came at this from the "random" tag), so I'll express it as pseudo-code:
generate(x, upper_limit)
loop with index i from upper_limit downto 1 by 2
p_value = x / floor((i + 1) / 2)
if rand <= p_value
include i in selected set
decrement x
return/exit if x <= 0
end if
end loop
end generate
x is the desired number of values to generate, upper_limit is the largest odd number in the range, and rand generates a uniformly distributed random number between zero and one. Basically, it steps through the candidate set of odd numbers and accepts or rejects each one based how many values you still need and how many candidates still remain.
I've tested this and it really works. It requires less intermediate storage than shuffling and fewer iterations than the original acceptance/rejection.
Generate a list of elements in the range, remove the element you want in your random series. Repeat x times.
Or you can generate an array with the odd numbers in the range, then do a shuffle
Generation is easy:
$range_array = array();
for( $i = 0; $i < $max_value; $i++){
$range_array[] .= $i*2 + 1;
}
Shuffle
shuffle( $range_array );
splice out the x first elements.
$result = array_slice( $range_array, 0, $x );
This is a complete solution.
function mt_rands($min_rand, $max_rand, $num_rand){
if(!is_integer($min_rand) or !is_integer($max_rand)){
return false;
}
if($min_rand >= $max_rand){
return false;
}
if(!is_integer($num_rand) or ($num_rand < 1)){
return false;
}
if($num_rand <= ($max_rand - $min_rand)){
return false;
}
$rands = array();
while(count($rands) < $num_rand){
$loops = 0;
do{
++$loops; // loop limiter, use it if you want to
$rand = mt_rand($min_rand, $max_rand);
}while(in_array($rand, $rands, true));
$rands[] = $rand;
}
return $rands;
}
// let's see how it went
var_export($rands = mt_rands(0, 50, 5));
Code is not tested. Just wrote it. Can be improved a bit but it's up to you.
This code generates 5 odd unique numbers in the interval [1, 20]. Change $min, $max and $n = 5 according to your needs.
<?php
function odd_filter($x)
{
if (($x % 2) == 1)
{
return true;
}
return false;
}
// seed with microseconds
function make_seed()
{
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
srand(make_seed());
$min = 1;
$max = 20;
//number of random numbers
$n = 5;
if (($max - $min + 1)/2 < $n)
{
print "iterval [$min, $max] is too short to generate $n odd numbers!\n";
exit(1);
}
$result = array();
for ($i = 0; $i < $n; ++$i)
{
$x = rand($min, $max);
//not exists in the hash and is odd
if(!isset($result{$x}) && odd_filter($x))
{
$result[$x] = 1;
}
else//new iteration needed
{
--$i;
}
}
$result = array_keys($result);
var_dump($result);

Categories