Function generating Unique Random Values Array - php

As Mysql rand() is time consuming I am using alternate way using Mysql max() and PHP. I wrote this code for fetching random product_id's:
function RandomUniqueArray($min,$max,$limit){
$random_array = array();
if(isset($limit) && is_numeric($limit)){
for($i=0;$i<$limit;){
$rand_val = mt_rand($min, $max);
if(!in_array($rand_val, $random_array)){
$random_array[] = $rand_val;
$i++;
}
}
}
return $random_array;
}
This works fine as I want each time it gives different result set with different unique values but it takes 6.232 micro seconds.
Ohter I got by Google is:
$random_array = array_rand(array_fill($min,$max, true),$limit);
with takes only 0.101 microseconds but its result set is repeated means. Unique values array is fine but whole set is repeated. Why is it so???
I called both of these by one by one as
$random_array = RandomUniqueArray(1,64000,4);
And
$random_array = array_rand(array_fill(1,64000, true),4);
Thank you.

I made a script,that only takes ̣̣̣+- 4.5E-6.
Try it.
function randomValue($min,$max,$limit)
{
$array = Array();
$MAX = mt_rand($min,$max);
for($i = 0;$i < $limit;$i++)
{
$array[$i] = mt_rand($min,$MAX);
while( is_array($array[$i],$array) ) //To check if exist, if. Make new.
{
$array[$i] = mt_rand($min,$MAX);
}
}
return $array;
}

Related

How to limit duplicate values in an array

First of all, I apologize for my lack of English. I hope you do understand what I'm trying to explain here.
So basically I need to build a function that would limit the number of duplicate values inside an array.
The reason I need to do this is that I'm building a system that would divide numbers into groups and every group has to have the same amount of numbers.
EDIT: Random number represents the group number.
I've written a function do this but for some reason, it is not working properly.
function jagaTiimid($max, $liiget, $tArvLength, $tArv){
$tiimid = []; //Starting array
for($z=0;$z<$liiget;$z++){
$numbers = [];
$rn = randomNumber($tArvLength, $tArv, $numbers); //Generate a random number for a group, etc group 1, group 2, group 3
$mitu = countInArray($tiimid, $rn); //Check how many times that number has occured in array
if($mitu == $max){ //If it equals to maximum number of times then...
$rnUus = randomNumber($tArvLength, $tArv, $numbers); //generate a new random number
while($rnUus == $rn){
$numbers = [];
$rnUus = randomNumber($tArvLength, $tArv, $numbers);
} //loop until the new generated number doesn't equal to old rn.
$tiimid[] = $rnUus; //if it doesn't equal to $rn then push into array
}else{
$tiimid[] = $rn;
}
}
return $tiimid;
}
For some reason the number still occures more than it is suppose to.
Basically how it shouldn't end up is.
As you can see, one group(group 2) occurs more times than other group but it should be equal for both groups.
EDIT: CountInArray();
function countInArray($array, $what) {
$count = 0;
for ($i = 0; $i < count($array); $i++) {
if ($array[$i] === $what) {
$count++;
}
}
return $count;
}
When the first random pick hits a number that is already used $liiget times, the inner loop kicks in, but it does not check whether the newly generated random number already occurs $liiget times.
For efficiency I would keep track of the number of times a number has been used. Also, you could benefit from a safety net, in case there really is no number any more that would not exceed the maximum recurrence.
It is not necessary to have a nested loop. The code would look like this:
function jagaTiimid($max, $liiget, $tArvLength, $tArv){
$tiimid = []; //Starting array
$counts = []; // Helper for quick count
$tries = 0; // Counter to avoid infinite looping
while (count($tiimid) < $liiget && $tries++ < 100) {
$numbers = [];
$rn = randomNumber($tArvLength, $tArv, $numbers); //Generate a random number for a group, etc group 1, group 2, group 3
if (!isset($counts[$rn])) $counts[$rn] = 0; // initialise on first occurence
if ($counts[$rn] < $max) {
$tiimid[] = $rn; // add it to the result
$counts[$rn]++; // ... and adjust the count
$tries = 0; // reset the safety
}
}
return $tiimid;
}
replace while($rnUus == $rn) with while(countInArray($tiimid, $rnUus) >= $max)
– Ilya Bursov

Is there a faster way than array_diff in PHP

I have a set of numbers from MySQL within the range 1000 0000 (8 digits) to 9 999 999 999 (10 digits). It's supposed to be consecutive, but there are missing numbers. I need to know which numbers are missing.
The range is huge. At first I was going to use PHP to do this:
//MySqli Select Query
$results = $mysqli->query("SELECT `OCLC Number` FROM `MARC Records by Number`");
$n_array = array();
while($row = $results->fetch_assoc()) {
$n_array[] = $row["OCLC Number"];
}
d($n_array);
foreach($n_array as $k => $val) {
print $val . " ";
}
/* 8 digits */
$counter = 10000000;
$master_array = array();
/* 10 digits */
while ($counter <= 9999999999 ) {
$master_array[] = $counter;
$counter++;
d($master_array);
}
d($master_array);
$missing_numbers_ar = array_diff ($master_array, $n_array);
d($missing_numbers_ar);
d() is a custom function akin to var_dump().
However, I just realized it would take tons of time for this to be done. At the 15 minute mark, $master_array is being populated with only 4000 numbers.
How can I do this in a quicker way? MySQL-only or MySQL-and-PHP solutions both welcome. If the optimal solution depends on how many numbers are missing, please let me know how so. Tq.
Your d() probably is the cause of slowness, please remove it, and make small changes in your code
while($row = $results->fetch_assoc()) {
$n_array[$row["OCLC Number"]] = 1;
}
and
$missing_numbers_ar = [];
while ($counter++ <= 9999999999 ) {
if (empty($n_array[$counter])) {
$missing_numbers_ar[] = $counter;
}
}
If the following is still slow I would be surprised. I also just noticed it is similar to #Hieu Vo's answer.
// Make sure the data is returned in order by adding
// an `ORDER BY ...` clause.
$results = $mysqli->query("SELECT `OCLC Number`
FROM `MARC Records by Number`
ORDER BY `OCLC Number`");
$n_array = array();
while($row = $results->fetch_assoc()) {
// Add the "OCLC Number" as a key to the array.
$n_array[$row["OCLC Number"]] = $row["OCLC Number"];
}
// assume the first array key is in fact correct
$i = key($n_array);
// get the last key, also assume it is not missing.
end($n_array);
$max = key($n_array);
// reset the array (should not be needed)
reset($n_array);
do {
if (! $n_array[$i]) {
echo 'Missing key:['.$i.']<br />';
// flush the data to the page as you go.
flush();
}
} while(++$i <= $max);

Find the matched value of an array with a given value

I have an array of value series like:
$series = [100,300,500,800,1000,3000,5000,10000,15000,20000];
Another value getting from DB like:
$point = $data[‘earned_point’];
I need the highest match from the series. such as I got a value from db (1500) the highest match of the value is 1000 in the series, so I need to get the $series[4] and make it
$reward = $series[4] * 0.1;
I'll run it in a loop to do it for all the values got from DB.
I'm posting alternate code as the accepted answer while is correct can be very inefficient if you are working with a large array.
<?php
function computeReward($series, $point, $percent = 0.1){
arsort($series); // sort the series in reverse so we can pass any array and the highest number will be the first because we are looking for a number lower or equal to our point
foreach($series as $min_point){
if($min_point <= $point){
return $min_point * $percent; // return the min_point * the percentage, this stops the loop there for being more efficient
}
}
}
$series = [100,300,500,800,1000,3000,5000,10000,15000,20000];
$point = $data['earned_point'];
$reward = computeReward($series, $point);
?>
Do you mean you want to get which highest $series item is equal or less than $point ?
<?php
$series = [100,300,500,800,1000,3000,5000,10000,15000,20000];
$point = $data['earned_point'];
foreach ($series as $min_point) {
if ($point >= $min_point) {
$matched_min_point = $min_point;
}
}
$reward = $matched_min_point*0.1;
Let me know if this works for you

How to configure php's "array_rand" to not give 2 same results on after another?

I use php array_rand to select 1 random record from array, Example:
$style_class = array("st_style1","st_style2","st_style3","st_style4");
$random_class = array_rand($style_class, 1);
$div_class = $style_class[$random_class];
The issue is that sometimes it gives a same record several times, and as I use only 4 records it happens quiet often (using "array_rand" is not neccesary) .
Example:
st_style1,
st_style2,
st_style2,
st_style2,
st_style4,
st_style2 ...
Is there a way to solve this issue, so two same record would not get displayed two times in a row.
For example
st_style2, st_style4, st_style2, st_style1, st_style3, st_style2, st_style1 ...
The simpliest solution is to keep track of the latest one and keep calling random until you get something different. Something like:
$style_class = array("st_style1","st_style2","st_style3","st_style4");
$styles = array()
$lastStyle = -1
for($i = 0; $i < 5; $i++) {
while(1==1) {
$newStyle = array_rand($style_class, 1);
if ($lastStyle != $newStyle) {
$lastStyle = $newStyle;
break;
}
}
$div_class = $style_class[$lastStyle]
$styles[] = $div_class
}
Then use the $styles[] array in order. It should not have any duplicates
Basically same as James J. Regan IV's answer, but using a do-while loop:
Set up the array like this:
$style_class = array("st_style1","st_style2","st_style3","st_style4");
$prev_class = -1;
And then, to obtain a random class:
do {
$random_class = array_rand($style_class, 1);
} while ($random_class == $prev_class);
$div_class = $style_class[$prev_class = $random_class];
Edit: Alternative solution, with no loops:
$style_class = array("st_style1","st_style2","st_style3","st_style4");
$random_class = array_rand($style_class);
To obtain a new random class:
$random_class += rand(1, count($style_class)-1);
$div_class = $style_class[$random_class % count($style_class)];
This works as long as the array keys are consecutive integers starting from zero (as is the case if you define it with array() and don't explicitly specify any keys).
Save the last style in a var, then make a loop till the new style differs from last style. And then you will have a different from the last on every execution.

K-means clustering: What's wrong? (PHP)

I was looking for a way to calculate dynamic market values in a soccer manager game. I asked this question here and got a very good answer from Alceu Costa.
I tried to code this algorithm (90 elements, 5 clustes) but it doesn't work correctly:
In the first iteration, a high percentage of the elements changes its cluster.
From the second iteration, all elements change their cluster.
Since the algorithm normally works until convergence (no element changes its cluster), it doesn't finish in my case.
So I set the end to the 15th iteration manually. You can see that it runs infinitely.
You can see the output of my algorithm here. What's wrong with it? Can you tell me why it doesn't work correctly?
I hope you can help me. Thank you very much in advance!
Here's the code:
<?php
include 'zzserver.php';
function distance($player1, $player2) {
global $strengthMax, $maxStrengthMax, $motivationMax, $ageMax;
// $playerX = array(strength, maxStrength, motivation, age, id);
$distance = 0;
$distance += abs($player1['strength']-$player2['strength'])/$strengthMax;
$distance += abs($player1['maxStrength']-$player2['maxStrength'])/$maxStrengthMax;
$distance += abs($player1['motivation']-$player2['motivation'])/$motivationMax;
$distance += abs($player1['age']-$player2['age'])/$ageMax;
return $distance;
}
function calculateCentroids() {
global $cluster;
$clusterCentroids = array();
foreach ($cluster as $key=>$value) {
$strenthValues = array();
$maxStrenthValues = array();
$motivationValues = array();
$ageValues = array();
foreach ($value as $clusterEntries) {
$strenthValues[] = $clusterEntries['strength'];
$maxStrenthValues[] = $clusterEntries['maxStrength'];
$motivationValues[] = $clusterEntries['motivation'];
$ageValues[] = $clusterEntries['age'];
}
if (count($strenthValues) == 0) { $strenthValues[] = 0; }
if (count($maxStrenthValues) == 0) { $maxStrenthValues[] = 0; }
if (count($motivationValues) == 0) { $motivationValues[] = 0; }
if (count($ageValues) == 0) { $ageValues[] = 0; }
$clusterCentroids[$key] = array('strength'=>array_sum($strenthValues)/count($strenthValues), 'maxStrength'=>array_sum($maxStrenthValues)/count($maxStrenthValues), 'motivation'=>array_sum($motivationValues)/count($motivationValues), 'age'=>array_sum($ageValues)/count($ageValues));
}
return $clusterCentroids;
}
function assignPlayersToNearestCluster() {
global $cluster, $clusterCentroids;
$playersWhoChangedClusters = 0;
// BUILD NEW CLUSTER ARRAY WHICH ALL PLAYERS GO IN THEN START
$alte_cluster = array_keys($cluster);
$neuesClusterArray = array();
foreach ($alte_cluster as $alte_cluster_entry) {
$neuesClusterArray[$alte_cluster_entry] = array();
}
// BUILD NEW CLUSTER ARRAY WHICH ALL PLAYERS GO IN THEN END
foreach ($cluster as $oldCluster=>$clusterValues) {
// FOR EVERY SINGLE PLAYER START
foreach ($clusterValues as $player) {
// MEASURE DISTANCE TO ALL CENTROIDS START
$abstaende = array();
foreach ($clusterCentroids as $CentroidId=>$centroidValues) {
$distancePlayerCluster = distance($player, $centroidValues);
$abstaende[$CentroidId] = $distancePlayerCluster;
}
arsort($abstaende);
if ($neuesCluster = each($abstaende)) {
$neuesClusterArray[$neuesCluster['key']][] = $player; // add to new array
// player $player['id'] goes to cluster $neuesCluster['key'] since it is the nearest one
if ($neuesCluster['key'] != $oldCluster) {
$playersWhoChangedClusters++;
}
}
// MEASURE DISTANCE TO ALL CENTROIDS END
}
// FOR EVERY SINGLE PLAYER END
}
$cluster = $neuesClusterArray;
return $playersWhoChangedClusters;
}
// CREATE k CLUSTERS START
$k = 5; // Anzahl Cluster
$cluster = array();
for ($i = 0; $i < $k; $i++) {
$cluster[$i] = array();
}
// CREATE k CLUSTERS END
// PUT PLAYERS IN RANDOM CLUSTERS START
$sql1 = "SELECT ids, staerke, talent, trainingseifer, wiealt FROM ".$prefix."spieler LIMIT 0, 90";
$sql2 = mysql_abfrage($sql1);
$anzahlSpieler = mysql_num_rows($sql2);
$anzahlSpielerProCluster = $anzahlSpieler/$k;
$strengthMax = 0;
$maxStrengthMax = 0;
$motivationMax = 0;
$ageMax = 0;
$counter = 0; // for $anzahlSpielerProCluster so that all clusters get the same number of players
while ($sql3 = mysql_fetch_assoc($sql2)) {
$assignedCluster = floor($counter/$anzahlSpielerProCluster);
$cluster[$assignedCluster][] = array('strength'=>$sql3['staerke'], 'maxStrength'=>$sql3['talent'], 'motivation'=>$sql3['trainingseifer'], 'age'=>$sql3['wiealt'], 'id'=>$sql3['ids']);
if ($sql3['staerke'] > $strengthMax) { $strengthMax = $sql3['staerke']; }
if ($sql3['talent'] > $maxStrengthMax) { $maxStrengthMax = $sql3['talent']; }
if ($sql3['trainingseifer'] > $motivationMax) { $motivationMax = $sql3['trainingseifer']; }
if ($sql3['wiealt'] > $ageMax) { $ageMax = $sql3['wiealt']; }
$counter++;
}
// PUT PLAYERS IN RANDOM CLUSTERS END
$m = 1;
while ($m < 16) {
$clusterCentroids = calculateCentroids(); // calculate new centroids of the clusters
$playersWhoChangedClusters = assignPlayersToNearestCluster(); // assign each player to the nearest cluster
if ($playersWhoChangedClusters == 0) { $m = 1001; }
echo '<li>Iteration '.$m.': '.$playersWhoChangedClusters.' players have changed place</li>';
$m++;
}
print_r($cluster);
?>
It's so embarassing :D I think the whole problem is caused by only one letter:
In assignPlayersToNearestCluster() you can find arsort($abstaende);. After that, the function each() takes the first value. But it's arsort so the first value must be the highest. So it picks the cluster which has the highest distance value.
So it should be asort, of course. :) To prove that, I've tested it with asort - and I get convergence after 7 iterations. :)
Do you think that was the mistake? If it was, then my problem is solved. In that case: Sorry for annoying you with that stupid question. ;)
EDIT: disregard, I still get the same result as you, everyone winds up in cluster 4. I shall reconsider my code and try again.
I think I've realised what the problem is, k-means clustering is designed to break up differences in a set, however, because of the way you calculate averages etc. we are getting a situation where there are no large gaps in the ranges.
Might I suggest a change and only concentrate on a single value(strength appears to make most sense to me) to determine the clusters, or abandon this sorting method altogether, and adopt something different(not what you want to hear I know)?
I found a rather nice site with an example k-mean sort using integers, I'm going to try and edit that, I will get back with the results some time tomorrow.
http://code.blip.pt/2009/04/06/k-means-clustering-in-php/ <-- link I mentioned and forgot about.

Categories