Präambel:Berechnung möglicher Lösungen in einem PHP Array: Wie kann ich meinen Algorithmus verbessern?
Diese Frage nicht über PHP zu lernen, noch ist es Code, ich plane, in einer produktiven Umgebung zu verwenden. Ich will nur so sehen und lernen, wie ich diese Art von Arbeit erledigen kann. Bitte korrigieren Sie also nur meinen Code oder zeigen Sie mir bessere, schnellere oder kürzere Lösungen dafür. Das Problem selbst wurde bereits gelöst. Vielen Dank!
Das Problem:
Vor einigen Tagen ein Benutzer asked a question hier auf SO. Sein Problem hat meine Aufmerksamkeit bekommen, denn ich wollte einen Weg finden, seine Bedürfnisse zu lösen.
Er wollte alle möglichen Schlüssel Kombinationen eines PHP array
, wo die Summe des Wertes100 oder so nah wie möglich auf 100. Er hatte uns ein Beispiel Array gegeben, die ich auch für meine Beispiele verwenden:
$array = array(25, 30, 50, 15, 20, 30);
Zum Beispiel ein Ergebnis [2, 4, 5]
sein sollte, weil 50 + 20 + 30
100
ist.
$sum = $array[2] + $array[4] + $array[5]; // = 100
Ich denke, die Grundidee sollte klar sein. Lassen Sie uns jetzt einen Blick auf meine Arbeit nehmen ...
Mein Ansatz:
Also dieses Problem meine Aufmerksamkeit als Entwickler erhalten hat. Zuerst dachte ich, es wäre ziemlich einfach. Tun Sie einfach etwas Ergänzung und überprüfen Sie das Ergebnis. Aber dann habe ich bemerkt, dass da einige Punkte zu beachten sind ...
Es gibt viele Kombinationen zu testen. Für das Beispiel-Array gäbe es bis zu 720() mögliche Permutationen. Um alle möglichen Kombinationen zu erhalten, wollte ich zuerst alle möglichen Permutationen des Arrays bekommen.
Aber das war nur die halbe Wahrheit. Da es im Array doppelte Werte geben könnte (wie in Ihrem Beispiel 30
), konnten wir nicht alle möglichen Permutationen der Array-Werte erhalten, stattdessen mussten wir alle möglichen Permutationen der Array-Schlüssel erhalten.
Also ich habe die pc_permut
Funktion der php cookbook verwendet und für meine Bedürfnisse geändert. Es wird alle möglichen Permutationen in einem Array von Schlüsseln zurückgeben.
/**
* gets all possible permutations of $array
* @param array $array
* @param array $permutations
* @return array
*/
function permutations($array, $permutations = array()) {
if(!empty($array)) {
$result = array();
for($i = count($array) - 1; $i >= 0; --$i) {
$newItems = $array;
$newPerms = $permutations;
list($values) = array_splice($newItems, $i, 1);
array_unshift($newPerms, $values);
$result = array_merge($result, permutations($newItems, $newPerms));
}
}
else {
$result = array($permutations);
}
return $result;
}
Das Ergebnis dieser Funktion ist ein mehrdimensionales Array, das alle Permutationen in einem geordneten Schlüssel-Array enthält.
Array (
[0] => Array (
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)
[1] => Array (
[0] => 1
[1] => 0
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)
[...
)
Also, für jetzt habe ich alle Permutationen mit zu arbeiten. Die Berechnung der möglichen Kombinationen ist gar nicht so schwer.Ich werde einfach die Permutation durchlaufen, die Summe erhöhen, bis sie 100
oder höher erreicht haben und die Tastenkombination zurückgeben.
Aber ich finde heraus, dass ich eine Sache verpasst habe. Da ich alle möglichen Permutationen erhalten habe, sind sogar einige Ergebnisse in der Liste doppelt vorhanden. Um zu erklären, diese beiden Ergebnisse sind im Grunde das gleiche:
[2, 4, 5]; // 50 + 20 + 30 = 100
[4, 5, 2]; // 20 + 30 + 50 = 100
Ich habe das Sortieren nach der Berechnung die Schlüssel endete und sie als Index in der resultierenden Array. Es wäre also sicher, dass jede Kombination nur einmal im Ergebnis existiert. Das ist meine combinations
Funktion:
/**
* gets all possible key combinations of $array with a sum below or equal $maxSum
* @param array $array
* @param integer $maxSum
* @return array
*/
function combinations($array, $maxSum) {
// get all permutations of the array keys
$permutations = permutations(array_keys($array));
$combinations = array();
// loop all permutations
foreach($permutations as $keys) {
// create a container for each permutation to store calculation
$current = array(
"sum" => 0,
"keys" => array()
);
// now loop through the permutation keys
foreach($keys as $key) {
// if the addition is still between or equal $maxSum
if($current["sum"] + $array[$key] <= $maxSum) {
// increment the sum and add key to result
$current["sum"] += $array[$key];
$current["keys"][] = $key;
}
}
// to be sure each combination only exists once in the result
// order the keys and use them as array index
sort($current["keys"]);
$combinations[join("", $current["keys"])] = $current;
}
// remove the created key-index from array when finished
return array_values($combinations);
}
Die Ausführung nach vorne einfach gerade ist:
$array = array(25, 30, 50, 15, 20, 30);
print_r(combinations($array, 100));
Das Ergebnis ist ein Array, alle Kombinationen enthalten. Für unser Beispiel-Array gibt es elf mögliche Kombinationen. Das Ergebnis sieht wie folgt aus:
Array (
[0] => Array (
[sum] => 90
[keys] => Array (
[0] => 0
[1] => 1
[2] => 3
[3] => 4
)
)
[1] => Array (
[sum] => 90
[keys] => Array (
[0] => 0
[1] => 2
[2] => 3
)
)
[...
Da ich dieses Skript als answer of the original question geschrieben habe, werde ich mich fragen, ob es eine andere sein würde, noch besser, die Arbeit zu tun. Vielleicht gibt es einen Weg ohne Permutationen oder eine Möglichkeit, gleiche Kombinationen aus der Berechnung oder dem resultierenden Array auszuschließen. Ich weiß, dass ich die Berechnung auch direkt in der permutations
Funktion ausführen könnte, aber das wäre im Grunde der gleiche Arbeitsablauf.
Ich würde wirklich gerne einige Ratschläge, Tipps oder Verbesserungen von Ihnen bekommen. Ich denke, hier ist etwas Potenzial, um das Skript zu verbessern, aber ich habe eigentlich keine Ahnung, wie. Aber ich bin mir sicher, es könnte einfacher und einfacher gemacht werden ...
Danke für Ihre Zeit! :)
Hmm. Ich denke nicht komplett, weil es keine Rezension ist. Es geht darum zu programmieren und wie, und das passt zu SO. Ich möchte keine Überprüfung des Codes, ich wollte andere Wege, was imo ist nicht gut in den Code-Bewertungen platziert ... – eisbehr
@eisbehr "Ich würde wirklich gerne ein paar Tipps, Tipps bekommen". Weißt du, was das Rucksackproblem, allgemeinere Verpackungsprobleme und dynamische Programmierung sind? Dies ist eine Klasse von Verpackungsproblemen. Es ist das Knap-sack-Problem, bei dem alle Gegenstände gleichwertig sind. – spinkus