想象一下......我有四个厨师和四个食谱,每个厨师都有每个食谱的分数,今天我希望每个厨师做一道菜(但不应该做两次菜),决定应该基于所有四个人的最佳(最高总分)排列(所以也许一个厨师不会使他个人最好)。
我已将数据放入多维数组中
array(
array (1,2,3,4),
array (35,0,0,0),
array (36,33,1,1),
array (20,20,5,3)
)
每个子数组中的值对与子数组的数量相同(如果有帮助的话)
实际上,子阵列的数量最多会达到8(因此最大烫数= 8!,大约40,000不是8 ^ 8,因为不允许使用许多组合)
如果有帮助
我正在尝试创建第二个数组,根据KEYs输出子数组的最佳(即最高值)可能组合,其中每个子数组只能使用一个
- 所以这里每个子阵列[0] [1] [2] [3]每个排列使用一次 并且每个subarrayKey [0] [1] [2] [3]将使用一次permutaion,在我的实际问题中我正在使用相关的数组,但这是这个问题的额外内容.--
因此该示例将创建一个数组 newArray(35,33,5,4)//请注意[2] [0]未使用
理想我宁愿不制作ALL烫发,但是,SOMEHOW,丢弃许多明显不合适的组合。
关于如何开始的任何想法?我会接受伪代码。
有关笛卡尔积的SO的示例,请参阅PHP 2D Array output all combinations
编辑2 更多关于如何提高笛卡尔积的效率,以及为什么它必须是特定于案例的,如果你想看看是否可以偷工减料(有风险)Efficient Cartesian Product algorithm
答案 0 :(得分:2)
道歉,但这将是一个比代码更多的逻辑布局......
我不太清楚阵列(1,2,3,4)是第一道菜还是第一道菜的得分,但我可能会使用这样的阵列
$array[$cook_id][$dish_number] = $score;
asort()每个数组使$ array [$ cook_id] = array($ lowest_scored_dish,...,$ highest);
考虑一个特定厨师的加权偏好,使菜肴成为最佳菜肴与其他菜肴得分之间的差异。
作为一个非常简单的例子,烹饪a,b,c和菜肴0,1,2
$array['a'] = array(0=>100, 1=>50, 2=>0); // cook a prefers 0 over 1 with weight 50, over 2 with weight 100
$array['b'] = array(0=>100, 1=>100, 2=>50); // cook b prefers 0,1 over 2 with weight 50
$array['c'] = array(0=>50, 1=>50, 2=>100); // cook c prefers 2 with weight 50
在asort()之后: $ array ['a'] = array(0 => 100,1 => 50,2> 0); $ array ['b'] = array(0 => 100,1 => 100,2 => 50); $ array ['c'] = array(2 => 100,0 => 50,1 => 50);
从烹饪'a'开始,他喜欢菜0,超过他的下一个最好的菜50分(体重)。 Cook'b'也喜欢dih 0,但是在下一道菜中重量为0。因此,它很可能(虽然尚不确定烹饪'a'应该制作菜0。
考虑保留菜0并继续烹饪'b'。不包括菜0,厨师'b'更喜欢菜1.没有其他厨师喜欢菜1,所以烹饪'b'被分配菜1。
库克'c'默认获得碟2。
这是一个非常方便的例子,每个厨师都可以烹饪一些个人最大的东西,但我希望它能说明一些可以解决的逻辑。
让我们不那么方便:
$array['a'] = array(0=>75, 1=>50, 2=>0);
$array['b'] = array(0=>100, 1=>50, 2=>50);
$array['c'] = array(0=>100, 1=>25, 2=>25);
再次尝试烹饪'a'并看到0是首选,但这次是重量25.烹饪'b'更喜欢重量为50而烹饪'c'更喜欢重量为75.烹饪'c'赢得菜0。
回到可用厨师名单,'a'喜欢1,体重50,但'b'更喜欢它的重量为0.'a'得到菜1,'b'得到菜2。
这仍然没有解决所有复杂问题,但它是朝着正确方向迈出的一步。有时,对第一个烹饪/菜肴组合的假设是错误的。
方便不太方便:
$array['a'] = array(0=>200, 1=>148, 2=>148, 3=>0);
$array['b'] = array(0=>200, 1=>149, 2=>0, 3=>0);
$array['c'] = array(0=>200, 1=>150, 2=>147, 3=>147);
$array['d'] = array(0=>69, 1=>18, 2=>16, 3=>15);
'a'得到0,因为那是最大值,没有其他人喜欢0的权重更高 'b'赢得1,体重149 'd'胜2,因为'c'没有可用选项的偏好 'c'得到3
得分:200 + 149 + 147 + 16 = 512
虽然这是一个很好的猜测,但没有检查每个排列,但这可能是错误的。从这里开始,问:“如果一个厨师和其他任何一个厨师交易,那么总量会增加吗?”
答案是肯定的,a(0)+ d(2)= 200 + 16 = 216,但a(2)+ d(0)= 148 + 69 = 217。
我会留给你用加权方法编写“最佳猜测”的代码,但在那之后,这是一个很好的开始:
// a totally uneducated guess...
$picks = array(0=>'a', 1=>'b', 2=>'c', 3=>'d');
do {
$best_change = false;
$best_change_weight = 0;
foreach ($picks as $dish1 => $cook1) {
foreach ($picks as $dish2 => $cook2) {
if (($array[$cook1][$dish1] + $array[$cook2][$dish2]) <
($array[$cook1][$dish2] + $array[$cook2][$dish1]))
{
$old_score = $array[$cook1][$dish1] + $array[$cook2][$dish2];
$new_score = $array[$cook1][$dish2] + $array[$cook2][$dish1];
if (($new_score - $old_score) > $best_change_weight) {
$best_change_weight = $new_score - $old_score;
$best_change = $dish2;
}
}
}
if ($best_change !== false) {
$cook2 = $picks[$best_change];
$picks[$dish1] = $cook2;
$picks[$dish2] = $cook1;
break;
}
}
} while ($best_change !== false);
我找不到一个反例来证明这不起作用,但我怀疑这种情况 ($ array [$ cook1] [$ dish1] + $ array [$ cook2] [$ dish2]) == ($ array [$ cook1] [$ dish2] + $ array [$ cook2] [$ dish1])
也许其他人会跟进这个“怎么回事?”的答案。
给定这个矩阵,括号中的项目是“选择”
[a1] a2 a3
b1 [b2] b3
c1 c2 [c3]
如果a1 + b2 == a2 + b1,那么'a'和'b'将不会切换菜肴。我不是100%肯定的情况是,如果存在矩阵,这是一个更好的选择:
a1 [a2] a3
b1 b2 [b3]
[c1] c2 c3
从第一个状态到第二个状态需要两个开关,第一个似乎是任意的,因为它不会改变总数。但是,只有通过这种任意改变才能进行最后一次切换。
我试图找到一个例子3x3这样基于我上面写的“加权偏好”模型,第一个将被选中,但也是这样,真正的最佳选择由第二个给出。我无法找到一个例子,但这并不意味着它不存在。我现在不想做更多矩阵代数,但也许有人会在我离开的地方继续学习。哎呀,也许这个案子不存在,但我想我应该指出这个问题。
如果它确实有效且你从正确的选择开始,上面的代码将只循环64次(8x8)8个厨师/菜。如果选择不正确并且第一个厨师有变化,那么它将达到72.如果第8个厨师有变化,则最多为128.这可能会多次循环,但我怀疑它将接近所有40k组合所需的CPU周期。
答案 1 :(得分:1)
我可能有一个起点,这个算法试图根据最大分数与分数总和的比例选择厨师(因此试图选择真正擅长一种食谱而不是其他食谱的厨师)做食谱的食谱)
$cooks = array(
array(1,2,3,4),
array(35,0,0,0),
array(36,33,1,1),
array(20,20,5,3)
);
$results = array();
while (count($cooks)) {
$curResult = array(
'cookId' => -1,
'recipe' => -1,
'score' => -1,
'ratio' => -1
);
foreach ($cooks as $cookId => $scores) {
$max = max($scores);
$ratio = $max / array_sum($scores);
if ($ratio > $curResult['ratio']) {
$curResult['cookId'] = $cookId;
$curResult['ratio'] = $ratio;
foreach ($scores as $recipe => $score) {
if ($score == $max) {
$curResult['recipe'] = $recipe;
$curResult['score'] = $score;
}
}
}
}
$results[$curResult['recipe']] = $curResult['score'];
unset($cooks[$curResult['cookId']]);
foreach ($cooks as &$cook) {
unset($cook[$curResult['recipe']]);
}
}
对于提供的数据集,它确实找到了最佳答案(35,33,5,4)。但是,它仍然不完美,例如,使用数组:
$cooks = array(
array(1,2,3,4),
array(35,0,33,0),
array(36,33,1,1),
array(20,20,5,3)
);
理想的答案是(20,33,33,4),但是这个算法会返回(35,33,5,4)。
但是既然问题是要求从哪里开始的想法,我想这至少可以作为开始的东西:P
答案 2 :(得分:0)
试试这个
$mainArr = array(
array (1,2,3,4) ,
array (35,0,0,0) ,
array (36,33,1,1) ,
array (20,20,5,3)
);
$i = 0;
foreach( $mainArr as $subArray )
{
foreach( $subArray as $key => $value)
{
$newArr[$key][$i]=$value;
$i++;
}
}
$finalArr = array();
foreach( $newArr as $newSubArray )
{
$finalArr[] = max($newSubArray);
}
print_r( $finalArr );
答案 3 :(得分:0)
好的这里是一个解决方案,可以让你找到一个厨师到一个食谱的最佳排列,没有厨师工作两次,没有配方两次。
感谢计算数组perm的代码转到o'reilly ... http://docstore.mik.ua/orelly/webprog/pcook/ch04_26.htm
注意事项:
厨师人数和食谱数量相同。
超过5乘5矩阵,这将非常快。 (见第2部分即将发布)
逻辑: 数组的排列分配一个位置以及仅包含(即组合的功能),所以为什么不将这样的数组的每个键分配给一个配方,排列保证不重复烹饪,键保证没有食谱重复。
如果我的想法或代码中有改进或错误,请告诉我,但现在就是这样!
<?php
function pc_next_permutation($p, $size) {
//this is from http://docstore.mik.ua/orelly/webprog/pcook/ch04_26.htm
// slide down the array looking for where we're smaller than the next guy
for ($i = $size - 1; $p[$i] >= $p[$i+1]; --$i) { }
// if this doesn't occur, we've finished our permutations
// the array is reversed: (1, 2, 3, 4) => (4, 3, 2, 1)
if ($i == -1) { return false; }
// slide down the array looking for a bigger number than what we found before
for ($j = $size; $p[$j] <= $p[$i]; --$j) { }
// swap them
$tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
// now reverse the elements in between by swapping the ends
for (++$i, $j = $size; $i < $j; ++$i, --$j) {
$tmp = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $tmp;
}
return $p;
}
$cooks[441] = array(340=>5,342=>43,343=>50,344=>9,345=>0);
$cooks[442] = array(340=>5,342=>-33,343=>-30,344=>29,345=>0);
$cooks[443] = array(340=>5,342=>3,343=>0,344=>9,345=>10,);
$cooks[444] = array(340=>25,342=>23,343=>20,344=>19,345=>20,);
$cooks[445] = array(340=>27,342=>27,343=>26,344=>39,345=>50,);
//a consideration: this solution requires that the number of cooks equal the number of recipes
foreach ($cooks as $cooksCode => $cooksProfile){
$arrayOfCooks[]=$cooksCode;
$arrayOfRecipes = (array_keys($cooksProfile));
}
echo "<br/> here is the array of the different cooks<br/>";
print_r($arrayOfCooks);
echo "<br/> here is the array of the different recipes<br/>";
print_r($arrayOfRecipes);
$set = $arrayOfCooks;
$size = count($set) - 1;
$perm = range(0, $size);
$j = 0;
do {
foreach ($perm as $i) { $perms[$j][] = $set[$i]; }
} while ($perm = pc_next_permutation($perm, $size) and ++$j);
echo "<br/> here are all the permutations of the cooks<br/>";
print_r($perms);
$bestCombo = 0;
foreach($perms as $perm){
$thisScore =0;
foreach($perm as $key =>$cook){
$recipe= $arrayOfRecipes[$key];
$cookScore =$cooks[$cook][$recipe];
$thisScore = $thisScore+$cookScore;
}
if ($thisScore>$bestCombo){
$bestCombo=$thisScore;
$bestArray= $perm;
}
}
echo "<br/> here is the very best array<br/>";
print_r ($bestArray);
echo "<br/> best recipe assignment value is:".$bestCombo."<br/><br/>";
?>