Я хочу рассчитать коэффициенты смешивания, но у меня есть проблемы во время выполнения,
Кажется, 7 компонентов с точностью до 4% являются пределом в PHP …
Поэтому я стараюсь найти способ избежать лишних циклов, чтобы быстрее вычислять.
У меня есть ингредиенты и лимит (здесь 6). Я хочу найти все комбинации
ниже 6. В шаге звука (здесь не показан) я просто заказываю эти
Хиты после цены. Я хотел бы найти самую дешевую комбинацию с
ингредиент_x ниже 6.
Я пытаюсь сделать это двумя способами, просто сложив ингредиенты, и шаг за шагом, чтобы прервать цикл раньше (в безопасные циклы).
<?php
$beginn = microtime(true);
$loops = 0;
$hits = 0;
$m = array();
// 1. TEST
$component[0]['ingredient_x'] = 6.95;
$component[1]['ingredient_x'] = 65.7;
$component[2]['ingredient_x'] = '';
$component[3]['ingredient_x'] = 2;
$component[4]['ingredient_x'] = '';
$component[5]['ingredient_x'] = '';
$component[6]['ingredient_x'] = '';
/*
Results:
With abort Loops
Loops: 285.188
Hits: 285.077
Seconds: 1,707 sec.
Without:
Loops: 736.281
Hits: 285.077
Seconds: 6,582 sec.
*/
// 2. TEST
$component[0]['ingredient_x'] = 6.95;
$component[1]['ingredient_x'] = 6.7;
$component[2]['ingredient_x'] = '';
$component[3]['ingredient_x'] = 2;
$component[4]['ingredient_x'] = '';
$component[5]['ingredient_x'] = '';
$component[6]['ingredient_x'] = '';
/*
Results:
With abort Loops
Loops: 735.244
Hits: 735.167
Seconds: 4,467 sec.
Without:
Loops: 736.281
Hits: 735.167
Seconds: 3,191 sec.
*/$abort_loops = 1;for ($m[0] = 0; $m[0] <= 100; $m[0] += 4) {
for ($m[1] = 0; $m[1] <= (100 - $m[0]); $m[1] += 4) {
for ($m[2] = 0; $m[2] <= (100 - $m[0] - $m[1]); $m[2] += 4) {
for ($m[3] = 0; $m[3] <= (100 - $m[0] - $m[1] - $m[2]); $m[3] += 4) {
for ($m[4] = 0; $m[4] <= (100 - $m[0] - $m[1] - $m[2] - $m[3]); $m[4] += 4) {
for ($m[5] = 0; $m[5] <= (100 - $m[0] - $m[1] - $m[2] - $m[3] - $m[4]); $m[5] += 4) {
$m[6] = (100 - $m[0] - $m[1] - $m[2] - $m[3] - $m[4] - $m[5]);
$loops++;
if ($abort_loops) {
$r = 0;
$do_break = 0;
// Checking ingredient_x sum, component by component
for ($i = 0; $i <= 6; $i++) {
$r += ($m[$i] * ($component[$i]['ingredient_x'] / 100));
// If Limit is reached, end all following loops
if ($r > 6) {
$m[$i] = 100;
// Cant break here, because of inner loop ...
$do_break = 1;
}
}
// ... so do it outside
if ($do_break) {
break;
}
$hits++;
} else {
// just sum ingredient_x
$r = ($m[0] * ($component[0]['ingredient_x'] / 100)) +
($m[1] * ($component[1]['ingredient_x'] / 100)) +
($m[2] * ($component[2]['ingredient_x'] / 100)) +
($m[3] * ($component[3]['ingredient_x'] / 100)) +
($m[4] * ($component[4]['ingredient_x'] / 100)) +
($m[5] * ($component[5]['ingredient_x'] / 100)) +
($m[6] * ($component[6]['ingredient_x'] / 100));
if ($r <= 6) {
$hits++;
}
}
}
}
}
}
}
}
print('Loops: ' . number_format($loops, 0, '', '.') . '<br>');
print('Hits: ' . number_format($hits, 0, '', '.') . '<br>');
print('Seconds: ' . number_format((microtime(true) - $beginn), 3, ',', '') . ' sec.');?>
Но это очень зависит от ингредиентов. Как вы можете видеть в тесте 1
(прерывание цикла в 5 раз быстрее) и Тест 2 (простой способ суммирования быстрее).
Можно ли сделать эти петли быстрее? Лучший код «отменить цикл»?
Есть ли более быстрый способ найти самую дешевую комбинацию?
Один из подходов — использовать простую математику для подведения итогов:
// just sum ingredient_x
$r = (
$m[0] * $component[0]['ingredient_x'] +
$m[1] * $component[1]['ingredient_x'] +
$m[2] * $component[2]['ingredient_x'] +
$m[3] * $component[3]['ingredient_x'] +
$m[4] * $component[4]['ingredient_x'] +
$m[5] * $component[5]['ingredient_x'] +
$m[6] * $component[6]['ingredient_x']
);
;
if ($r <= 600) {
$hits++;
}
Все деления на 100 могут быть полностью сняты, если вы просто увеличите свой лимит в 100 раз.
a0 / 100 + a1 / 100 + a2/100 + ... a6/100
такой же как
(a0 + a1 + a2 + ... + a6) /100
Для этого понадобится только одно деление вместо 7.
Даже этого разделения можно избежать, если вы установите ограничение в 100 *.
Скорость улучшения на моей машине составляет около 10-15%.
Других решений пока нет …