4

i am trying to loop through a two-dimensional array and take a sum of the combinations of the columns automatically.

Suppose i have an array called $a with 4 columns:0,1,2,3,

$a=array();
$a[0][0]=1;
$a[0][1]=3;
$a[0][2]=5;

$a[1][0]=10;
$a[1][1]=2;  
$a[1][2]=5;
$a[1][3]=7;

$a[2][0]=9;
$a[2][1]=8;  
$a[2][2]=9;
$a[2][3]=8;

$a[3][0]=9;
$a[3][1]=8;  
$a[3][2]=9;
$a[3][3]=8;
$a[3][4]=1;

And i am trying to sum over all of the combinations of the columns like sum(0,0;1,0;2;0,3;0) etc using this code

for($i=0;$i<count($a[0]);$i++){
for($l=0;$l<count($a[1]);$l++){ 
for($s=0;$s<count($a[2]);$s++){ 
for($m=0;$m<count($a[3]);$m++){
 echo $sum[]= $a[0][$i]+$a[1][$l]+$a[2][$s]+$a[3][$m]; 
 echo $sum;
  echo "<br>";
   } 
   } 
 }
 }

 ?>

And the code works, the problem is that i am doing these for loops manually, there must be some way in which i can simplify this by somehow inserting the count of the number of columns?

I tried something like

$numberofcolumns=4; 

for($n=0;$n<$numberofcolumns;$n++){
for($i=0;$i<count($a[$n]);$i++){
for($m=0;$m<count($a[$n+1]);$m++){
echo $sums[]= $a[$n][$i]+$a[$n+1][$m];
}
}
}

but that doesn't work, there must be some way to simplify the for loops so that i don't have to manually type in the for loops each column

anybody have a clue?

sad electron
  • 97
  • 4
  • 10

4 Answers4

1

You can use RecursiveIteratorIterator

Try

$a = array ();
$a [0] [0] = 1;
$a [0] [1] = 3;
$a [0] [2] = 5;

$a [1] [0] = 10;
$a [1] [1] = 2;
$a [1] [2] = 5;
$a [1] [3] = 7;

$a [2] [0] = 9;
$a [2] [1] = 8;
$a [2] [2] = 9;
$a [2] [3] = 8;

$a [3] [0] = 9;
$a [3] [1] = 8;
$a [3] [2] = 9;
$a [3] [3] = 8;
$a [3] [4] = 1;

$sum = 0;
$array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a ) );
foreach ( $array as $key => $value ) {
        $sum += $value;
}
echo $sum;

Output

 102

Use $array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a[1] ) ); to get sum of each section ...

Baba
  • 94,024
  • 28
  • 166
  • 217
  • Thanks, that works for the first combination, so do i just loop it to get all of the combinations now? – sad electron May 06 '12 at 18:25
  • like the first row is 1+10+9+9, then i sum over 1+10+9+8([0][0][1][0][2][0][3][1]), then i sum over 1+10+9+9([0][0][1][0][2][0][3][3]), then i sum over all of the possible combinations between the columns basically. That's what those for loops do – sad electron May 06 '12 at 18:36
  • use `$array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a[1] ) );` to get sum of each section – Baba May 06 '12 at 18:43
  • Ye, that does sum the columns, but not the rows haha. I am trying to sum the combination of the rows, not 1+3+5(the 0 column), but 1+10+9+9 (the combination of the columns) and i am trying to make it loop through all of the columns no matter how many i have (right now i have 4 columns). If you run the for loops that i have in php, you will see what i am trying to do. – sad electron May 06 '12 at 18:51
1

You can use recursion, or just straight nested loops for this, but when working with combinations or permutations, the total number of possibilities can explode and become a huge number, consuming lots of memory to the point where you just cant run the code. Using an iterator is a nice way to trade cpu efficiency for memory efficiency. Here's an iterator I wrote.

class CartesianProductIterator implements Iterator {
    protected $iterators;

    function __construct(array $iters) {
        $this->iterators = $iters;
    }

    function rewind() {
        foreach ($this->iterators as $it) {
            $it->rewind();
        }
    }

    function current() {
        $values = array();
        foreach ($this->iterators as $it) {
            $values[] = $it->current();
        }
        return $values;
    }

    function key() {
        return null;
    }

    function next() {
        /*      
        loop them in reverse, but exclude first
        why? example, odometer: 55199
        you always check the rightmost digit first to see if incrementing it would roll it over and need to be "rewound" to 0, 
        which causes the digit to the left to increase as well, which may also cause it to roll over as well, and so on...
        looping in reverse operates from right column to the left.
        we dont rewind the first column because if the leftmost column is on its last element and needs to roll over
        then this iterator has reached its end, and so rewind() needs to be explicitly called 
        */
        for ($i = count($this->iterators) - 1; $i > 0; --$i) {
            $it = $this->iterators[$i];
            $it->next();
            if ($it->valid()) {
                // were done advancing because we found a column that didnt roll over
                return;
            } else {
                $it->rewind();
            }
        }

        //if execution reached here, then all of the columns have rolled over, so we must attempt to roll over the left most column
        $this->iterators[0]->next();
    }

    function valid() {
        return $this->iterators[0]->valid();
    }
}

Then use it as

$iterators = array();
foreach ($a as $columnNumber => $values) {
    $iterators[] = new ArrayIterator($values);
}
foreach (new CartesianProductIterator($iterators) as $combo) {
    // combo has 1 value from each of the ArrayIterators we instantiated
    printf("summing %s = %d\n", join('+', $combo), array_sum($combo));
}

heres a demo http://codepad.org/UasdgvWf

goat
  • 31,486
  • 7
  • 73
  • 96
  • Thanks this worked! Just out of curiosity, how would one approach this problem with loops? By the way, thanks for understanding what iwas trying to do, was struggling with this for a while. – sad electron May 06 '12 at 20:55
  • Well, a lot of people would write a recursive function for this, and that wouldn't be too difficult. Any recursive algorithm can be rewritten as an iterative algorithm, and you use the concept of a stack to keep track of the states of all your variables, just like a compiler does for any programming language. The few times I've done it, it was kinda mind bending for me, and I don't really feel like it right now. – goat May 06 '12 at 21:06
0

If I'm understanding you correctly, this function should do the trick:

<?php
function recursive_sum($arr) { 

    $sum = 0;

    foreach($arr as $value) { 
        if(is_array($value)) {
            $sum += recursive_sum($value);
        }
        else { 
            $sum += $value;
        }
    }

    return $sum;
}  
?>

Just call recursive_sum($a) to get the sum of all the values in your array, like this:

<?php
    echo recursive_sum($a);
?>    
Daan
  • 3,403
  • 23
  • 19
  • I'm not trying to sum over all of the columns, i am trying to sum over the combinations where the sum consists of a number from the first column, the second, third, and forth. That's what the for loops do, but i am trying to make it automatic such that no matter how many columns i have, i can still get a sum of all of the different combinations – sad electron May 06 '12 at 18:41
  • I'm sorry, but I'm having some trouble understanding what you're looking for. I tried running your example code (the one you said works), but that gives me some garbage output and a fatal error (PHP 5.3.8). Could you edit your post to add a few examples of the output you're looking for? – Daan May 06 '12 at 18:55
  • for some reason it won't let me edit my post, keep saying my code is not properly spaced. If you start with , it will give you a series of number outputs such as array29, array 28, etc, they are just the sums of the rows of the columns, what i am trying to do is to automate the for loops so that, no matter how many columns i have(currently 4), the code will sum all of the combinations – sad electron May 06 '12 at 19:06
0
<? //PHP 5.4+
$a=[];
$a[0][0]=1;
$a[0][1]=3;
$a[0][2]=5;

$a[1][0]=10;
$a[1][1]=2;  
$a[1][2]=5;
$a[1][3]=7;

$a[2][0]=9;
$a[2][1]=8;  
$a[2][2]=9;
$a[2][3]=8;

$a[3][0]=9;
$a[3][1]=8;  
$a[3][2]=9;
$a[3][3]=8;
$a[3][4]=1;

//This is downright evil, but it works.
eval(\array_reduce(
  \array_reverse(\array_keys($a)),
  static function($eval, $key){
    return "foreach(\$a[$key]as\$i$key)$eval+\$i$key";
  },
  '{echo$sum[]=0') . ';echo"$sum<br/>";}');
?>
Cory Carson
  • 276
  • 1
  • 6