Multiple counters not counting ( foreach / ifs) - php

This is my first StackOverFlow post.
I have an array of numbers, which I shuffle:
$nums = array("1","1","1","1","1","2","2","2","2","3","3","3","4","4","4");
shuffle($nums);
I am trying to identify a sequence/pattern of any three 3 identical numbers in a row in the shuffled array and
output the combined total of all sets of matching numbers.
The problem that I am running into seems to stem from attempting to compare the current number in the loop with the previous number (to see if they match).
When I echo the "previous" number it always outputs as "0". Thus I am unable to compare the current number and old number, which means I am not able to sum identify and sum a pattern of identical numbers.
Here is my code:
<?php
$t3count = 0;
$oldnum = 0;
$tots = 0;
$nums = array("1","1","1","1","1","2","2","2","2","3","3","3","4","4","4");
shuffle($nums);
foreach ($nums as $num) {
echo "$num: [$oldnum] ";
if ($num = $oldnum) {
$t3count++;
if ($t3count = 3) {
$tots = $num * $num;
$t3count = 0;
$oldnum = $num;
} else {
# do nonum
}
}
else {
$oldnum = $num;
}
# echo "<li>$num</li>";
}
echo "Your total is: $tots";
unset($num);
?>
Thank you.

You need to do comparison == not assignment = here:
if ($num = $oldnum)
and here:
if ($t3count = 3)

also this is probably going to bite you if i got the logic right
$t3count++;
if ($t3count == 3) {
how do you know which one counted to 3, id build nested arrays of like values first then process that.

you missed == to compare if ($num = $oldnum) and if ($t3count = 3)
replace with
if ($num == $oldnum)
if ($t3count == 3)

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

Random Numbers, Column and Row. PHP

Please excuse me as this is my first post and I am fairly new to any type of programming. I hope my question is clear, I am using Excel references as I think this explains what I am trying to do best.
I am trying to generate random numbers for a pool. I have 8 rows of numbers and each row contains 10 spots, 0 to 9. I want to have a random number in each row and make sure the the number does not repeat in each row.
Example Grid - 8 columns wide x 10 rows long.
I am repeating this scrip for each column, but I am getting the same number is rows and I want make sure that does not happen.
for ($i=1; $i<=10; $i++) {
while (1) {
$duplicate = 0;
$num=rand(0,9);
for ($x=1; $x<$i; $x++) {
if ($NFC1[$x]==$num) { $duplicate = 1; }
}
if ($duplicate==0) {
$NFC1[$i]=$num;
break;
}
}
}
This is the results, as you can see I have random numbers is each column but not in each row.
"4";"8";"5";"5";"0";"4";"2";"7"
"5";"9";"4";"3";"9";"9";"9";"0"
"9";"5";"1";"1";"5";"8";"6";"1"
"7";"4";"6";"2";"6";"7";"3";"3"
"2";"6";"8";"4";"7";"2";"7";"5"
"0";"1";"0";"7";"2";"1";"4";"6"
"1";"7";"9";"9";"4";"3";"0";"4"
"3";"0";"3";"0";"3";"5";"5";"9"
"8";"2";"7";"8";"1";"6";"8";"2"
"6";"3";"2";"6";"8";"0";"1";"8"
The answer from here addapted to non-square array may looks like below:
$rows = 10; // Number of rows
$columns = 8; // Number of columns
$row = range(0, $columns-1);
$column = range(0, $rows-1);
shuffle($row);
shuffle($column);
// Create an array
foreach ($row as $x => $value)
foreach ($column as $y)
$array[$y][$x] = $value++ % max($rows, $columns);
And if you want to see the result:
foreach($array as $r) {
foreach($r as $number) {
echo $number.' ';
}
echo "<br/>";
}

How to tell if a comma delimited list of numbers obeys the natural order of numbers

I have a comma delimited list of numbers which i am converting into an array and what i want to know about the list of numbers is if the numbers listed obey a natural ordering of numbers,you know,have a difference of exactly 1 between the next and the previous.
If its true the list obeys the natural ordering,i want to pick the first number of the list and if not the list obeys not the natural order,i pick the second.
This is my code.
<?php
error_reporting(0);
/**
Analyze numbers
Condition 1
if from number to the next has a difference of 1,then pick the first number in the list
Condition 2
if from one number the next,a difference of greater than 1 was found,then pick next from first
Condition 3
if list contains only one number,pick the number
*/
$number_picked = null;
$a = '5,7,8,9,10';
$b = '2,3,4,5,6,7,8,9,10';
$c = '10';
$data = explode(',', $b);
$count = count($data);
foreach($data as $index => $number)
{
/**
If array has exactly one value
*/
if($count == 1){
echo 'number is:'.$number;
exit();
}
$previous = $data[($count+$index-1) % $count];
$current = $number;
$next = $data[($index+1) % $count];
$diff = ($next - $previous);
if($diff == 1){
$number_picked = array_values($data)[0];
echo $number_picked.'correct';
}
elseif($diff > 1){
$number_picked = array_values($data)[1];
echo $number_picked.'wrong';
}
}
?>
The problem i am having is to figure out how to test the difference for all array elements.
No loops are needed, a little bit of maths will help you here. Once you have your numbers in an array:
$a = explode(',', '5,7,8,9,10');
pass them to this function:-
function isSequential(array $sequence, $diff = 1)
{
return $sequence[count($sequence) - 1] === $sequence[0] + ($diff * (count($sequence) - 1));
}
The function will return true if the numbers in the array follow a natural sequence. You should even be able to adjust it for different spacings between numbers, eg 2, 4, 6, 8, etc using the $diff parameter, although I haven't tested that thoroughly.
See it working.
Keep in mind that this will only work if your list of numbers is ordered from smallest to largest.
Try using a function to solve this... Like so:
<?php
error_reporting(0);
/**
Analyze numbers
Condition 1
if from number to the next has a difference of 1,then pick the first number in the list
Condition 2
if from one number the next,a difference of greater than 1 was found,then pick next from first
Condition 3
if list contains only one number,pick the number
*/
$number_picked = null;
$a = '5,7,8,9,10';
$b = '2,3,4,5,6,7,8,9,10';
$c = '10';
function test($string) {
$data = explode(',', $string);
if(count($data) === 1){
return 'number is:'.$number;
}
foreach($data as $index => $number)
{
$previous = $data[($count+$index-1) % $count];
$current = $number;
$next = $data[($index+1) % $count];
$diff = ($next - $previous);
if($diff == 1){
$number_picked = array_values($data)[0];
return $number_picked.'correct';
}
elseif($diff > 1){
$number_picked = array_values($data)[1];
return $number_picked.'wrong';
}
}
}
echo test($a);
echo test($b);
echo test($c);
?>
You already know how to explode the list, so I'll skip that.
You already handle a single item, so I'll skip that as well.
What is left, is checking the rest of the array. Basically; there's two possible outcome values: either the first element or the second. So we'll save those two first:
$outcome1 = $list[0];
$outcome2 = $list[1];
Next, we'll loop over the items. We'll remember the last found item, and make sure that the difference between the new and the old is 1. If it is, we continue. If it isn't, we abort and immediately return $outcome2.
If we reach the end of the list without aborting, it's naturally ordered, so we return $outcome1.
$lastNumber = null;
foreach( $items as $number ) {
if($lastNumber === null || $number - $lastNumber == 1 ) {
// continue scanning
$lastNumber = $number;
}
else {
// not ordened
return $outcome2;
}
}
return $outcome1; // scanned everything; was ordened.
(Note: code not tested)
To avoid the headache of accessing the previous or next element, and deciding whether it still is inside the array or not, use the fact that on a natural ordering the item i and the first item have a difference of i.
Also the corner case you call condition 3 is easier to handle outside the loop than inside of it. But easier still, the way we characterize a natural ordered list holds for a 1-item list :
$natural = true;
for($i=1; $i<$count && $natural; $i++)
$natural &= ($data[$i] == $data[0] + $i)
$number = $natural ? $data[0] : $data[1];
For $count == 1 the loop is never entered and thus $natural stays true : you select the first element.

How to define trends according to some values?

I am trying to to mark some trends, so I have 1 as the lowest and 5 as the biggest value.
So for example,
I may have the following case:
5,4,5,5 (UP)
3,4, (UP)
4,3,3 (DOWN)
4,4,4,4, (FLAT - this is OK for all same numbers)
I am planning to have unlimited number of ordered values as input, an as an output I will just show an (UP), (DOWN), or (FLAT) image.
Any ideas on how I can achieve this?
Sorry if I am not descriptive enough.
Thank you all for you time.
Use least square fit to calculate the "slope" of the values.
function leastSquareFit(array $values) {
$x_sum = array_sum(array_keys($values));
$y_sum = array_sum($values);
$meanX = $x_sum / count($values);
$meanY = $y_sum / count($values);
// calculate sums
$mBase = $mDivisor = 0.0;
foreach($values as $i => $value) {
$mBase += ($i - $meanX) * ($value - $meanY);
$mDivisor += ($i - $meanX) * ($i - $meanX);
}
// calculate slope
$slope = $mBase / $mDivisor;
return $slope;
} // function leastSquareFit()
$trend = leastSquareFit(array(5,4,5,5));
(Untested)
If the slope is positive, the trend is upwards; if negative, it's downwards. Use your own judgement to decide what margin (positive or negative) is considered flat.
A little bit hard to answer based on the limited info you provide, but assuming that:
if there's no movement at all the trend is FLAT,
otherwise, the trend is the last direction of movement,
then this code should work:
$input = array();
$previousValue = false;
$trend = 'FLAT';
foreach( $input as $currentValue ) {
if( $previousValue !== false ) {
if( $currentValue > $previousValue ) {
$trend = 'UP';
} elseif( $currentValue < $previousValue ) {
$trend = 'DOWN';
}
}
$previousValue = $currentValue;
}
For your examples :
Calculate longest increasing subsequence, A
Calulate longest decreasing subsequence , B
Going by your logic, if length of A is larger than B , its an UP , else DOWN.
You will also need to keep track of all equals using one boolean variable to mark FLAT trend.
Query :
What trend would be :
3,4,5,4,3 ?
3,4,4,4,3 ?
1,2,3,4,4,3,2,2,1 ?
Then the logic might need some alterations depending upon what your requirements are .
I'm not sure if i understand your problem totally but I would put the values in an array and use a code like this (written in pseudocode):
int i = 0;
String trend = "FLAT":
while(i<length(array)) {
if(array(i)<array(i+1)) {
trend = "UP";
}
else if(array(i)>array(i+1) {
trend = "DOWN";
}
i++;
}
EDIT: this would obviously only display the trend of the latest alteration
one would also may count the number of times the trend is up or down and determine the overall trend by that values
echo foo(array(5,4,5,5)); // UP
echo foo(array(3,4)); // UP
echo foo(array(4,3,3)); // DOWN
echo foo(array(4,4,4,4)); // FLAT
function foo($seq)
{
if (count(array_unique($seq)) === 1)
return 'FLAT';
$trend = NULL;
$count = count($seq);
$prev = $seq[0];
for ($i = 1; $i < $count; $i++)
{
if ($prev < $seq[$i])
{
$trend = 'UP';
}
if ($prev > $seq[$i])
{
$trend = 'DOWN';
}
$prev = $seq[$i];
}
return $trend;
}
I used the code from #liquorvicar to determine Google search page rank trends, but added some extra trend values to make it more accurate:
nochange - no change
better (higher google position = lower number)
worse (lower google position = higher number)
I also added extra checks when the last value had no change, but taking in account the previous changes i.e.
worsenochange (no change, previouse was worse - lower number)
betternochange (no change, previouse was better - lower number)
I used these values to display a range of trend icons:
$_trendIndicator="<img title="trend" width="16" src="/include/main/images/trend-'. $this->getTrend($_positions). '-icon.png">";
private function getTrend($_positions)
{
// calculate trend based on last value
//
$_previousValue = false;
$_trend = 'nochange';
foreach( $_positions as $_currentValue ) {
if( $_previousValue !== false ) {
if( $_currentValue > $_previousValue ) {
$_trend = 'better';
} elseif( $_currentValue < $_previousValue ) {
$_trend = 'worse';
}
if ($_trend==='worse' && ($_previousValue == $_currentValue)) {$_trend = 'worsenochange';}
if ($_trend==='better' && ($_previousValue == $_currentValue)) {$_trend = 'betternochange';}
}
$_previousValue = $_currentValue;
}
return $_trend;
}

How can I check an array for consecutive times?

I have an array of qualified times from my database:
$avail_times = array("9","11","12","13","15","16","17","18");
I want to display 4 consecutive values if they exist, if not I want to continue. For example in the above array, the only place where there are four consecutive numbers that properly follow the one before is 15,16,17,and 18
Thoughts?
This may be a duplicate problem, but I have not found a solution. My situation is a bit different. I need to show only those numbers that are consecutive four or more times. This is what I have come up with, but it is not working properly:
$avail_times = array("9","10","11","13","14","15","16","17","19","20","21","22");
for($i=1, $max = count($times) + 4; $i < $max; $i++)
{
if ($avail_times[$i] == $avail_times[$i + 1] - 1)
{
echo $avail_times[$i];
}
}
This should do you:
$avail_times = array("9","10","11","13","14","15","16","17","19","20","21","22");
$consec_nums = 1;
for($i = 1; $i <count($avail_times); $i++) {
if($avail_times[$i] == ($avail_times[$i - 1] + 1)) {
$consec_nums++;
if($consec_nums == 4) break;
}
else {
$consec_nums = 1;
}
}
if($consec_nums == 4) {
echo "found: {$avail_times[$i-3]}, {$avail_times[$i-2]}, {$avail_times[$i-1]}, {$avail_times[$i]}\n";
}
And a few notes:
array indexing starts at 0, when your for loop starts with $i = 1, you are skipping the first element. Notice that while I start at $i=1, I am comparing $avail_times[$i] and $avail_times[$i-1] so I do cover $avail_times[0].
I don't know what you're doing with $max = count($times). You never define $times.

Categories