Get diagonal values of spiral matrix - php

I have a n*n spiral matrix.
if N = 4
then matrix :
7 8 9 10
6 1 2 11
5 4 3 12
16 15 14 13
if N = 3
7 8 9
6 1 2
5 4 3
I want to get the diagonal values of this spiral matrix.
In the n=4 case diagonal values would be 7,1,3,13,10,2,4,16
I can do this by storing this matrix in array and traversing for each diagonal value.
Is there any better way to get these values.

To get the numbers on the main diagonal, we can notice that the values are
1 = 1
1 + 2 = 3
1 + 2 + 4 = 7
1 + 2 + 4 + 6 = 13
So the general formula is 1 + (sum i = 0 to k of 2*i) for k = 0, 1, 2, ...
Simplifying this, we get k^2 + k + 1 for k = 0, 1, 2, ...
In PHP, we can generate these via something like this:
function mainDiagonal($n) {
$values = array();
for ($k = 0; $k < $n; $k++) {
$values[] = $k*$k + $k + 1;
}
return $values;
}
To get the numbers on the antidiagonal for even N we see:
2 = 2
2 + 2 = 4
2 + 2 + 6 = 10
2 + 2 + 6 + 6 = 16
If we continue this pattern for larger matrices we see the general formula is
sum i = 0 to k of floor(i/2)*4 + 2 for k = 0, 1, 2, ...
Similarly for odd N we find the formula is
1 + (sum i = 0 to k of ceil(i/2)*4) for k = 0, 1, 2, ...
In PHP, we can generate these via something like this:
function antiDiagonal($n) {
$values = array();
if ($n % 2 == 0) {
for ($k = 0; $k < $n; $k++) {
$accum = 0;
for ($j = 0; $j <= $k; $j++) {
$accum += floor($j/2)*4 + 2;
}
$values[] = $accum;
}
} else {
for ($k = 0; $k < $n; $k++) {
$accum = 1;
for ($j = 0; $j <= $k; $j++) {
$accum += ceil($j/2)*4;
}
$values[] = $accum;
}
}
return $values;
}
Notice that the maximum value of k is one less than the dimension of the matrix.
Combining these functions, we obtain:
array_unique(array_merge(mainDiagonal($n), antiDiagonal($n)))

The problem can be divided into 4 parts: Find the numbers along the diagonal spoke in each quadrant. There are four quadrants, so we have four spokes:
Northwest (NW) spoke
Northeast (NE) spoke
Southwest (SW) spoke
Southeast (SE) spoke
For example, in your illustration of Ulam spiral, when N is even.
NW spoke have 1, 7, ...
NE spoke have 2, 10, ...
SW spoke have 4, 16, ...
SE spoke have 3, 13, ...
The problem is further subdivided into two cases:
N is even.
N is odd.
Case 1: N is even
Here are the formulas for each spoke:
NW spoke: f(n) = 4*n*n + 2*n + 1
NE spoke: g(n) = 4*n*n + 4n + 2
SW spoke: h(n) = 4*n*n + 8*n + 4
SE spoke: i(n) = 4*n*n + 6*n + 3
where n = 0, 1, 2, ...
For 4x4 matrix, compute the following set:
{f(0), f(1), g(0), g(1), h(0), h(1), i(0), i(1)}
It yields the diagonal values:
{1, 7, 2, 10, 4, 16, 3, 13}
In general, for an NxN matrix, when N is even, compute the following set to get the diagonal values:
{ f(0), ..., f(N/2 - 1),
g(0), ..., g(N/2 - 1),
h(0), ..., h(N/2 - 1),
i(0), ..., i(N/2 - 1) }
Case 2: N is odd
In your illustration of Ulam spiral, when N is odd, the formulas for each spoke are:
NW spoke: f(n) = 4*n*n + 2*n + 1
NE spoke: g(n) = 4*n*n + 4*n + 1
SW spoke: h(n) = 4*n*n + 1
SE spoke: i(n) = 4*n*n - 2*n + 1
where n = 0, 1, 2, ...
Note that f(0) = g(0) = h(0) = i(0) = 1.
For 3x3, compute the following set:
{f(0), f(1), g(1), h(1), i(1)}
It yields the following diagonal values:
{1, 7, 9, 5, 3}.
In general, for an NxN matrix, when N is odd, compute the following set to get the diagonal values:
{ f(0), ..., f((N - 1)/2,
g(0), ..., g((N - 1)/2),
h(0), ..., h((N - 1)/2),
i(0), ..., i((N - 1)/2) }
PHP Code
Finally, here is a PHP program that demonstrates what I have discussed above.
<?php
function ulam_diag($N)
{
$result = array();
if ($N % 2 == 0) {
for ($n = 0; $n < $N / 2; $n++) {
$result[] = 4*$n*$n + 2*$n + 1;
$result[] = 4*$n*$n + 4*$n + 2;
$result[] = 4*$n*$n + 8*$n + 4;
$result[] = 4*$n*$n + 6*$n + 3;
}
} else {
$result[] = 1;
for ($n = 1; $n <= ($N - 1) / 2; $n++) {
$result[] = 4*$n*$n + 2*$n + 1;
$result[] = 4*$n*$n + 4*$n + 1;
$result[] = 4*$n*$n + 1;
$result[] = 4*$n*$n - 2*$n + 1;
}
}
sort($result);
return $result;
}
print_r(ulam_diag(4));
print_r(ulam_diag(3));
?>
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 7
[5] => 10
[6] => 13
[7] => 16
)
Array
(
[0] => 1
[1] => 3
[2] => 5
[3] => 7
[4] => 9
)
Here is the code one Ideone: http://ideone.com/F9jaC0
In case you are wondering how I arrived at the formulas, there are well established results for the four spokes of Ulam spiral. Here are the references:
https://oeis.org/A054569 (NW spoke in your illustration)
https://oeis.org/A016754 (NE spoke in your illustration)
https://oeis.org/A053755 (SW spoke in your illustration)
https://oeis.org/A054554 (SE spoke in your illustration)
The Ulam spirals in your illustrations are oriented differently from the popular representation of Ulam spirals, so I took these well known results and adjusted the offset of n for each formula, so that it works with your Ulam spiral. These adjustments are left as exercises to the reader. ;-)

Well for each row in a matrix we have two diagonal values. To obtain these two values i used two position(x1,y1) and (x2,y2) for main and anti diagonals.
Well I wrote this code:
<?php
function getSpiralDiagonal($spiralArr,$N){
$diagonalValueCount = $N*2;
$xIndexMainDiagonal = 0;
$yIndexMainDiagonal = 0;
$xIndexAntiDiagonal = 0;
$yIndexAntiDiagonal = $N-1;
while($diagonalValueCount > 0){
//checking for same position
if($yIndexMainDiagonal == $yIndexAntiDiagonal){
echo $spiralArr[$xIndexMainDiagonal][$yIndexMainDiagonal].'<br>';
}else{
echo $spiralArr[$xIndexMainDiagonal][$yIndexMainDiagonal].'<br>';
echo $spiralArr[$xIndexAntiDiagonal][$yIndexAntiDiagonal].'<br>';
}
$xIndexMainDiagonal++;
$yIndexMainDiagonal++;
$xIndexAntiDiagonal++;
$yIndexAntiDiagonal--;
$diagonalValueCount -= 2;
}
}
$spiralArr = array(array('7','8','9'),array('6','1','2'),array('5','4','3'));
getSpiralDiagonal($spiralArr,3);
?>

Related

PHP - Find the number of groups given some constraints

Given n = 3 dogs and m = 3 pairs of enemies, a = [1, 2, 3] and b = [3, 3, 1], dog 1 is the enemy of dog 3, and dog 3 is the enemy of dogs 1 and 2. Because 3 is an enemy of both 1 and 2, it must be in its own container. dogs 1 and 2 can be together or separately. There are 4 possible groups: {1, 2} ,{1}, {2}, {3}. Note that the intervals are along the original line of dogs numbered consecutively from 1 to n, i.e. [1, 2, 3] in this case. The dogs cannot be reordered and dogs cannot be skipped, e.g. {2, 1} and {1, 3} are invalid.
So given the following:
case #1:
n = 5
m = 2
a = (1,2)
b = (3,5)
Result is: Total of 11 groups can be formed.
case #2
n = 8
m = 4
a = (2,3,4,3)
b = (8,5,6,4)
Result is: Total of 18 groups can be formed.
Here's my code:
function countSubstrings($n, $a, $b) {
$tokenArr = array();
$x = 1;
while ($x <= $n){
$tokenArr[] = $x;
$x++;
}
$first = 0;
$last = $n - 1;
$outArr = array();
$pointer = 0;
/* generate groups left to right */
for ($i = $first; $i <= $last; $i++) {
$outArr[$pointer][] = $tokenArr[$i];
$tokenString = $tokenArr[$i];
$pointer++;
for ($j = $i + 1; $j <= $last; $j++) {
$tokenString .= $tokenArr[$j];
$outArr[$pointer] = str_split($tokenString);
$pointer++;
}
}
/* find the enemeies */
$intersects = array();
for($k = 0; $k < count($outArr); $k++){
if (count(array_intersect($outArr[$k], $a)) > 1 || count(array_intersect($outArr[$k], $b)) > 1) {
$intersects[] = $outArr[$k];
}
}
/* remove first and last items which are basically equal to $a and $b */
$intersects = array_slice($intersects, 1, -1);
/* remove the enemeies from generated groups */
foreach ($outArr as $keya => $valuea) {
if (in_array($valuea, $intersects)) {
unset($outArr[$keya]);
}
}
return count($outArr);
}
So far my code works in case: #1 but fails on #2.
The intersect logic seems to be incorrect to me as we have to check if the relationship formed by [a , b], for example, [1,2] exists in $outArr or not. Current check of count(array_intersect($outArr[$k], $a)) > 1 does not care about that. It rather checks if any element in $outArr[$k] is present in $a or not.
So, change the current logic from:
/* find the enemeies */
$intersects = array();
for($k = 0; $k < count($outArr); $k++){
if (count(array_intersect($outArr[$k], $a)) > 1 || count(array_intersect($outArr[$k], $b)) > 1) {
$intersects[] = $outArr[$k];
}
}
/* remove first and last items which are basically equal to $a and $b */
$intersects = array_slice($intersects, 1, -1);
to
$intersects = array();
foreach($a as $index => $val1){
$val2 = $b[$index];
foreach($outArr as $current_group){
if(in_array($val1,$current_group) && in_array($val2,$current_group)){ // check if both exist as they are enemies
$intersects[] = $current_group;
}
}
}
Demo: https://3v4l.org/Q2rnP
In the above code, we:
loop through all elements of $a and simultaneously with $b with the help of $index in foreach.
Check if for the current group in $outArr, whether both $a[$index](a.k.a $val1) and $b[$index](a.k.a $val2) exist in the group or not.
If both exist in current group, we put them under intersect as they are enemies. Your rest of the logic is correct.
Efficient Solution:
We have to exploit this line:
A group is defined as an interval (x, y) such that all dogs in the range from x to y form a group.
This means that we need to look at subarrays(as you correctly judged) instead of subsequences.
Now, we loop from 1 to N and if we find a number which has an enemy on the left, we can only form the next groups from that number + 1 onwards. Anything before them can't be included anyway since we are looking at subarrays.
For example, let's assume 5 is an enemy of 3 in a line of 1 to 5 and no other enemies are present. So, group formations would look like below.
Representation:
1 2 3 4 5
-1 -1 5 -1 3
|___|
|___|___|
|___|___|___|
|___|
|___|___|
|___|
|___| // the connection/group (4,5) remains and breaks everything before 4 since 3 is an enemy of 5 and we are looking for subarrays. So everything before 4 is disconnected anyway.
So, our next starting animal/dog to look from is 4.
For each enemy/animal, we maintain the nearest enemy on the left if present. If present, we update the next animal to look from for groups as proved above. In the below code, $prev_start is the variable that maintains the next animal to look from.
In order to get nearest enemy on the left for each animal, we preprocess the enemy details as follows:
Preprocessing:
$enemies = array_combine(range(1,$n),array_fill(0,$n,-1)); // nothing tricky, just generates an array filled with sequential numbers as keys and sets it's value as -1
foreach($a as $index => $enemy_1){
$enemy_2 = $b[$index];
if($enemy_1 < $enemy_2){
$enemies[$enemy_2] = max($enemies[$enemy_2],$enemy_1);
}else if($enemy_2 < $enemy_1){
$enemies[$enemy_1] = max($enemies[$enemy_1],$enemy_2);
}
}
Computation:
$prev_start = 1;
$count = 0;
for($i=1;$i<=$n;++$i){
if($enemies[$i] !== -1){
$prev_start = max($enemies[$i] + 1,$prev_start);
}
$count += ($i - $prev_start + 1);
}
Since we preprocessed enemy details, we update $prev_start accordingly from where we have to start counting for groups again.
$count += ($i - $prev_start + 1); simply counts the number of groups(subarrays) to consider for counting.
Time complexity: O(m + n) where m is number of pairs and n is the number of dogs/animals.
Space complexity: O(n) where n is the number of dogs/animals.
Full Code:
<?php
function countSubarrays($n, $a, $b) {
$enemies = array_combine(range(1,$n),array_fill(0,$n,-1)); // nothing tricky, just generates an array filled with sequential numbers as keys and sets it's value as -1
foreach($a as $index => $enemy_1){
$enemy_2 = $b[$index];
if($enemy_1 < $enemy_2){
$enemies[$enemy_2] = max($enemies[$enemy_2],$enemy_1);
}else if($enemy_2 < $enemy_1){
$enemies[$enemy_1] = max($enemies[$enemy_1],$enemy_2);
}
}
$prev_start = 1;
$count = 0;
for($i=1;$i<=$n;++$i){
if($enemies[$i] !== -1){
$prev_start = max($enemies[$i] + 1,$prev_start);
}
$count += ($i - $prev_start + 1);
}
return $count;
}
Demo: https://3v4l.org/1W26C

Algorithm for calculating number of fitting boxes

I've a client selling wine bottles. He uses boxes with space for 6 bottles, 12 bottles, 18 bottles and 21 bottles. But he only wants to accept orders which fit exactly into these boxes. There must not be any empty space inside.
E.g.
33 is ok: 1x21 and 2x6
48 is ok: 2x21 and 1x6 or 4x12
26 or 35 or 61 are not ok
For my first try was an straight simple way. I produce an array containing a lot of valid numbers, remove duplicates and order them.
$numbers = [];
$end = (int) $bottles/6 + 1;
for ($i=1; $i<=$end; $i++) {
$numbers[] = $i * 6;
$numbers[] = $i * 21;
$numbers[] = $i * 21 + 6;
$numbers[] = $i * 21 + 6 + 6;
$numbers[] = $i * 21 + 6 + 6 + 6;
}
$numbers = array_unique($numbers);
sort($numbers);
It looks like this:
Array
(
[0] => 6
[1] => 12
[2] => 18
[3] => 21
[4] => 24
[5] => 27
[6] => 30
[7] => 33
[8] => 36
[9] => 39
[10] => 42
[11] => 48
[12] => 54
[13] => 60
[14] => 63
....
I can check against my list. ok, fine!
But I want to make a "perfekt" solution fitting for all possible numbers, e.g. I want to know if 123456 is possible. You see, that the array must be very huge for getting this :-)
I tried an equation with 2 unknowns. Why only 2? Because 18 and 12 can be divided by 6. So my approch was:
bottles = 6a + 21b
"a" and "b" must be integer values and may contain zero. "bottles" is an integer value, too. I transformed it to:
bottles / 6 - 3,5b = a
But this doesn't help me to make a good algorithm... I think I'm on the right way, but how can I solve this quite elegant? Where are the algebra gurus? ;-)
To expand on maraca's comment, we're trying to solve the equation x = 6a + 21b over nonnegative integers. Since 6 and 21 are divisible by 3 (the greatest common divisor of 6 and 21), it is necessary that x is divisible by 3. Moreover, if x is less than 21, then it is necessary that x is divisible by 6.
Conversely, if x is divisible by 6, we can set a = x/6 and b = 0. If x is an odd multiple of 3, then x - 21 is divisible by 6; if x is at least 21, we can set a = (x - 21)/6 and b = 1. Every multiple of 3 is either odd or even (and hence divisible by 6), so this proves maraca's equivalence claim.
I found #vivek_23's comment challenging so I figured I would give it a try.
This code will optimize the amount to the smallest number of boxes to fill the order.
It does so by first trying with 21 boxes and if the result is not %6 then it will loop backwards to if it gets a sum that is %6 and split up the rest.
// 99 is challenging since 99 can be divided on 21 to 4, but the remainder is not % 6 (15).
// However the remainder 15 + 21 = 36 which is % 6.
// Meaning the "correct" output should be 3 x 21 + 2 x 18 = 99
$order = 99;
$b = [21 => 0, 18 => 0, 12 => 0, 6 => 0];
// number of 21 boxes possible
if($order >= 21){
$b[21] = floor($order/21);
$order -= $b[21]*21;
}
// if the remainder needs to be modified to be divisible on 6
while($order % 6 != 0){
if($b[21] > 0){
// remove one box of 21 and add the bottles back to the remainder
$order += 21;
$b[21]--;
}else{
// if we run out of 21 boxes then the order is not possible.
echo "order not possible";
exit;
}
}
// split up the remainder on 18/12/6 boxes and remove empty boxes
$b = array_filter(split_up($b, $order));
var_dump($b);
function split_up($b, $order){
// number of 18 boxes possible
if($order >= 18){
$b[18] = floor($order/18);
$order -= $b[18]*18;
}
// number of 12 boxes possible
if($order >= 12){
$b[12] = floor($order/12);
$order -= $b[12]*12;
}
// number of 6 boxes possible
if($order >= 6){
$b[6] = floor($order/6);
$order -= $b[6]*6;
}
return $b;
}
https://3v4l.org/EM9EF
You can reduce this homework to some simpler logic with 3 valid cases:
Multiples of 21.
Multiples of 6.
A combination of the above.
Eg:
function divide_order($q) {
$result['total'] = $q;
// find the largest multiple of 21 whose remainder is divisible by 6
for( $i=intdiv($q,21); $i>=0; $i-- ) {
if( ($q - $i * 21) % 6 == 0 ) {
$result += [
'q_21' => $i,
'q_6' => ( $q - $i * 21 ) / 6
];
break;
}
}
if( count($result) == 1 ) {
$result['err'] = true;
}
return $result;
}
var_dump(
array_map('divide_order', [99, 123456])
);
Output:
array(2) {
[0]=>
array(3) {
["total"]=>
int(99)
["q_21"]=>
int(3)
["q_6"]=>
int(6)
}
[1]=>
array(3) {
["total"]=>
int(123456)
["q_21"]=>
int(5878)
["q_6"]=>
int(3)
}
}
Then you can apply some simple logic to reduce multiple boxes of 6 into boxes of 12 or 18.
function winePacking(int $bottles): bool {
return ($bottles % 6 == 0 || ($bottles % 21) % 3 == 0);
}
https://3v4l.org/bTQHe
Logic Behind the code:
You're working with simple numbers, 6,12,18 can all be covered by mod 6, being that 6 goes into all 3 of those numbers. 21 we can just check a mod 21, and if it's somewhere in between then it's mod 21 mod 6.
Simple as that with those numbers.
What if you do something like so:
function boxes($number) {
if($number >= 21) {
$boxesOfTwentyOne = intval($number / 21);
$remainderOfTwetyOne = floor($number % 21);
if($remainderOfTwetyOne === 0.0) {
return $boxesOfTwentyOne . ' box(es) of 21';
}
$boxesOfTwentyOne = $boxesOfTwentyOne - 1;
$number >= 42 ? $textTwentyOne = $boxesOfTwentyOne . ' boxes of 21, ' : $textTwentyOne = '1 box of 21, ';
$sixesBoxes = floor($number % 21) + 21;
switch (true) {
case ($sixesBoxes == 24):
if($number >= 42) {
return $textTwentyOne . '1 box of 18 and 1 box of 6';
}
return '1 box of 18 and 1 box of 6';
break;
case ($sixesBoxes == 27):
return $boxesOfTwentyOne + 1 . ' box(es) of 21 and 1 box of 6';
break;
case ($sixesBoxes == 30):
if($number >= 42) {
return $textTwentyOne . '1 box of 18 and 1 box of 12';
}
return '1 box of 18 and 1 box of 12';
break;
case ($sixesBoxes == 33):
return $boxesOfTwentyOne + 1 . ' box(es) of 21 and 1 box of 12';
break;
case ($sixesBoxes == 36):
if($number >= 42) {
return $textTwentyOne . '2 boxes of 18';
}
return '2 boxes of 18';
break;
case ($sixesBoxes == 39):
return $boxesOfTwentyOne + 1 . ' box(es) of 21 and 1 box of 18';
break;
default:
return 'Not possible!';
break;
}
} else {
switch (true) {
case ($number == 6):
return '1 box of 6';
break;
case ($number == 12):
return '1 box of 12';
break;
case ($number == 18):
return '1 box of 18';
break;
default:
return 'Not possible!';
break;
}
}
}
EDIT: I have updated my answer, and now I think it is working properly. At least, it passed in all the tests I've made here.
This is actually array items summing up to a target where repetition is allowed problem.
Since in many cases multiple box configurations will come up, you may chose to use the shortest boxes sub list or may be the boxes sublist with the available boxes at hand in real life.
Sorry my PHP is rusty... the below algorithm is in JS but you may simply adapt it to PHP. Of course you may freely change your box sizes to accomodate any number of bottles. So for the given boxes and target 87 we get in total 20 different solutions like
[12,18,18,18,21], [12,12,21,21,21] ... [6,6,6,6,6,6,6,6,6,6,6,21]
function items2T([n,...ns],t){cnt++ //remove cnt in production code
var c = ~~(t/n);
return ns.length ? Array(c+1).fill()
.reduce((r,_,i) => r.concat(items2T(ns, t-n*i).map(s => Array(i).fill(n).concat(s))),[])
: t % n ? []
: [Array(c).fill(n)];
};
var cnt = 0, result;
console.time("combos");
result = items2T([6,12,18,21], 87)
console.timeEnd("combos");
console.log(result);
console.log(`${result.length} many unique ways to sum up to 87
and ${cnt} recursive calls are performed`);
The code is taken from a previous answer of mine.

Prefix sum find max collect value by moving in array in m moves

I am trying to implement the prefix sum logic in php codility.
Here is the problem that I'm trying to solve:
You are given a non-empty, zero-indexed array A of n (1 ¬ n ¬ 100 000)
integers a0, a1, . . . , an−1 (0 ¬ ai ¬ 1 000). This array represents
number of mushrooms growing on the consecutive spots along a road. You
are also given integers k and m (0 ¬ k, m < n). A mushroom picker is
at spot number k on the road and should perform m moves. In one move
she moves to an adjacent spot. She collects all the mushrooms growing
on spots she visits. The goal is to calculate the maximum number of
mushrooms that the mushroom picker can collect in m moves. For
example, consider array A such that: [2, 3, 7, 5, 1, 3, 9]
The mushroom picker starts at spot k = 4 and should perform m = 6
moves. She might move to spots 3, 2, 3, 4, 5, 6 and thereby collect 1
+ 5 + 7 + 3 + 9 = 25 mushrooms. This is the maximal number of mushrooms she can collect.
Here's my code in php:
$P = [2, 3, 7, 5, 1, 3, 9]; // mushroom count per position
$k = 4; // start position of picker
$m = 6; // moves allowed
$n = count($P);
$result = 0;
$pref = $this->getPrefixSum($P);
$leftBoundary = min($m, $k);
for ($i=0; $i < $leftBoundary; $i++) {
$leftPos = $k - $i;
$rightPos = min($n - 1, max($k, $k + $m - 2 * $i));
$result = max($result, $pref[$rightPos] - $pref[$leftPos]);
}
$rightBoundary = min($m + 1, $n - $k);
for ($i=0; $i < $rightBoundary ; $i++) {
$rightPos = $k + $i;
$leftPos = max(0, min($k, $k - ($m - 2 * $i)));
$result = max($result, $pref[$rightPos] - $pref[$leftPos]);
}
function getPrefixSum($A)
{
$prefixSums = array();
$prefixSums[0] = $A[0];
for ($i=1; $i < count($A); $i++) {
$prefixSums[$i] = $prefixSums[$i - 1] + $A[$i];
}
return $prefixSums;
}
Unfortunately, I am getting a result of 19 only (Expected answer was 25). Do you guys have any idea if I'm missing anything? Any help would be appreciated.
There are multiple mistakes translating the code from the example. The primary problem is that your prefixSum function is producing an array with one less index than the example code. Here's a comparison:
# them
[0, 2, 5, 12, 17, 18, 21, 30]
# you
Array
(
[0] => 2
[1] => 5
[2] => 12
[3] => 17
[4] => 18
[5] => 21
[6] => 30
)
Otherwise, you've omitted operations that they included, so I'll highlight them in the working code below:
function getPrefixSum($A) {
$prefixSums = [0];
# ^^^
for ($i = 1; $i < count($A) + 1; $i++) {
# ^^^^
$prefixSums[$i] = $prefixSums[$i-1] + $A[$i-1];
# ^^
}
return $prefixSums;
}
$P = [2, 3, 7, 5, 1, 3, 9]; // mushroom count per position
$k = 4; // start position of picker
$m = 6; // moves allowed
$n = count($P);
$result = 0;
$pref = getPrefixSum($P);
$leftBoundary = min($m, $k) + 1;
# ^^^^
for ($i = 0; $i < $leftBoundary; $i++) {
$leftPos = $k - $i;
$rightPos = min($n - 1, max($k, $k + $m - 2 * $i));
$result = max($result, $pref[$rightPos+1] - $pref[$leftPos]);
# ^^
}
$rightBoundary = min($m + 1, $n - $k);
for ($i = 0; $i < $rightBoundary; $i++) {
$rightPos = $k + $i;
$leftPos = max(0, min($k, $k - ($m - 2 * $i)));
$result = max($result, $pref[$rightPos+1] - $pref[$leftPos]);
# ^^
}
echo "$result\n";
Output:
25
Try it out.

Codility Invalid result type, int expected with PHP

Could you tell why Codility tell me the next error, please?
Running solution... Compilation successful.
Example test: [-1, 3, -4, 5, 1, -6, 2, 1] Output (stderr): Invalid
result type, int expected. RUNTIME ERROR (tested program terminated
unexpectedly)
Detected some errors.
My solution was wrote on PHP.
function solution($A) {
$N = count($A);
$Ps = array();
foreach ( $A as $KeyP => $P ) {
$sum = 0;
if ( $KeyP == 0 ) {
for ( $x = 1; $x < $N; $x++ ) {
$sum += $A[$x];
}
if ( $sum == $P ) {
$Ps[] = $KeyP;
}
}
else {
if ( ($KeyP+1) == $N ) {
for ( $z = 0; $z < $KeyP; $z++) :
$sum += $A[$z];
endfor;
if ( ( $sum >= 0 ) AND ( $sum < $N ) ) {
$Ps[] = $KeyP;
}
}
else {
$sum1 = 0;
$sum2 = 0;
for ( $z = 0; $z < $KeyP; $z++ ) :
$sum1 += $A[$z];
endfor;
for ( $y = ( $KeyP+1 ); $y <= ($N-1); $y++ ) :
$sum2 += $A[$y];
endfor;
if ( $sum1 == $sum2 ) {
if ( $sum1 < $N ) {
$Ps[] = $KeyP;
}
}
}
}
}
return ( count($Ps) <= 0 ) ? -1: $Ps;
}
The output of my function given the next array has been:
array(-1, 3, -4, 5, 1, -6, 2, 1);
Ouput
Array ( [0] => 1 [1] => 3 [2] => 7 )
It's just like they request me in the task but Codility show me all those errors.
The demo task is below:
This is a demo task.
A zero-indexed array A consisting of N integers is given. An
equilibrium index of this array is any integer P such that 0 ≤ P < N
and the sum of elements of lower indices is equal to the sum of
elements of higher indices, i.e. A[0] + A[1] + ... + A[P−1] = A[P+1]
+ ... + A[N−2] + A[N−1]. Sum of zero elements is assumed to be equal to 0. This can happen if P = 0 or if P = N−1.
For example, consider the following array A consisting of N = 8
elements:
A[0] => -1
A[1] => 3
A[2] => -4
A[3] => 5
A[4] => 1
A[5] => -6
A[6] => 2
A[7] => 1
P = 1 is an equilibrium index of this array, because:
A[0] = −1 = A[2] + A[3] + A[4] + A[5] + A[6] + A[7] P = 3 is an
equilibrium index of this array, because:
A[0] + A[1] + A[2] = −2 = A[4] + A[5] + A[6] + A[7] P = 7 is also an
equilibrium index, because:
A[0] + A[1] + A[2] + A[3] + A[4] + A[5] + A[6] = 0 and there are no
elements with indices greater than 7.
P = 8 is not an equilibrium index, because it does not fulfill the
condition 0 ≤ P < N.
Write a function:
function solution($A);
that, given a zero-indexed array A consisting of N integers, returns
any of its equilibrium indices. The function should return −1 if no
equilibrium index exists.
For example, given array A shown above, the function may return 1, 3
or 7, as explained above.
Assume that:
N is an integer within the range [0..100,000]; each element of array A
is an integer within the range [−2,147,483,648..2,147,483,647].
Complexity:
expected worst-case time complexity is O(N); expected worst-case space
complexity is O(N), beyond input storage (not counting the storage
required for input arguments). Elements of input arrays can be
modified.
Thank you.
for the Codility error please check this post :
https://stackoverflow.com/a/19804284/4369087
Try this one it's more readable, in this solution I'm making the code more readable by introducing two functions.
sumRight(), sumLeft() in which I use built in php functions.
array_sum() : Calculate the sum of values in an array :
it returns the sum of values as an integer or float; 0 if the array is empty.
array_slice(): Extract a slice of the array: array_slice() returns
the sequence of elements from the array as specified by the
offset and length parameters.
So every time we loop over the array we calculate to sum of the right and left slice of the array from the given position $i :
<?php
function solution(array $a)
{
$result = [];
$count = count($a);
for($i = 0; $i < $count; $i++) {
if(sumLeft($a, $i-1) === sumRight($a, $i+1)) {
$result[] = $i;
}
}
return count($result) ? $result : -1;
}
function sumRight(array $a, float $position): float
{
return array_sum(array_slice($a, $position));;
}
function sumLeft(array $a, float $position): float
{
return array_sum(array_slice($a, 0, $position + 1));
}
echo "<pre>";
print_r(solution([-1, 3, -4, 5, 1, -6, 2, 1]));
output:
Array
(
[0] => 1
[1] => 3
[2] => 7
)
The Tape Equilibrium questions is worded super confusingly.
P is the values of the array $A
P values must be greater than 0 and less than N
N is the total number of indexes in the array $A
$A is a non-empty array consisting of N integers
The part "0 < P < N" doesn't make sense. If N is the integers in non-empty array A then N should be the length of the array and data such as [100,200] should not work because P=100 & P=200, both of which are greater than 2.
My opinion is that the explanation is poorly worded. However, the example is clear, so the solution is relatively straight forward if you can get past the opening text.
A non-empty array A consisting of N integers is given. Array A represents numbers on a tape.
Any integer P, such that 0 < P < N, splits this tape into two
non-empty parts: A[0], A[1], ..., A[P − 1] and A[P], A[P + 1], ...,
A[N − 1].
The difference between the two parts is the value of: |(A[0] + A[1] +
... + A[P − 1]) − (A[P] + A[P + 1] + ... + A[N − 1])|
In other words, it is the absolute difference between the sum of the
first part and the sum of the second part.
The part that stumped me initially was that I was returning NULL for erroneous array input and receiving a Codility error that NULL was returned when it was expecting an INT. I thought, how can I return an INT when there is no solution for bad data? Turns out, they want you to return a zero.
Here is my 100/100 PHP solution:
function solution($A) {
if ( empty($A) ) {
return 0;
}
$count = count($A);
if ($count == 1) {
return $A[0];
}
for ($i=0, $max_position = $count - 1; $i<$max_position; $i++) {
if (!is_int($A[$i])) {
return;
}
if ($i == 0) {
$left = $A[0];
$right = array_sum($A) - $left;
$min = abs($left - $right);
}
else {
$left += $A[$i];
$right -= $A[$i];
$min = min([$min, abs($left - $right)]);
}
}
return $min;
}
Three things to take note of.
IF $A is empty then return an integer (0) because NULL results in a
codility error
If $A only has one index then return an integer (0) because you cannot split a single index into two equal parts like they want and
NULL results in a codility error
Apparently, invalid non-integer values are not tested - you can see where I checked for !is_int($A[$i]) and then a "return;" that
should have resulted in a NULL (and an error about the NULL not
being an int), but I did not.
Assume that: N is an integer within the range [0..100,000]; each element of array A is an integer within the range [−2,147,483,648..2,147,483,647].
This means I don't see any reason to check if an element isInt() as suggested by #Floyd.
For best efficiency, always break/return as early as possible.
There is no reason to iterate on the first or last index in the array because splitting the array on the first element will result in nothing on the "left side" and splitting on the last element will result in nothing on the "right side". For this reason, start iterating from 1 and only iterate while $i is less than array count - 1.
I struggled to follow the asker's coding attempt, but it seems likely that iterating/evaluating the bookend elements was to blame.
Code -- not actually tested on Codility: (Demo)
$test = [-1, 3, -4, 5, 1, -6, 2, 1];
function solution(array $array): int {
for ($i = 1, $count = count($array) - 1; $i < $count; ++$i) {
if (array_sum(array_slice($array, 0, $i)) === array_sum(array_slice($array, $i + 1))) {
return $i;
}
}
return -1;
}
echo solution($test);

Calculate average percentage difference in a php array

Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 3
[6] => 1
)
I was wondering how one would go about working out the average percentage difference between the current value in an array and the next value. If the next value were a larger one, it would perform like so. (ie keys [0]-[1] 1/2 * 100 = 50). If it were a smaller value it would perform like so. (ie keys [4]-[5] = 3/5 * 100 = -60).
The following will represent what I am aiming to do with these percentage calculations.
1/2 * 100
2/3 * 100
3/4 * 100
4/5 * 100
3/5 * 100 (negative)
1/3 * 100 (negative)
Total : total/count
This will iterate through the list and then work out the average from the count. I have looked into splitting arrays but don't see how else I could do this.
$count = count($num);
foreach ($num as $value) {
array_chunk($num, 1);
if($value<$value){
$total1 = $total1 + ($value/$value)*100;
}
if($value>$value){
$total2 = $total2 + ($value/$value)*100;
}
}
$average = (($total1-$total2)/$count);
print($average);
I understand the above code is incorrect, but I hope it reveals where I am getting at with this.
Any help would be greatly appreciated.
You don't want to use foreach as you'll always be needing two array elements. Note that this snippet does not protect you from 0 values. These will make your script fail.
$num = array(1, 2, 3, 4, 5, 3, 1);
$total = 0;
// The number of percent changes is one less than
// the size of your array.
$count = count($num) - 1;
// Array indexes start at 0
for ($i = 0; $i < $count; $i++) {
// The current number is $num[$i], and the
// next is $num[$i + 1]; knowing that it's
// pretty easy to compare them.
if ($num[$i] < $num[$i + 1]) {
$total += (100 * $num[$i] / $num[$i + 1]);
}
else {
$total += (-100 * $num[$i + 1] / $num[$i]);
};
};
echo ($total / $count);

Categories