0

I have a following array (php):

[
 [id=>1,weight=]
 [id=>2,weight=]
 [id=>3,weight=]
 [id=>4,weight=]
]

I need to create all possible versions of this array asigning 0-100 weight to each item['weight'] with a step of N.

I don't know how this type of problems are called. It is NOT permutation/combination.

Lets say N is 10, I am aiming to get:

[
    [
     [id=>1,weight=10]
     [id=>2,weight=10]
     [id=>3,weight=10]
     [id=>4,weight=70]
    ]
    [
     [id=>1,weight=10]
     [id=>2,weight=10]
     [id=>3,weight=20]
     [id=>4,weight=60]
    ]
    [
     [id=>1,weight=10]
     [id=>2,weight=10]
     [id=>3,weight=30]
     [id=>4,weight=50]
    ]
    [
     [id=>1,weight=10]
     [id=>2,weight=10]
     [id=>3,weight=40]
     [id=>4,weight=40]
    ]
    ...all possible combination of weights for id=x.
    [
     [id=>1,weight=70]
     [id=>2,weight=10]
     [id=>3,weight=10]
     [id=>4,weight=10]
    ]
]

Sum of 4 item['weights'] in array on same level is always 100 (or 0.1). And inside parent array I've all possible combinations of weights from 10-100 for id=x.

5
  • So each id should have a subarray of all values from 10-100? Or a complete set o new arrays for each new combination? Commented Dec 3, 2017 at 14:34
  • 1
    10 10 10 70 to 70 10 10 10 does not explain range of 0 to 100 with step of 10.! please take a good example. Commented Dec 3, 2017 at 14:43
  • @Andreas new arrays for each combination. Commented Dec 3, 2017 at 14:55
  • @JayJoshi I will try to give a better example Commented Dec 3, 2017 at 15:08
  • @Andreas I rephrased my problem, to the best of my knowledge, is it clearer? Commented Dec 3, 2017 at 17:54

2 Answers 2

1

This problem is sometimes described as allocating identical balls into distinct bins. You didn't specify your problem exactly, so I'll take a guess here but the logic will be identical.

I'll assume you're distributing b = N/step balls into 4 bins.

Think of the balls all in a row, and then using 3 bars to separate the balls into 4 bins: *|||*****.

If N=10 and you're distributing 100 points, the above example is the same is 30, 20, 0, 50. If zeroes aren't allowed, you can reduce the amount you're distributing by 4*b and assume each bin starts out with N/step in it (so you're distributing the leftover points).

The number of ways to do this is choose(balls + bins - 1, bins - 1).

Sign up to request clarification or add additional context in comments.

1 Comment

Hi, thank you for looking in, I've updated my question. Is it still relevant?
0

Theres probably a better way, but heres my attempt:

$result=array(); // Empty array for your result
$array=range(1117,7777); // Make an array with every number between 1117 and 7777
foreach ($array as $k=>$v) {  // Loop through numbers
    if ((preg_match('/[890]/',$v) === 0) && (array_sum(str_split($v, 1)) === 10)) { 
        // If number does not contain 8,9 or 0 and sum of all 4 numbers is 10
        // Apply function to multiply each number by 10 and add to result array
        $result[] = array_map("magnitude", str_split($v, 1));
    }
}
function magnitude($val) { // function to multiply by 10 for array map
    return($val * 10);
}
print_r($result);

Working demo here

EDIT

Sorry I realised my code explanation isn't totally clear and I condensed it all a bit too much to make it easy to follow.

In your example the first array would contain (10,10,10,70). For the sake of simplicity I divided everything by 10 for the calculations and then just multiplied by 10 once I had a result, so your array of (10,10,10,70) becomes (1,1,1,7). Then your final array would be (70,10,10,10) which would become (7,1,1,1).

My approach was to first to create an array containing every combination of these four numbers, which I did in two steps.

This line $array=range(1117,7777); creates an array like this (1117, 1118, 1119 ... 7775, 7776, 7777) (My number range should really have been 1117 - 7111 instead of 1117-7777).

Applying str_split($v, 1) to each value in the loop splits each 4 digit number in the array into another array conatining 4 single digit numbers, so 1117 will become (1, 1, 1, 7) etc

As each of your items can't have a weight below 10 or above 70 we use (preg_match('/[890]/',$v) === 0) to skip any arrays which have 0,8 or 9 in them anywhere, then array_sum(str_split($v, 1)) === 10) adds up the four digits in the array and only returns arrays which total 10 (you wanted ones which total 100, but I divided by 10 earlier).

array_map applies a function to each element in an array. In my example the function multiplies each value by 10, to undo the fact I divided by 10 earlier.

When you say is it possible to alter steps, can you give me a couple of examples of other values and the output you want for them?

If you want a totally different approach and using mysql isn't a problem then this also works:

Create a new table with a single row. Insert all the values you need to check

INSERT INTO `numbers` (`number`) VALUES
(10),
(20),
(30),
(40),
(50),
(60),
(70);

Then your php looks like this

$result=array();
try {
    $dbh = new PDO('mysql:host=aaaaa;dbname=bbb', 'ccc', 'dddd');
    foreach($dbh->query('SELECT *
FROM numbers a
CROSS JOIN   // A cross join returns the cartesian product of rows
numbers b    // so every row with every combination of the other rows
CROSS JOIN
numbers c
CROSS JOIN
numbers d
ON
a.number = b.number OR a.number != b.number') as $row) {
        if (($row[0] + $row[1] + $row[2] + $row[3]) === 100) {
            $result[] = $row;
        }
    }
    $dbh = null;
} catch (PDOException $e) {
    print "Error!: " . $e->getMessage() . "<br/>";
    die();
}
print_r($result);

5 Comments

I don’t think you needed to include all 84 items of the result, and certainly not formatted in such a verbose form.
Not every browser (or the SO app) displays it that way; even listing each of the 84 elements as just a line w/ the 4 numbers would be a big improvement.
@Scott Hunter Fair point, I've switched it out for a demo link
Hey, how are you getting these numbers? 1117-7777 and do you think it is possible to alter steps? e.g. change it to 5 6 7 or 10 like you have.
Updated my answer in response to your follow up

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.