5

EDIT 1 - 投稿以来、根底にある質問は CARTESIAN PRODUCT を見つける方法に関するものであることがわかりました (今はグーグルに行きます)。キーは順列ごとに一度しかありません そして私の「余分な」質問は、デカルト積が必要とするワークロードを最小限に抑える方法についてです(小さなエラー率を受け入れて、私は言わなければなりません)-

想像してみてください... 私には 4 人の料理人と 4 つのレシピがあり、各料理人には各レシピのスコアがあり、今日は各料理人に 1 つの料理を作ってもらいたい (ただし、料理を 2 回作るべきではありません)。 (最高の合計スコア) 4 つすべての順列 (したがって、料理人は自己ベストを尽くすことはできません)。

データをそのまま多次元配列に入れました

 array(
   array (1,2,3,4),
   array (35,0,0,0),
   array (36,33,1,1),
   array (20,20,5,3)
 )
  • 各サブ配列には、サブ配列の数と同じ数の値のペアがあります(それが役立つ場合)

  • 実際には、サブアレイの数は最大 8 に達します (最大 perms したがって =8!、多くの組み合わせが許可されていないため、8^8 ではなく約 40,000)。

  • この形式のデータを持つことの選択は、それが役立つ場合は柔軟です

各サブ配列の 1 つだけを使用できる KEY ごとに、サブ配列の最良の (つまり、最高値) 可能な組み合わせを出力する 2 番目の配列を作成しようとしています。

--したがって、ここでは、各 subarray[0][1][2][3] が順列ごとに 1 回使用され、各 subarrayKey [0][1][2][3] が順列ごとに 1 回使用されます。関連付けられた配列を使用していますが、それはこの問題の追加事項です.--

したがって、この例では newArray (35,33,5,4) // [2][0] が使用されていないことに注意してください。

理想的には、すべてのパーマを作成するのではなく、明らかに最適ではない多くの組み合わせを何らかの方法で破棄することを好みます.

開始方法についてのアイデアはありますか?疑似コードを受け入れます。

デカルト積に関する SO の例については、PHP 2D Array output all conversionsを参照してください。

デカルト積をより効率的にするための詳細については、EDIT 2を参照してください。また、(リスクを伴いながら)コーナーをカットできるかどうかを確認したい場合は、ケース固有でなければならない理由もあります。効率的なデカルト積アルゴリズム

4

4 に答える 4

2

申し訳ありませんが、これはコードというよりも論理的なレイアウトになります...

配列(1,2,3,4)が最初の料理のスコアなのか、最初の料理のスコアなのかははっきりしませんが、おそらく次のような配列を使用します

$array[$cook_id][$dish_number] = $score;

$array[$cook_id] = array($lowest_scored_dish,...,$highest); となるように各配列を asort() します。

最高の料理と別の料理のスコアの差になるように、特定の料理人が料理を作るための重み付けされた好みを考えてみましょう。

非常に単純な例として、料理 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' は重み 50 の 1 を好みますが、'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' を獲得します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

最初の状態から 2 番目の状態に移行するには 2 つのスイッチが必要です。最初の状態は合計を変更しないため、任意のように見えます。しかし、この恣意的な変更を経ることによってのみ、最後の切り替えを行うことができます。

上で書いた「重み付けされた好み」モデルに基づいて、最初のモデルが選択されるだけでなく、実際の最適な選択が 2 番目のモデルによって与えられるような 3x3 の例を見つけようとしました。例を見つけることができませんでしたが、それが存在しないという意味ではありません。今は行列代数をこれ以上やろうとは思わないが、おそらく誰かが私が中断したところから始めるだろう. 一体、そのケースは存在しないのかもしれませんが、懸念事項を指摘する必要があると思いました。

それが機能し、正しい選択で開始した場合、上記のコードは 8 つの料理/皿に対して 64 回 (8x8) ループします。ピックが正しくなく、最初のクックが変更された場合、最大 72 になります。8 番目のクックが変更された場合、最大 128 になります。do-while が数回ループする可能性がありますが、疑わしいと思います。 40k の組み合わせをすべて合計するのに必要な CPU サイクル近くまで上昇します。

于 2013-04-26T17:12:39.797 に答える
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 );
于 2013-04-26T08:39:33.050 に答える
0

ここで、1 つの料理から 1 つのレシピへの最良の順列を見つけることができ、料理が 2 回機能することはなく、レシピが 2 回作成されることもありません。

配列の perm を計算するコードを o'reilly に送っていただきありがとうございます... http://docstore.mik.ua/orelly/webprog/pcook/ch04_26.htm

考慮事項:

  • 料理人の数とレシピの数は同じです。

  • ここのように 5 x 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/>";  




?>
于 2013-04-28T16:42:38.630 に答える