PHP - how to tell if ALL adjacent elements in multidimensional array exist? - php

This is a stupid small problem we had at work that a few of us have different solutions to, and we're wondering if there's a better way to do it. I'll boil it down to something really simple for the sake of example.
Say we have a multidimensional array with the below values in it. Each value is it's own element, and each row is an array.
0a00
000b
c000
In the above "array", $array[0][1] would be "a", $array[1][3] would be "b", and $array[2][0] would be "c". What we need to do, is increment all values adjacent to non-numeric values by 1. So, the array after we've incremented the values should look like the below array. The current solution we have is to first check all 4 "corners" of the array, increment the adjacent values, then check the top and bottom rows, then check the first and last columns, and finally, check all other elements. Whenever we hit a non-numeric element, we increment all other adjacent non-numeric elements by 1. This is kind of lame/cumbersome, and we know there has to be a better way. It's pretty much building a bombsweeper board in reverse, when you know where all of the bombs are.
1a21
222b
c111

I have in mind this simple algorithm:
function getNeighborsCount($rgData, $iX, $iY)
{
if(ord($rgData[$iX][$iY])>=ord('a') && ord($rgData[$iX][$iY])<=ord('z'))
{
return null;
}
$iResult = 0;
for($i=$iX-1; $i<=$iX+1; $i++)
{
for($j=$iY-1; $j<=$iY+1; $j++)
{
if(isset($rgData[$i][$j]) &&
ord($rgData[$i][$j])>=ord('a') &&
ord($rgData[$i][$j])<=ord('z'))
{
$iResult++;
}
}
}
return $iResult;
}
-then apply it to whole array:
$rgData = [
str_split('0a00'),
str_split('000b'),
str_split('c000')
];
for($i=0; $i<count($rgData); $i++)
{
for($j=0; $j<count($rgData[$i]); $j++)
{
if($iCount = getNeighborsCount($rgData, $i, $j))
{
$rgData[$i][$j]=$iCount;
}
}
}
-this will result with
echo(join(PHP_EOL, array_map(function($rgStr)
{
return join('', $rgStr);
}, $rgData)));
to:
1a21
222b
c111
Now, about complexity. If we have N elements, it will be O(9N) since we're iterating 9 times for each element inside function.

Funny little puzzle. Try the following:
$arr = array(
array(0, 'a', 0, 0),
array(0, 0, 0, 'b'),
array('c', 0, 0, 0),
);
$max_i = count($arr);
$max_j = count($arr[0]);
for ($i = 0; $i < $max_i; $i++) {
for ($j = 0; $j < $max_j; $j++) {
if (!is_int($arr[$i][$j])) {
for ($_i = $i - 1; $_i <= $i + 1; $_i++) {
for ($_j = $j - 1; $_j <= $j + 1; $_j++) {
if (($_i == $i && $_j == $j) || $_i < 0 || $_i >= $max_i || $_j < 0 || $_j >= $max_j) {
continue;
}
if (is_int($arr[$_i][$_j])) {
$arr[$_i][$_j]++;
}
}
}
}
}
}
Not sure if this is the most efficient way, but it should be shorter than what you have.

Related

How to compare every value in a for loop?

I have a multidimentional array of 5 items and I want that my loop would compare it like:
1 -> 2, 1 -> 3, 1 -> 4, 1 -> 5, 2->1, 2->3, 2->4, 2->5......// so on and 5 -> 4 in the end.
The problem is that after my array $i value matches 1 and $j value matches 3, the unset is done and the $i value becomes 2 (which is correct) and $j value becomes 4 instead of 3. Could someone tell me why and what I'm doing wrong?
My loop is:
for ($i = 0; $i <= count($myArray); $i++) {
for ($j = $i+1; $j <= count($myArray); $j++) {
if (
// condition 1
&& // condition 2
) {
unset($myArray[$i]);
$i++;
}
}
}
I think that's the problem is when you unset the element in the array, you increment the counter of the loop $i. In this way, the elements of the array that are not configured are removed, this empty array position will be maintained, it will not be reordered, you will have to do it manually or using array_values ​​method.
In the last tour of the array, it will break because you are comparing the number of array elements as equal. You must use index < count($array)
The code would be like this:
for ($i = 0; $i < count($myArray); $i++) {
for ($j = $i+1; $j < count($myArray); $j++) {
if (
// condition 1
&& // condition 2
) {
unset($myArray[$i]);
// $i++;
}
}
}
try something like this
for ($i = 0; $i <= count($myArray); $i++) {
for ($j = 0; $j <= count($myArray); $j++) {
if ($j!=$i)
{
if (
// condition 1
&& // condition 2
) {
unset($myArray[$i]);
$i++;
}
}
}
}
$temp = $myArray;
for ($i = 0; $i <= count($myArray); $i++)
{
for ($j = $i + 1; $j <= count($myArray); $j++)
{
if (
// condition 1
&& // condition 2
)
{
unset($temp[$i]);
$i++;
}
}
}
print_r($temp);
Your result is in $temp. So here indexes wont get hampered, you actually are applying all operation on $temp and normally traversing $myArray.
To be honest, I do not know why nobody has yet advise to use nested foreach, since you all noticed there was a problem with array size.
foreach ($numbers as $number_horizontal_parsing) {
foreach ($numbers as $number_vertical_parsing) {
if ($number_horizontal_parsing != $number_vertical_parsing) {
//do your stuff in your case it seems you want to compare both variables
}
}
}

Calculate from an array the number that is equal or higher and closest to a given number

I need to calculate from a given array the number that is equal or higher and closest to a given number in PHP. Example:
Number to fetch:
6.85505196
Array to calculate:
3.11350000
4.38350000
4.04610000
3.99410000
2.86135817
0.50000000
Only correct combination should be:
3.99410000 + 2.86135817 = 6.85545817
Can somebody help me? It's been 3 hours I'm getting mad!
UPDATE: I finally finished my code as following:
$arr = array(3.1135, 4.3835, 4.0461, 3.9941, 2.86135817, 0.5);
$fetch = 6.85505196;
$bestsum = get_fee($arr, $fetch);
print($bestsum);
function get_fee($arr, $fetch) {
$bestsum = 999999999;
$combo = array();
$result = array();
for ($i = 0; $i<count($arr); $i++) {
combinations($arr, $i+1, $combo);
}
foreach ($combo as $idx => $arr) {
$sum = 0;
foreach ($arr as $value) {
$result[$idx] += $value;
}
if ($result[$idx] >= $fetch && $result[$idx] < $bestsum) $bestsum = $result[$idx];
}
return $bestsum;
}
function combinations($arr, $level, &$combo, $curr = array()) {
for($j = 0; $j < count($arr); $j++) {
$new = array_merge($curr, array($arr[$j]));
if($level == 1) {
sort($new);
if (!in_array($new, $combo)) {
$combo[] = $new;
}
} else {
combinations($arr, $level - 1, $combo, $new);
}
}
}
I hope the following example might help you. Please try this
<?php
$array = array(
"3.11350000",
"4.38350000",
"4.04610000",
"3.99410000",
"2.86135817",
"0.50000000"
);
echo "<pre>";
print_r($array);// it will print your array
for($i=0; $i<count($array); $i++)
{
$j=$i+1;
for($j;$j<count($array); $j++)
{
$sum = $array[$i] + $array[$j];
// echo $array[$i]. " + ".$array[$j]." = ".$sum."<br>"; //this will display all the combination of sum
if($sum >= 6.85505196 && ($sum <= round(6.85505196)) )//change the condition according to your requirement
{
echo "The correct combinations are:<br/><br/>";
echo "<b>". $array[$i]. " + ".$array[$j]." = ".$sum."<b>";
echo "<br/>";
}
}
echo "<br/>";
}
?>
We will get the result as below
Array
(
[0] => 3.11350000
[1] => 4.38350000
[2] => 4.04610000
[3] => 3.99410000
[4] => 2.86135817
[5] => 0.50000000
)
The correct combinations are:
4.04610000 + 2.86135817 = 6.90745817
3.99410000 + 2.86135817 = 6.85545817
You should do it in two steps:
a. Work out (or look up) an algorithm to do the job.
b. Implement it.
You don't say what you've managed in the three hours you worked on this, so here's a "brute force" (read: dumb) algorithm that will do the job:
Use a variable that will keep your best sum so far. It can start out as zero:
$bestsum = 0;
Try all single numbers, then all sums of two numbers, then all sums of three numbers, etc.: Every time you find a number that meets your criteria and is better than the current $bestsum, set $bestsum to it. Also set a second variable, $summands, to an array of the numbers you used to get this result. (Otherwise you won't know how you got the solution). Whenever you find an even better solution, update both variables.
When you've tried every number combination, your two variables contain the best solution. Print them out.
That's all. It's guaranteed to work correctly, since it tries all possibilities. There are all sorts of details to fill in, but you can get to work and ask here for help with specific tasks if you get stuck.
Thank you all for your help!
My code is working pretty cool when is needed to fetch one or two numbers (addition) only. But can't figure out how to add more combinations up to the total count of elements in my given array.
I mean if there are, let's say, 8 numbers in my array I want to try all possible combinations (additions to each other) as well.
My actual code is:
$bestsum = 1000000;
for ($i = 0; $i < count($txinfo["vout"]); $i++) {
if ($txinfo["vout"][$i]["value"] >= $spent && $txinfo["vout"][$i]["value"] < $bestsum) {
$bestsum = $txinfo["vout"][$i]["value"];
}
}
for($i = 0; $i < count($txinfo["vout"]); $i++) {
$j = $i + 1;
for($j; $j < count($txinfo["vout"]); $j++) {
$sum = $txinfo["vout"][$i]["value"] + $txinfo["vout"][$j]["value"];
if($sum >= $spent && $sum < $bestsum) {
$bestsum = $sum;
}
}
}
$fee = bcsub($bestsum, $spent, 8);
print("Fee: ".$fee);
New updated code.
<?php
$x = 6.85505196;
$num = array(3.1135, 4.3835, 4.0461, 3.9941, 2.86135817, 0.5);
asort($num); //sort the array
$low = $num[0]; // lowest value in the array
$maxpossible = $x+$low; // this is the maximum possible answer, as we require the number that is equal or higher and closest to a given number
$num = array_values($num);
$iterations = $x/$num[0]; // possible combinations loop, to equate to the sum of the given number using the lowest number
$sum=$num;
$newsum = $sum;
$k=count($num);
for($j=0; $j<=$iterations; $j++){
$l = count($sum);
for($i=0; $i<$l; $i++){
$genSum = $sum[$j]+$sum[$i];
if($genSum <= $maxpossible){
$newsum[$k] = $genSum;
$k++;
}
}
$newsum = array_unique($newsum);
$newsum = array_values($newsum);
$k = count($newsum);
$sum = $newsum;
}
asort($newsum);
$newsum = array_values($newsum);
for($i=0; $i<count($newsum); $i++){
if($x<=$newsum[$i]){
echo "\nMaximum Possible Number = ".$newsum[$i];
break;
}
}
?>

PHP Check if number is smaller than array element values

This seems really simple but I can't figure out the cleanest way to do it...
Basically I have a sorted Array with numbers like this:
$array1 = [3, 7, 12, 63, 120, 512, 961];
What I need to do is check each element of the array against a number which can be like this:
$number = 320;
And I need to get the element which is next to the number in this example it would be 120 because 120 < $number < 512.
Well, my way approach which kinda works is pretty messy I think:
foreach ($i = 0; $i < count($array1); $i++) {
if ($array[$i] < $number) {
// echo "do nothing, elements are smaller than number
} else {
if ($flag == true) {
// echo "elements are not smaller anymore and flag is set"
$getValue = $array[$i-1]; // last element which was smaller
$flag == false;
}
}
}
Another problem is, I need to cover the cases if $number is smaller than the smallest element of the array or if its larger than the largest element of the array. For that case I create another Variable $t and check it with the length of the array in each iteration
$t = 0;
$len = count($array1);
// if element bigger than number and first iteration
if ($array[$i] > $number && $t == 0) {
}
$t += 1;
I left the foreach loop out here but as you can probably see it gets really long and its certainly not clean. How can it be done better?
As it is an sorted array you are working with then i recommend you to implement binary search algorithm which has O(log(n)) in time complexity .
int binary_search(int A[], int key, int imin, int imax)
{
// continue searching while [imin,imax] is not empty
while (imin <= imax)
{
// calculate the midpoint for roughly equal partition
int imid = midpoint(imin, imax);
if(A[imid] == key)
// key found at index imid
return imid;
// determine which subarray to search
else if (A[imid] < key)
// change min index to search upper subarray
imin = imid + 1;
else
// change max index to search lower subarray
imax = imid - 1;
}
// key was not found
return KEY_NOT_FOUND;
}
source: wikipedia.org
$array = [3, 7, 12, 63, 120, 512, 961];
$min = 0;
$max = count($array)-1;
$no = 320;
while ($min < $max)
{
$mid = (int)(($min+$max)/2);
if ($array[$mid] == $no) {
print $array[$mid-1]."<".$no."<".$array[$mid+1];
return;
}
else if($array[$mid] < $no) {
$min = $mid+1;
} else {
$max = $mid-1;
}
if($min == $max ) {
if($array[$min] > $no) {
print $array[$min-1]."<".$no."<".$array[$min];
return;
} else {
print $array[$min]."<".$no."<".$array[$min+1];
return;
}
}
}

Sudoku solving/generating algorithm in PHP

I'm having trouble with a specific part of my algorithm and was hoping someone has an idea what I'm doing wrong.
My program basically works like this:
Create 81 empty cells, fill each cell step per step while checking if it's valid there.
I have 3 valid checks and the horizontal valid check (if numbers are double or more in 1 line) is already giving me trouble.
This is my function:
private function isValidHorizontal($index)
{
for ($i = 0; $i < 81; $i += 9){
$firstIndex = $i * 9;
$lastIndex = 9 * ($i + 1) - 1;
// fisrt loop tracking fowards, 2nd loop tracking backwards
if ($index >= $i && $index <= $lastIndex) {
for ($j = 0; $j <= $lastIndex; $j++) {
if ($this->cell[$index]->getValue() == $j) {
return false;
}
}
for ($k = 0; $k >= $firstIndex; $k--){
if ($this->cell[$index]->getValue() == $j) {
return false;
}
}
}
}
return true;
}
$index is the position of the cell so when $index = 0 that would be the very first cell. Last cell would be $index = 80
$this->cell[$index]->getValue() returns an int number i checked so I'm getting the value correctly.
The problem it somehow never returns true
Any Ideas? obviously this is just part of the code, if you need more to help, write a comment and I'll edit :)
In the second inner loop you are using $j instead of $k:
for ($k = 0; $k >= $firstIndex; $k--){
if ($this->cell[$index]->getValue() == $j) { // Here, change to $k
You already got the right answer from #this.lau_, but If I may offer some advice, you could shorten it up a bit by changing the logic. PHP isn't really the best suited language for this, so it'll still look a bit clunky, but I might be worth taking a look at. :)
private function isValidHorizontal($index) {
$taken = [];
foreach (range($index, 81, 9) as $i) {
$value = $this->cell[$i]->getValue();
if (is_int($value) && in_array($value, $taken)) {
return false;
}
$taken[] = $value;
}
return true;
}

combination with array elements

I have this code:
$um = array("PHP", "JAVA", "MySQL")
$a = count($um)
for ($i = 0; $i < $a; $i++) {
for ($x = $i + 1; $x <$a; $x++) {
$arr1 [] = array($um[$i],$um[$x]);
}
}
This will output something like these combinations:
PHP[0] JAVA[1]
PHP[0] MySQL[2]
JAVA[1] MySQL[2]
Well, works without any problem.
But now, i want to change the output to something like:
PHP[0] JAVA[1]
JAVA[1] MySQL[2]
MySQL[2] PHP[0]
The logic will be the same for the three array elements or even 10, and so on.
Any idea about this ?
Just use modular arithmetic: you want to pair indices (i,i+1), where the i+1 wraps around to 0 if it becomes too large.
for ($i = 0; $i < $a; $i++) {
echo $um[$i], ', ', $um[($i+1) % $a]
}
So for $a=3 this displays according to indices:
0, 1
1, 2
2, 0 (since 3 % 3==0)
Modulus Operator docs
// make a copy of the array
$array2 = $array;
// rotate the entries by one
$array2[] = array_shift($array2);
// combine the elements
$combined = array_map(function () { return func_get_args(); }, $array, $array2);

Categories