Фабула
$cells = [3, 1, 0, 2, 4, 0, 1];
Есть массив чисел это “клетки”:
Каждое число это “энергия” клетки.
Правила одного шага
- Каждая клетка с энергией > 0:
- теряет 1 энергию
- отдает +1 энергии соседям (левому и правому, если они есть)
- Клетки с 0 энергией:
- ничего не отдают
- могут получать энергию от соседей
- Все изменения применяются одновременно, не по очереди.
Нужно
- Реализовать функцию
step(array $cells): array - Прогнать симуляцию N шагов
- После каждого шага сохранять состояние
Пример
start: [3,1,0,2]
step1: [3,1,2,1]
step2: [3,2,3,1]
Решение.
<?php
$cells = [3, 1, 0, 2, 4, 0, 1];
function step(array $cells): array
{
$n = count($cells);
// 1) база: -1 всем, кто > 0
$base = [];
for ($i = 0; $i < $n; $i++) {
$base[$i] = ($cells[$i] > 0) ? ($cells[$i] - 1) : 0;
}
// 2) прибавки (дельты) от соседей, считаем по исходному $cells
// считаем, сколько энергии КАЖДАЯ клетка получит от соседей за шаг...
$delta = array_fill(0, $n, 0);
for ($i = 0; $i < $n; $i++) {
if ($cells[$i] <= 0) {
continue;
}
if ($i - 1 >= 0) {
$delta[$i - 1] += 1;
}
if ($i + 1 < $n) {
$delta[$i + 1] += 1;
}
}
// 3) итог: база + дельта
$result = [];
for ($i = 0; $i < $n; $i++) {
$result[$i] = $base[$i] + $delta[$i];
}
return $result;
}
$step1 = step($cells);
print_r($step1);
// Output
Array
(
[0] => 1
[1] => 1
[2] => 2
[3] => 1
[4] => 1
[5] => 2
[6] => 0
)
Особенности:
- array_fill – создает массив нужной длины и сразу заполняет его одним значением.
Пример:
$a = array_fill(0, 5, 0);
// Output
[0, 0, 0, 0, 0]
То есть:
- начать с индекса
0 - создать
5элементов - каждый элемент равен
0
Наше решение состоит из 3 этапов:
- Отнимаем если надо от каждого элемента -1. Собираем в массив base.
function step(array $cells): array
{
$n = count($cells);
// 1) база: -1 всем, кто > 0
$base = [];
for ($i = 0; $i < $n; $i++) {
$base[$i] = ($cells[$i] > 0) ? ($cells[$i] - 1) : 0;
}
...
// Output
Array
(
[0] => 2
[1] => 0
[2] => 0
[3] => 1
[4] => 3
[5] => 0
[6] => 0
)
- Считаем сколько энергии КАЖДАЯ клетка получит от соседей за шаг.
Формируем массив прибавок. Получаем массив сколько надо прибавить к каждому элементу
// 2) прибавки (дельты) от соседей, считаем по исходному $cells
// считаем, сколько энергии КАЖДАЯ клетка получит от соседей за шаг...
$delta = array_fill(0, $n, 0);
for ($i = 0; $i < $n; $i++) {
if ($cells[$i] <= 0) {
continue;
}
if ($i - 1 >= 0) {
$delta[$i - 1] += 1;
}
if ($i + 1 < $n) {
$delta[$i + 1] += 1;
}
}
// Output
Array
(
[0] => 1
[1] => 1
[2] => 2
[3] => 1
[4] => 1
[5] => 2
[6] => 0
)
это значит:
- клетка 0 получит +1
- клетка 1 получит +1
- клетка 2 получит +2
- …
Мы ничего еще не применяем, только считаем.
- Последний этап, сумируем Базовый массив с прибавочным.
// 3) итог: база + дельта
$result = [];
for ($i = 0; $i < $n; $i++) {
$result[$i] = $base[$i] + $delta[$i];
}
return $result;
// Output
Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 2
[4] => 4
[5] => 2
[6] => 0
)
И еще я хочу вызывать функцию в цикле, пока например массив не перестанет меняться (условно, такого не будет):
<?php
$cells = [3, 1, 0, 2, 4, 0, 1];
$history = [];
$step = 0;
while (true) {
// сохраняем текущее состояние
$history["step{$step}"] = $cells;
$next = step($cells);
// если изменений больше нет — выходим
if ($next === $cells) {
break;
}
if ($step > 100) {
break;
}
$cells = $next;
$step++;
}
// вывод
foreach ($history as $name => $state) {
echo $name . ': [' . implode(',', $state) . ']' . PHP_EOL;
}
function step(array $cells): array
{
$n = count($cells);
// 1) база: -1 всем, кто > 0
$base = [];
for ($i = 0; $i < $n; $i++) {
$base[$i] = ($cells[$i] > 0) ? ($cells[$i] - 1) : 0;
}
// 2) прибавки (дельты) от соседей, считаем по исходному $cells
// считаем, сколько энергии КАЖДАЯ клетка получит от соседей за шаг...
$delta = array_fill(0, $n, 0);
for ($i = 0; $i < $n; $i++) {
if ($cells[$i] <= 0) {
continue;
}
if ($i - 1 >= 0) {
$delta[$i - 1] += 1;
}
if ($i + 1 < $n) {
$delta[$i + 1] += 1;
}
}
// 3) итог: база + дельта
$result = [];
for ($i = 0; $i < $n; $i++) {
$result[$i] = $base[$i] + $delta[$i];
}
return $result;
}
// Output
step0: [3,1,0,2,4,0,1]
step1: [3,1,2,2,4,2,0]
step2: [3,2,3,3,5,2,1]
step3: [3,3,4,4,6,3,1]
step4: [3,4,5,5,7,4,1]
step5: [3,5,6,6,8,5,1]
step6: [3,6,7,7,9,6,1]
step7: [3,7,8,8,10,7,1]
step8: [3,8,9,9,11,8,1]
step9: [3,9,10,10,12,9,1]
step10: [3,10,11,11,13,10,1]
step11: [3,11,12,12,14,11,1]
...
Базовая идея
Есть:
- текущее состояние
$cells - новое состояние
$next = step($cells) $historyхранит все состояния: start, step1, step2…
Если:
$next === $cells
значит система перестала меняться, можно останавливать цикл.
Более аккуратный вариант (функция)
function simulate(array $cells, int $maxSteps = 100): array
{
$history = [];
$step = 0;
while ($step < $maxSteps) {
$history[] = $cells;
$next = step($cells);
if ($next === $cells) {
break;
}
$cells = $next;
$step++;
}
return $history;
}
Весь код
<?php
$cells = [3, 1, 0, 2, 4, 0, 1];
function simulate(array $cells, int $maxSteps = 100): array
{
$history = [];
$step = 0;
while ($step < $maxSteps) {
$history[] = $cells;
$next = step($cells);
if ($next === $cells) {
break;
}
$cells = $next;
$step++;
}
return $history;
}
function step(array $cells): array
{
$n = count($cells);
// 1) база: -1 всем, кто > 0
$base = [];
for ($i = 0; $i < $n; $i++) {
$base[$i] = ($cells[$i] > 0) ? ($cells[$i] - 1) : 0;
}
// 2) прибавки (дельты) от соседей, считаем по исходному $cells
// считаем, сколько энергии КАЖДАЯ клетка получит от соседей за шаг...
$delta = array_fill(0, $n, 0);
for ($i = 0; $i < $n; $i++) {
if ($cells[$i] <= 0) {
continue;
}
if ($i - 1 >= 0) {
$delta[$i - 1] += 1;
}
if ($i + 1 < $n) {
$delta[$i + 1] += 1;
}
}
// 3) итог: база + дельта
$result = [];
for ($i = 0; $i < $n; $i++) {
$result[$i] = $base[$i] + $delta[$i];
}
return $result;
}
print_r(simulate($cells, 20));
