How to make for loop produce five unique values - php

I created this code so that it will give me five value based on probability. Then insert into an array. It works but I want it to give me 5 unique value. If I used array_unique() it will make the output unique so the end result will be 3 or 2 or sometimes 4 value because it removes duplicates. but I want them to be 5 unique values all the time. How can I do This.
This is my code
<?php
$random= [];
function getRandomWeightedElement(array $weightedValues)
{
$array = array();
foreach ($weightedValues as $key => $weight) {
$array = array_merge(array_fill(0, $weight, $key), $array);
}
return $array[array_rand($array)];
}
global $res;
for($i=0; $i<5 ; $i++)
{
$res=getRandomWeightedElement(array('A'=>0.05, 'B'=>0.05,'C'=>0.10,'D'=>5,'E'=>5,'F'=>5,'G'=>5,'H'=>13,'I'=>16,'J'=>16,'K'=>16.8,'L'=>18));
global $stopAt;
if ($res=='A') {
$stopAt = 20;
} else if ($res=='B') {
$stopAt = 50;
} else if ($res=='C') {
$stopAt = 70;
} else if ($res=='D') {
$stopAt = 100;
} else if ($res=='E') {
$stopAt = 135;
} else if ($res=='F') {
$stopAt = 165;
continue;
} else if ($res=='G') {
$stopAt = 190;
} else if ($res=='H') {
$stopAt = 230;
} else if ($res=='I') {
$stopAt = 265;
} else if ($res=='J') {
$stopAt = 292;
} else if ($res=='K') {
$stopAt = 320;
} else if ($res=='L') {
$stopAt = 350;
} else{
echo "error";
}
$random[]= $stopAt;
}
print_r(array_unique($random));
?>

You will have to check if the value already exists in the result array, you can use in_array() for that. But that means you have to keep looping until you have 5 unique results which means changing the loop.
$random= [];
function getRandomWeightedElement(array $weightedValues)
{
$array = array();
foreach ($weightedValues as $key => $weight) {
$array = array_merge(array_fill(0, $weight, $key), $array);
}
return $array[array_rand($array)];
}
//for($i=0; $i<5 ; $i++) {
$params = array('A'=>0.05, 'B'=>0.05,'C'=>0.10,'D'=>5,
'E'=>5,'F'=>5,'G'=>5,'H'=>13,'I'=>16,
'J'=>16,'K'=>16.8,'L'=>18);
$notGot5 = true;
while ( $notGot5 ) {
$res=getRandomWeightedElement($params);
if ($res=='A') {
$stopAt = 20;
} else if ($res=='B') {
$stopAt = 50;
} else if ($res=='C') {
$stopAt = 70;
} else if ($res=='D') {
$stopAt = 100;
} else if ($res=='E') {
$stopAt = 135;
} else if ($res=='F') {
$stopAt = 165;
continue;
} else if ($res=='G') {
$stopAt = 190;
} else if ($res=='H') {
$stopAt = 230;
} else if ($res=='I') {
$stopAt = 265;
} else if ($res=='J') {
$stopAt = 292;
} else if ($res=='K') {
$stopAt = 320;
} else if ($res=='L') {
$stopAt = 350;
} else{
echo "error";
}
if ( in_array($stopAt, $random) ) {
// keep looking
continue;
} else {
$random[]= $stopAt;
}
// loop terminates when we have 5 in the array
$notGot5 = count($randon) != 5;
}
print_r($random);

Instead of doing several if statements to choose random values, you can choose the values directly from the array itself.
The below code should give you what you want:
$valuesToChooseFrom = [
'A' => 0.1,
'B' => 0.3,
'C' => 3.1,
'D' => 20,
'E' => 10,
'F' => 4,
'G' => 222
];
$result = [];
for ($i = 0; $i < 5; $i++) {
$key = array_rand($valuesToChooseFrom);
$result[$key] = $valuesToChooseFrom[$key];
unset($valuesToChooseFrom[$key]);
}
print_r($result);
And gives:
Array
(
[F] => 4
[G] => 222
[D] => 20
[B] => 0.3
[E] => 10
)
It works by selecting a random element from the valuesToChooseFrom array, using the array_rand() function. The array_rand function selects a random element from the array you pass it, and returns that element (the key in this case, since we are working with a 2 dimensional array). You then add an item to the result array, with the key being the key returned from the array_rand call, and the value being the value in the valuesToChooseFrom array, using the key to index the valuesToChooseFrom array.
To prevent PHP from choosing the same value more than once, you have to remove the value valuesToChooseFrom array. This is what the unset($valuesToChooseFrom[$key]); line does.
array_rand() also accepts a second argument, which is the number of random elements to select from the array. If you pass in 5 as the second argument, it will choose 5 random elements from the array. And since they all appear to be unique (I could be mistaken), a better way would be to do array_random($valuesToChooseFrom, 5) to select 5 random values from the array.
But since that method only returns the keys, you will need to do a foreach loop to loop over the returned array, and then use each key to grab the value from the valuesToChooseFrom array.

Related

Find first duplicate in an array

Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.
My code:
function firstDuplicate($a) {
$unique = array_unique($a);
foreach ($a as $key => $val) {
if ($unique[$key] !== $val){
return $key;
}else{
return -1;
}
}
}
The code above will be OK when the input is [2, 4, 3, 5, 1] but if the input is [2, 1, 3, 5, 3, 2] the output is wrong. The second duplicate occurrence has a smaller index. The expected output should be 3.
How can I fix my code to output the correct result?
$arr = array(2,1,3,5,3,2);
function firstDuplicate($a) {
$res = -1;
for ($i = count($a); $i >= 1; --$i) {
for ($j = 0; $j < $i; ++$j) {
if ($a[$j] === $a[$i]) {
$res = $a[$j];
}
}
}
return $res;
}
var_dump(firstDuplicate($arr));
By traversing the array backwards, you will overwrite any previous duplicates with the new, lower-indexed one.
Note: this returns the value (not index), unless no duplicate is found. In that case, it returns -1.
// Return index of first found duplicate value in array
function firstDuplicate($a) {
$c_array = array_count_values($a);
foreach($c_array as $value=>$times)
{
if($times>1)
{
return array_search($value, $array);
}
}
return -1;
}
array_count_values() will count the duplicate values in the array for you then you just iterate over that until you find the first result with more then 1 and search for the first key in the original array matching that value.
Python3 Solution:
def firstDuplicate(a):
mySet = set()
for i in range(len(a)):
if a[i] in mySet:
return a[i]
else:
mySet.add(a[i])
return -1
function firstDuplicate($a) {
foreach($a as $index => $value) {
$detector[] = $value;
$counter = 0;
foreach($detector as $item) {
if($item == $value) {
$counter++;
if($counter >= 2) {
return $value;
break;
}
}
}
}
return -1;
}
It's easy to just get the first number that will be checked as duplicated, but unfortunately, this function exceeded 4 seconds with a large array of data, so please using it with a small scale of array data.
EDIT
I have new own code fixes execution time for large array data
function firstDuplicate($a) {
$b = [];
$counts = array_count_values($a);
foreach($counts as $num => $duplication) {
if($duplication > 1) {
$b[] = $num;
}
}
foreach($a as $value) {
if(in_array($value, $b)) {
$detector[] = $value;
$counter = 0;
foreach($detector as $item) {
if($item == $value) {
$counter++;
if($counter >= 2) {
return $value;
break;
}
}
}
}
}
return -1;
}
The new code target the current numbers having a reputation only by using array_count_values()
function firstDuplicate($a) {
$indexNumber = -1;
for($i = count($a); $i >= 1 ; --$i){
for($k = 0; $k < $i; $k++){
if(isset($a[$i]) && ($a[$i] === $a[$k]) ){
$indexNumber = $a[$k];
}
}
}
return $indexNumber;
}
Remove error from undefined index array.

PHP Reverse Selection Sort

I would like to perform a selection sort of an array, using PHP. But instead of having it move from left to right, I would like it to move from right to left.
Example of Logic
$array = array(3, 0, 2, 5, -1, 4, 1);
function swap($data1, $a, $b) {
//Create temp storage for b
$bTmp = $data1[$b];
//Switch b for a value
$data1[$b] = $data1[$a];
//Set a as b value before switch
$data1[$a] = $bTmp;
//Return the sorted data
return $data1;
}
function selection($data)
{
$i1=count($data)-1;
$j1=$i1-1;
//For each value in the array
for($i=$i1; $i>1; $i--) {
//Set the minimum as the current position
$min = $i;
//Check the next value in the array (left)
for($j=$j1; $j>0; $j--) {
//If the original value (i) is bigger than the next value...
if ($data[$j]>$data[$min]) {
//Set the smaller value to be the next value
$min = $j;
}
}
$data = swap($data, $i, $min);
}
return $data;
}
//Perform the module using the array values and then output values with keys
echo(var_dump(selection($array)));
I have tried to incorporate this by using a decrementing for loop. But it only appears to partially sort the array.
Check this, it will work as expected
<?php
$array = array(3, 0, 2, 5, -1, 4, 1);
// $array = array(24,12,16,32,41,22);
function swap($data1, $a, $b) {
$bTmp = $data1[$b];
$data1[$b] = $data1[$a];
$data1[$a] = $bTmp;
return $data1;
}
function selection($data)
{
$i1=count($data)-1;
$j1 = $i1;
foreach($data as $key => $val){
for($i=$i1; $i>0; $i--) {
$min = $i;
$j1 = $min;
for($j=$j1; $j>=0; $j--) {
if ($data[$j]>$data[$min]) {
$data = swap($data, $j, $min);
$min = $j;
}
}
}
}
return $data;
}
echo(var_dump(selection($array)));
?>

Functions about random numbers

How can I create functions that answer to:
How many different numbers were generated during simulation?
% Of appearance of the numbers from 0 to 50 ?
What is the number that appeared several times during the simulation?
function randomDigits($numDigits) {
$arrayRange = 1000;
if ($numDigits <= 0) {
return '';
} else{
for($i=0; $i<$arrayRange; $i++){
echo mt_rand(0, 50) ." ";
}
}
}
$maxq = 100;
for ($i = 1; $i <= $maxq; $i++) {
echo $i . "<br>\n";
randomDigits($i) . "<br>\n----<br>\n";
}
If you store all the generated numbers into an array, you could use array_count_values()
This will count the occurrences of multiple key values in your array (e.g. if there are 12 occurrences of the number 7) and return the result in an array. This will only work for strings and integer values.
<?php
$array = ['a', 'a', 'a', 'a', 'b', 'b', 'c'];
print_r(array_count_values($array));
?>
Results from the above:
Array(
[a] => 4
[b] => 2
[c] => 1
)
From there, you should be able to easily do all the methods and outputs that you wish.
Here is the basic integration with your existing code...
<?php
$maxq = 100;
$returned_array = [];
for ($i = 1; $i <= $maxq; $i++) {
$returned_array = randomDigits($i); // return the generated array
// merge 'add' the two arrays, like saying i = i +2 or
// merged_array = merged_array + array
array_merge($returned_array, $returned_array);
// here you now have one array of 1000 random values
// print_r($returned_array);
// lets do some math
print_r(array_count_values($returned_array));
// this will show how many values were duplicates...
// e.g
// Array (
// [43] => 25 the number 43 was generated 25 times
// [25] => 22 the number 25 was generated 22 times
// [1] => 28 ect...
// you can loop through this array here and see which value was generated several times,
// format the results nicely, and do all sorts of maths on it as you wish
}
function randomDigits($numDigits) {
$arrayRange = 1000;
$generated_array = []; // here is an actual array that will store the generated numbers
if ($numDigits <= 0) {
return null;
} else {
for($i = 0; $i < $arrayRange; $i++) {
$random = mt_rand(0, 50);
array_push($generated_array, $random); // add the random value to the array
}
// here you have your array of generated numbers
return $generated_array;
}
}
?>
Example here - http://codepad.org/9Dv1CwR7
Sequence generation contains random integers for given length
function generate_array($count, $min, $max) {
$arr = array();
for ($i=0; $i<$count; $i++) {
$arr[$i] = rand($min, $max);
}
return $arr;
}
Calculating percentage of given interval (with a helper function):
function cnt($element, $arr){
$cnt = array_count_values($arr);
return $cnt[$element];
}
function percentages($min, $max, $arr) {
$total = sizeof($arr);
$occurences = 0;
while ($min < $max) {
if (!array_key_exists($min, $arr)) {
continue;
} else {
$occurences = $occurences + cnt($min, $arr);
}
$min++;
}
return $occurences/$total;
}
Add all simulated values to an array, and use array_count_values to get all unique values.
function randomDigits($numDigits) {
$arrayRange = 1000;
if ($numDigits <= 0) {
return '';
} else{
$arr=array();
for($i=0; $i<$arrayRange; $i++){
$val=mt_rand(0, 50);
$arr[]=$val;
echo $val ." ";
}
echo "\n";
$arr=array_count_values($arr);
echo "Total Unique numbers:".count($arr);
}
}
$maxq = 100;
for ($i = 1; $i <= $maxq; $i++) {
randomDigits($i) . "<br>\n----<br>\n";
}

N random numbers that can duplicate after 2 elements

I want to generate 10 numbers with each ranging from (1 to 5) but can only duplicate after 2 elements
for example 5 3 1 4 2 5(can be duplicated here) 2 (cannot be duplicate here since it occur before 1 element) ...etc.
I have this code in php working but its performance is awful since it sometimes exceeds the maximum 30 seconds execution time.
<?php
function contain($prevItems, $number) {
if (count($prevItems) == 3)
{
array_shift($prevItems);
}
for ($k=0; $k<count($prevItems); $k++) {
if ($prevItems[$k] == $number)
return true;
}
return false;
}
$num[0] = rand(1,5);
$prevItems[0] = $num[0];
for ($i=1; $i<=10; $i++) {
$num[$i] = rand(1,5);
while (contain($prevItems, $num[$i])) {
$num[$i] = rand (1,5);
}
$prevItems[$i] = $num[$i]; //append into the array
}
print_r($num);
?>
Edit:
I have also tried this method, its performance is good but it duplicates elements
<?php
$evalAccurance = array();
$count = 0;
while ( $count < 11)
{
$random = rand(1, 5);
if (in_array($random, $evalAccurance))
{
$p = $random;
for ($k = $p ; $k <5; $k++)
{
$random = $random++;
if (in_array($random, $evalAccurance))
continue 1;
else break 1;
}
if (in_array($random, $evalAccurance))
{
for ($k = $p ; $k >0; $k--)
{
$random = $random--;
if (in_array($random, $evalAccurance))
continue 1;
else break 1;
}
}
}
$evalAccurance[] = $random;
if (count ($evalAccurance) == 4)
array_shift($evalAccurance);
print_r ($evalAccurance);
$count++;
}
?>
One way you could do this:
// pass to function current array of numbers
function randomNumber($ar){
// create a random number from 1 to 5
$num = rand(1,5);
// check backwards 3 elements for same number, if none found return it
if(!in_array($num, array_slice($ar, -3, 3, true))){
return $num;
} else {
// else recall function with same array of numbers
return randomNumber($ar);
}
}
$array = array();
// loop 10 numbers and call randomNumber with current set of results.
for($i=1; $i<=10; $i++){
$array[] = randomNumber($array);
}
print_r($array);
Using PHP SPLQueue:
$queue = new SplQueue();
$values = array(1, 2, 3, 4, 5);
$container = array();
for ($i = 0; $i < 10; $i++) {
$value = give_random($values, $queue);
$container[] = $value;
if ($queue->offsetExists(1) AND $queue->offsetExists(0)) {
$queue->dequeue();
}
$queue->enqueue($value);
}
function give_random(&$values, &$queue) {
$picked_value = $values[array_rand($values)];
if ($queue->offsetExists(1)) {
if ($picked_value == $queue->offsetGet(1)) {
$picked_value = give_random($values, $queue);
}
}
if ($queue->offsetExists(0)) {
if ($picked_value == $queue->offsetGet(0)) {
$picked_value = give_random($values, $queue);
}
}
return $picked_value;
}
print_r($container);
It could be neater, but you can figure what's going on.
Cheers.

How to find the mode of an array in PHP

I have an array that has been sorted from low to high which has over 260k values in. I have found out the mean(average) and median of the array just need to find out the mode?
I cannot use any mathematical functions that PHP has, it has to be all done manually.
I would like it so there could be just one value that is the mode but then there can be multiple values that can be the mode. I also need to be able to record the number of times that the value is stored. For example the number 51 appears 6 times so I can print both values.
This is my code so far:
$amountRecords = 0;
$totalValue = 0;
$valueArray = array();
// reads in csv file
$handle = fopen('Task1-DataForMeanMedianMode.csv', 'r');
// to skip the header names/values
fgetcsv($handle);
// creates array containing variables of csv file in ascending order
while(($row = fgetcsv($handle, "\r")) != FALSE)
{
// if amountRecords equals 0
if($amountRecords == 0)
{
// adds value from csv to array
$valueArray[] = $row[1];
} // else amountRecords does not equal 0
else
{
// if the value in array location before is greater than the current value from the csv file
if($valueArray[$amountRecords - 1] > $row[1])
{
// the current array location becomes the one in the location before
$valueArray[] = $valueArray[$amountRecords - 1];
// add the value from the csv to the location before
$valueArray[$amountRecords - 1] = $row[1];
} // if the value in the location before is not greater than the current value in the csv file
else
{
// adds value from csv to array
$valueArray[] = $row[1];
}
}
// calculates the total value of the values in csv file
$totalValue = $totalValue + $row[1];
// calculates the number of values in the csv file
$amountRecords++;
}
// calculate average value of payments
$averageValue = $totalValue / $amountRecords;
// limit integer to 2 decimal place
$average = number_format($averageValue,2,'.','');
// finds middle value
$middle = floor(($amountRecords / 2) - 1);
// calculates the median value
// if array size is even
if($amountRecords % 2 == 0)
{
// calculates median
$median = $valueArray[$middle];
}
else // if array size is odd
{
// calculate low and high values
$low = $valueArray[$middle];
$high = $valueArray[$middle + 1];
// calculates median
$median = (($low + $high) / 2);
}
// works out mode
// creates array count
$count = array();
// for each value in the valueArray
foreach( $valueArray as $value )
{
if( isset( $count[$value] ))
{
$count[$value]++;
}
else
{
$count[$value] = 1;
}
}
$mostCommon = "";
$iter = 0;
foreach( $count as $k => $v )
{
if( $v > $iter )
{
$mostCommon = $k;
$iter = $v;
}
}
$modeArray = array( "mode" => $mostCommon , "count" => $iter );
The mode of a numerical set is the number that occurs the most often. You can do this with PHP using code similar to the following:
$values = array_count_values($valueArray);
$mode = array_search(max($values), $values);
Simple!
$arr = array(4,6,7,1,4,7,4,7,1);
$freq = array();
for($i=0; $i<count($arr); $i++)
{
if(isset($freq[$arr[$i]])==false)
{
$freq[$arr[$i]] = 1;
}
else
{
$freq[$arr[$i]]++;
}
}
$maxs = array_keys($freq, max($freq));
for($i=0; $i<count($maxs); $i++)
{
echo $maxs[$i] . ' ' . $freq[$maxs[$i]];
echo '<br />';
}
Mathematical only solution:
//sample data
$dataArr = ["1", "3", "5", "1", "3", "7", "1", "8", "1"];
//a multidimensional array to hold the keys (extracted fro above) and their values (number of occurrences)
$multiDArr = [];
for ($i = 0; $i < count($dataArr); $i++) {
$key = $dataArr[$i];
if (isset($multiDArr[$key])) {
//key already exists; increment count of its value
$multiDArr[$key] = $multiDArr[$key] + 1;
} else {
//key does nto exist; add it and an inital value of 1
$multiDArr[$key] = 1;
}
}
$highestOccuring = 0;
$highestOccuringKey = null;
foreach ($multiDArr as $key => $value) {
if ($value > $highestOccuring) {
$highestOccuring = $value;
$highestOccuringKey = $key;
}
}
echo "MODE / highest occuring key: " . $highestOccuringKey;
/** find array mode, most often see value
* #param list(int) $a_in
* #return list(int)
*/
function array_mode(array $a_in): array{
$a_freq = [];
foreach( $a_in as $v ) {
if (!isset($a_freq[$v])) {
$a_freq[$v] = 0;
}
$a_freq[$v]++;
}
$a_maxs = array_keys($a_freq, max($a_freq));
return $a_maxs;
}
// test code
$a_in = [4,6,7,1,4,7,4,7,1];
array_mode( $a_in);
This will take values and turn it into an array of the modes. For Example: print_r(get_mode(1,2,2,3,3,3,4,4,5,5,5,6,7,8,9)); will return,
Array
(
[0] => Array
(
[value] => 3
[count] => 3
)
[1] => Array
(
[value] => 5
[count] => 3
)
)
code:
function get_mode(...$inputArray){
$max=0;
return array_reduce(
array_values(array_reduce(array_values($inputArray),function($talliedArray,$inputNode){
if(isset($talliedArray[(string)$inputNode]))
$talliedArray[(string)$inputNode]['count']++;
else
$talliedArray[(string)$inputNode] = [
'value' => $inputNode,
'count' => 1
];
return $talliedArray;
},[])),function($modeArray,$talliedNode)use(&$max){
if($talliedNode['count'] < $max) return $modeArray;
if($talliedNode['count']=== $max) return array_merge($modeArray,[$talliedNode]);
//if count > $max
$max = $talliedNode['count'];
return [$talliedNode];
},[]);
}
This satisfies the "no math functions", the "multiple return modes" and the "have the value and number of occurrences returned".
This will only really work with strings and numbers. It will go weird with booleans, Objects and Arrays.

Categories