производительность — PHP Построение более быстрых циклов — Расчет коэффициентов смешивания

Я хочу рассчитать коэффициенты смешивания. Три компонента m1 30%, м2 30%, м3 40%
с вариацией 5%

<?php
$beginn = microtime(true);
$loops = 0;
$hits = 0;
$precision = 1;
$focus = 5;

print("<table border=1 cellpadding=2>");

for($m1 = 30 - $focus;  $m1 <= 30 + $focus; $m1 += $precision) {
for($m2 = 30 - $focus;  $m2 <= 30 + $focus; $m2 += $precision) {
for($m3 = 40 - $focus;  $m3 <= 40 + $focus; $m3 += $precision) {

$loops++;

if (($m1 + $m2 + $m3) == 100) {
$hits++;

print("<tr>");
print("<td>" . $m1 . " %</td>");
print("<td>" . $m2 . " %</td>");
print("<td>" . $m3 . " %</td>");
print("<td>" . ($m1 + $m2 + $m3) . " %</td>");
print("</tr>");

}

}
}
}
print("</table>");

print(" Loops: " . $loops);
print(" Hits: " . $hits);

$dauer = microtime(true) - $beginn;
echo " $dauer Sek.";
?>

Проблема в том, что мне нужно 1331 циклов, чтобы найти 91 попадание (в сумме 100%),
выглядит так

<table border=1 cellpadding=2><tr><td>25 %</td><td>30 %</td><td>45 %</td><td>100 %</td></tr><tr><td>25 %</td><td>31 %</td><td>44 %</td><td>100 %</td></tr><tr><td>25 %</td><td>32 %</td><td>43 %</td><td>100 %</td></tr><tr><td>25 %</td><td>33 %</td><td>42 %</td><td>100 %</td></tr><tr><td>25 %</td><td>34 %</td><td>41 %</td><td>100 %</td></tr><tr><td>25 %</td><td>35 %</td><td>40 %</td><td>100 %</td></tr><tr><td>26 %</td><td>29 %</td><td>45 %</td><td>100 %</td></tr><tr><td>26 %</td><td>30 %</td><td>44 %</td><td>100 %</td></tr><tr><td>26 %</td><td>31 %</td><td>43 %</td><td>100 %</td></tr><tr><td>26 %</td><td>32 %</td><td>42 %</td><td>100 %</td></tr><tr><td>26 %</td><td>33 %</td><td>41 %</td><td>100 %</td></tr><tr><td>26 %</td><td>34 %</td><td>40 %</td><td>100 %</td></tr><tr><td>26 %</td><td>35 %</td><td>39 %</td><td>100 %</td></tr><tr><td>27 %</td><td>28 %</td><td>45 %</td><td>100 %</td></tr><tr><td>27 %</td><td>29 %</td><td>44 %</td><td>100 %</td></tr><tr><td>27 %</td><td>30 %</td><td>43 %</td><td>100 %</td></tr><tr><td>27 %</td><td>31 %</td><td>42 %</td><td>100 %</td></tr><tr><td>27 %</td><td>32 %</td><td>41 %</td><td>100 %</td></tr><tr><td>27 %</td><td>33 %</td><td>40 %</td><td>100 %</td></tr><tr><td>27 %</td><td>34 %</td><td>39 %</td><td>100 %</td></tr><tr><td>27 %</td><td>35 %</td><td>38 %</td><td>100 %</td></tr><tr><td>28 %</td><td>27 %</td><td>45 %</td><td>100 %</td></tr><tr><td>28 %</td><td>28 %</td><td>44 %</td><td>100 %</td></tr><tr><td>28 %</td><td>29 %</td><td>43 %</td><td>100 %</td></tr><tr><td>28 %</td><td>30 %</td><td>42 %</td><td>100 %</td></tr><tr><td>28 %</td><td>31 %</td><td>41 %</td><td>100 %</td></tr><tr><td>28 %</td><td>32 %</td><td>40 %</td><td>100 %</td></tr><tr><td>28 %</td><td>33 %</td><td>39 %</td><td>100 %</td></tr><tr><td>28 %</td><td>34 %</td><td>38 %</td><td>100 %</td></tr><tr><td>28 %</td><td>35 %</td><td>37 %</td><td>100 %</td></tr><tr><td>29 %</td><td>26 %</td><td>45 %</td><td>100 %</td></tr><tr><td>29 %</td><td>27 %</td><td>44 %</td><td>100 %</td></tr><tr><td>29 %</td><td>28 %</td><td>43 %</td><td>100 %</td></tr><tr><td>29 %</td><td>29 %</td><td>42 %</td><td>100 %</td></tr><tr><td>29 %</td><td>30 %</td><td>41 %</td><td>100 %</td></tr><tr><td>29 %</td><td>31 %</td><td>40 %</td><td>100 %</td></tr><tr><td>29 %</td><td>32 %</td><td>39 %</td><td>100 %</td></tr><tr><td>29 %</td><td>33 %</td><td>38 %</td><td>100 %</td></tr><tr><td>29 %</td><td>34 %</td><td>37 %</td><td>100 %</td></tr><tr><td>29 %</td><td>35 %</td><td>36 %</td><td>100 %</td></tr><tr><td>30 %</td><td>25 %</td><td>45 %</td><td>100 %</td></tr><tr><td>30 %</td><td>26 %</td><td>44 %</td><td>100 %</td></tr><tr><td>30 %</td><td>27 %</td><td>43 %</td><td>100 %</td></tr><tr><td>30 %</td><td>28 %</td><td>42 %</td><td>100 %</td></tr><tr><td>30 %</td><td>29 %</td><td>41 %</td><td>100 %</td></tr><tr><td>30 %</td><td>30 %</td><td>40 %</td><td>100 %</td></tr><tr><td>30 %</td><td>31 %</td><td>39 %</td><td>100 %</td></tr><tr><td>30 %</td><td>32 %</td><td>38 %</td><td>100 %</td></tr><tr><td>30 %</td><td>33 %</td><td>37 %</td><td>100 %</td></tr><tr><td>30 %</td><td>34 %</td><td>36 %</td><td>100 %</td></tr><tr><td>30 %</td><td>35 %</td><td>35 %</td><td>100 %</td></tr><tr><td>31 %</td><td>25 %</td><td>44 %</td><td>100 %</td></tr><tr><td>31 %</td><td>26 %</td><td>43 %</td><td>100 %</td></tr><tr><td>31 %</td><td>27 %</td><td>42 %</td><td>100 %</td></tr><tr><td>31 %</td><td>28 %</td><td>41 %</td><td>100 %</td></tr><tr><td>31 %</td><td>29 %</td><td>40 %</td><td>100 %</td></tr><tr><td>31 %</td><td>30 %</td><td>39 %</td><td>100 %</td></tr><tr><td>31 %</td><td>31 %</td><td>38 %</td><td>100 %</td></tr><tr><td>31 %</td><td>32 %</td><td>37 %</td><td>100 %</td></tr><tr><td>31 %</td><td>33 %</td><td>36 %</td><td>100 %</td></tr><tr><td>31 %</td><td>34 %</td><td>35 %</td><td>100 %</td></tr><tr><td>32 %</td><td>25 %</td><td>43 %</td><td>100 %</td></tr><tr><td>32 %</td><td>26 %</td><td>42 %</td><td>100 %</td></tr><tr><td>32 %</td><td>27 %</td><td>41 %</td><td>100 %</td></tr><tr><td>32 %</td><td>28 %</td><td>40 %</td><td>100 %</td></tr><tr><td>32 %</td><td>29 %</td><td>39 %</td><td>100 %</td></tr><tr><td>32 %</td><td>30 %</td><td>38 %</td><td>100 %</td></tr><tr><td>32 %</td><td>31 %</td><td>37 %</td><td>100 %</td></tr><tr><td>32 %</td><td>32 %</td><td>36 %</td><td>100 %</td></tr><tr><td>32 %</td><td>33 %</td><td>35 %</td><td>100 %</td></tr><tr><td>33 %</td><td>25 %</td><td>42 %</td><td>100 %</td></tr><tr><td>33 %</td><td>26 %</td><td>41 %</td><td>100 %</td></tr><tr><td>33 %</td><td>27 %</td><td>40 %</td><td>100 %</td></tr><tr><td>33 %</td><td>28 %</td><td>39 %</td><td>100 %</td></tr><tr><td>33 %</td><td>29 %</td><td>38 %</td><td>100 %</td></tr><tr><td>33 %</td><td>30 %</td><td>37 %</td><td>100 %</td></tr><tr><td>33 %</td><td>31 %</td><td>36 %</td><td>100 %</td></tr><tr><td>33 %</td><td>32 %</td><td>35 %</td><td>100 %</td></tr><tr><td>34 %</td><td>25 %</td><td>41 %</td><td>100 %</td></tr><tr><td>34 %</td><td>26 %</td><td>40 %</td><td>100 %</td></tr><tr><td>34 %</td><td>27 %</td><td>39 %</td><td>100 %</td></tr><tr><td>34 %</td><td>28 %</td><td>38 %</td><td>100 %</td></tr><tr><td>34 %</td><td>29 %</td><td>37 %</td><td>100 %</td></tr><tr><td>34 %</td><td>30 %</td><td>36 %</td><td>100 %</td></tr><tr><td>34 %</td><td>31 %</td><td>35 %</td><td>100 %</td></tr><tr><td>35 %</td><td>25 %</td><td>40 %</td><td>100 %</td></tr><tr><td>35 %</td><td>26 %</td><td>39 %</td><td>100 %</td></tr><tr><td>35 %</td><td>27 %</td><td>38 %</td><td>100 %</td></tr><tr><td>35 %</td><td>28 %</td><td>37 %</td><td>100 %</td></tr><tr><td>35 %</td><td>29 %</td><td>36 %</td><td>100 %</td></tr><tr><td>35 %</td><td>30 %</td><td>35 %</td><td>100 %</td></tr></table> 

2

Решение

Вы можете рассчитать границы для м2, что даст приемлемое значение для м3.

Например, когда m1 = 25, поскольку максимальное значение для m3 равно 45, m2 не может иметь значение ниже 30, иначе 25 + 29 оставит 46, что недопустимо для м3 (диапазон 35 … 45).

Это означает, что нижняя граница для m2 составляет 100 — m1 — (макс. М3). Но это имеет место, только если полученное значение выше чем нижняя допустимая граница для m2: если бы расчет дал 24 вместо 30, нам пришлось бы начинать со значения 25 для m2, поскольку 24 неприемлемо.

Таким образом, мы хотим получить максимум между (минимально допустимым значением m2) и (значением, принятым m2, когда m3 равен его максимум).

То же самое рассуждение для верхнего предела дает m2max = min (m2limit, 100 — (m3min)).

$focus = 5;
$precision = 1;
$loops = 0;

for ($m1 = 30 - $focus; $m1 <= 30 + $focus; $m1 += $precision) {
$m2a = 100 - $m1 - (40 + $focus); // This is the minimum m2 that makes sense.
$m2a = max($m2a, 30 - $focus);  // But it must also be allowed by m2 bounds.

$m2b = 100 - $m1 - (40 - $focus); // Maximum m2 that makes sense.
$m2b = min($m2b, 30 + $focus); // Clip to the allowed range.

for ($m2 = $m2a; $m2 <= $m2b; $m2 += $precision) {
$loops++;
$m3 = 100 - $m1 - $m2;
print "{$m1}% + {$m2}% + {$m3}% = " . ($m1 + $m2 + $m3) . "%\n";
}
}
print $loops . "\n";

Выходы:

25% + 30% + 45% = 100%
...omitted...
35% + 30% + 35% = 100%

91

Чтобы пойти еще быстрее — не то, чтобы это имело смысл — вы можете оптимизировать вывод:

for ($m1 = 30 - $focus; $m1 <= 30 + $focus; $m1 += $precision) {
$m2a = 100 - $m1 - (40 + $focus); // This is the minimum m2 that makes sense.
$m2a = max($m2a, 30 - $focus);  // But it must also be allowed by m2 bounds.

$m2b = 100 - $m1 - (40 - $focus); // Maximum m2 that makes sense.
$m2b = min($m2b, 30 + $focus); // Clip to the allowed range.

$tr = '<tr><td>' . $m1 . ' %</td><td>';

for ($m2 = $m2a; $m2 <= $m2b; $m2 += $precision) {
$m3 = 100 - $m1 - $m2;
// We know that m1 + m2 + m3 = 100.
$html .= $tr . $m2 . ' %</td><td> 100%</td></tr>';
}
}
print $html;
1

Другие решения

Вот первая оптимизация, которая приходит на ум …

for($m1 = 30 - $focus;  $m1 <= 30 + $focus; $m1 += $precision) {
for($m2 = 30 - $focus;  $m2 <= 30 + $focus; $m2 += $precision) {
$loops++;
$m3 = 100 - $m1 - $m2;
if ($m3>=40-$focus && $m3<=40+$focus) {
// a hit! so print stuff
}
}
}

Это исключает самый внутренний цикл для $ m3, поскольку необходимое значение $ m3 можно рассчитать из $ m1 и $ m2.

РЕДАКТИРОВАТЬ: Я только что попробовал это. Отказ от внутреннего цикла сбрасывает $ loop с 1331 до 121. Достаточно ли близко к 91? Любая дальнейшая оптимизация, чтобы приблизиться к 91, вероятно, будет стоить дорого с точки зрения непонятности кода.

0