生成数组的所有版本的算法,可能的权重分配为0-100

时间:2017-12-03 14:30:31

标签: php algorithm

我有一个以下数组(php):

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

我需要创建此数组的所有可能版本,将 0-100 权重称为每个item['weight'],步长为N.

我不知道如何调用这类问题。它不是排列/组合。

让我们说N是10,我的目标是:

[
    [
     [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]
    ]
]

同一级别的数组中的item['weights']的总和始终为100(或0.1)。在父母数组中,我为所有可能的权重组合从10到100为id = x。

2 个答案:

答案 0 :(得分:1)

这个问题有时被描述为将相同的球分配到不同的箱中。你没有准确地指出你的问题,所以我会在这里猜测,但逻辑是相同的。

我假设你将b = N /步球分配到4个箱子里。

连续想想球,然后使用3个球将球分成4个箱: 的 * | || *****

如果N = 10并且您正在分配100个点,则上面的示例是相同的是30,20,0,50。如果不允许使用零,则可以减少分配的数量4 * b并假设每个bin以N / step开头(所以你要分配剩余点)。

执行此操作的方法是选择(球+箱子 - 1,箱子 - 1)。

答案 1 :(得分:0)

Theres可能是一种更好的方式,但继承了我的尝试:

$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);

工作演示here

修改

很抱歉,我意识到我的代码解释并不完全清楚,而且我将它简化得过于简单,以便于理解。

在您的示例中,第一个数组将包含(10,10,10,70)。为了简单起见,我将所有内容除以10进行计算,然后在得到结果后再乘以10,这样(10,10,10,70)的数组变为(1,1,1,7)。然后你的最终数组将是(70,10,10,10),它将成为(7,1,1,1)

我的方法是首先创建一个包含这四个数字的每个组合的数组,我分两步完成。

这一行$array=range(1117,7777);创建一个像(1117, 1118, 1119 ... 7775, 7776, 7777)这样的数组(我的数字范围应该是1117 - 7111而不是1117-7777)。

str_split($v, 1)应用于循环中的每个值会将数组中的每个4位数字拆分为包含4个单个数字的另一个数组,因此1117将变为(1, 1, 1, 7)

由于您的每件商品的重量不能低于10或高于70,我们会使用(preg_match('/[890]/',$v) === 0)跳过任何位于其中0,8或9的数组,然后array_sum(str_split($v, 1)) === 10)加起来数组中的四个数字,只返回总数为10的数组(你想要的总数为100,但我之前除以10)。

array_map将函数应用于数组中的每个元素。在我的例子中,函数将每个值乘以10,以撤消之前除以10的事实。

当你说可以改变步骤时,你能给我一些其他值的例子以及你想要的输出吗?

如果你想要一个完全不同的方法并且使用mysql不是问题,那么这也有效:

创建一个包含单行的新表。插入您需要检查的所有值

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

然后你的php看起来像这样

$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);