Я пытаюсь исправить эту загадку:
У меня есть массив под названием input
:
$input = [
'A' => ['X', 'Y', 'Z'],
'B' => ['X', 'Y', 'Z'],
'C' => ['X', 'Y', 'Z'],
];
Я должен сделать некоторую магию, чтобы выходной массив имел уникальное значение для одного и того же ключа в каждом массиве, может выглядеть легко, но это не так, эта магия должна добавить пустую строку, если нет совпадения для ячейки, единственное правило — иметь уникальное значение на тот же ключ на каждом внутреннем массиве.
$output = [
'A' => ['X', 'Y', 'Z'],
'B' => ['Z', 'X', 'Y'],
'C' => ['Y', 'Z', 'X'],
];
Правила таковы:
- выходной массив должен содержать массивы с такими же значениями из входного массива,
- Подстановочные знаки допускаются только в том случае, если для данного индекса нет действительного соответствия
- допускаются разные размеры внутреннего массива
- значения внутреннего массива не могут дублироваться.
- выходной массив должен иметь уникальные значения на одном и том же ключе
пример:
$input = [
'A' => ['X', 'Y'],
'B' => ['X', 'Y'],
'C' => ['X', 'Y'],
];
возможное решение:
$output = [
'A' => ['X', 'Y', ''],
'B' => ['', 'X', 'Y'],
'C' => ['Y', '', 'X'],
];
TL; DR: Выровняйте матрицу по квадрату, затем используйте грубую силу для генерации каждого возможного вращения столбца. Если один поворот не имеет повторяющихся значений столбцов, остановитесь. Смотрите это в прямом эфире на 3v4l.org.
Мой другой ответ обеспечивает компактное решение если и только если все строки содержат все возможные значения (в любом порядке). Вот пример. Мой предыдущий ответ гарантирует решение для матрицы слева, а не справа:
Satisfies Does not satisfy
[ [
[ 'X', 'Y' ], [ 'X', 'Y' ],
[ 'Y', 'X' ], [ 'Y' ],
] ]
Чтобы справиться с общим случаем, когда любая строка может содержать любое произвольное подмножество всех возможных значений, мы можем грубо форсировать решение. Наш скот должен знать, как:
Первый легко написать. Для каждого столбца в данном квадратная матрица, проверьте, отличается ли количество уникальных, не подстановочных значений от количества значений. Если это так, то в этом столбце дублируется хотя бы одно не подстановочное значение:
function matrix_is_solved(array $matrix) {
foreach (array_keys(current($matrix)) as $offset) {
$column = array_filter($raw = array_column($matrix, $offset));
if (count($column) != count(array_unique($column))) return false;
}
return true;
}
Вторая часть требует хитрости. Заметьте, что можно применять вектор к любой квадратной матрице, чтобы вращать значения в матрице. Я пропущу математику и просто покажу несколько примеров:
original = [
[ 'X', 'Y' ],
[ 'Y', 'X' ],
]
vector = [ 0, 0 ] // rotate 1st row 0 places, 2nd row 0 places
result = [
[ 'X', 'Y' ],
[ 'Y', 'X' ],
]
vector = [ 1, 0 ] // rotate 1st row 1 place, 2nd row 0 places
result = [
[ 'Y', 'X' ],
[ 'Y', 'X' ],
]
vector = [ 0, 1 ] // rotate 1st row 0 places, 2nd row 1 place
result = [
[ 'X', 'Y' ],
[ 'X', 'Y' ],
]
vector = [ 1, 1 ] // rotate 1st row 1 place, 2nd row 1 place
result = [
[ 'Y', 'X' ],
[ 'X', 'Y' ],
]
Поскольку имеется 2 строки по 2 столбца в каждой, существует ровно 4 комбинации векторов вращения (все они перечислены выше). Из этих четырех векторов два — [ 0, 0 ]
а также [ 1, 1 ]
— найти решение. Поскольку нам нужно только одно решение, достаточно остановиться на первом векторе, который дает решенную матрицу.
Зная это, вот как мы напишем наш грубый метод: сгенерируем все возможные векторы вращения, попробуем каждый из них против нашей головоломки и вернемся, если преобразованная матрица находится в решенном состоянии:
function matrix_generate_vectors(array $matrix) {
$vectors = [];
$columns = count(current($matrix));
$gen = function ($depth=0, $combo='') use (&$gen, &$vectors, $columns) {
if ($depth < $columns)
for ($i = 0; $i < $columns; $i++)
$gen($depth + 1, $i . $combo);
else
$vectors[] = array_map('intval', str_split($combo));
};
$gen();
return $vectors;
}
function matrix_rotate(array $matrix, array $vector) {
foreach ($matrix as $row => &$values) {
array_rotate($values, $vector[$row]);
}
return $matrix;
}
function matrix_brute_solve(array $matrix) {
matrix_make_square($matrix);
foreach (matrix_generate_vectors($matrix) as $vector) {
$attempt = matrix_rotate($matrix, $vector);
if (matrix_is_solved($attempt))
return matrix_display($attempt);
}
echo 'No solution';
}
Нам также понадобятся несколько помощников, тестовый набор и несколько тестов:
function array_rotate(array &$array, $offset) {
foreach (array_slice($array, 0, $offset) as $key => $val) {
unset($array[$key]);
$array[$key] = $val;
}
$array = array_values($array);
}
function matrix_display(array $matrix = null) {
echo "[\n";
foreach ($matrix as $row => $inner) {
echo " $row => ['" . implode("', '", $inner) . "']\n";
}
echo "]\n";
}
function matrix_make_square(array &$matrix) {
$pad = count(array_keys($matrix));
foreach ($matrix as &$row)
$row = array_pad($row, $pad, '');
}
$tests = [
[ ['X'], ['X'] ],
[ ['X'], ['X'], ['X'] ],
[ [ 'X', '' ], [ '', 'X' ] ],
[ ['X', 'Y', 'Z'], ['X', 'Y'], ['X']],
[ ['X', 'Y'], ['X', 'Y'], ['X', 'Y'] ],
[ ['X', 'Y', 'Z'], ['X', 'Y', 'Z'], ['X', 'Y', 'Z'] ],
[ ['X', 'Y', 'Z', 'I', 'J'], ['X', 'Y', 'Z', 'I'], ['X', 'Y', 'Z', 'I'], ['X', 'Y', 'Z', 'I'], ['X', 'Y', 'Z'], ['X', 'Y', 'Z'] ],
];
array_map(function ($matrix) {
matrix_display($matrix);
echo "solved by:" . PHP_EOL;
matrix_brute_solve($matrix);
echo PHP_EOL;
}, $tests);
Который производит для тех тестов (только несколько примеров):
[
0 => ['X', 'Y', 'Z']
1 => ['X', 'Y', 'Z']
2 => ['X', 'Y', 'Z']
]
solved by:
[
0 => ['Z', 'X', 'Y']
1 => ['Y', 'Z', 'X']
2 => ['X', 'Y', 'Z']
]
[
0 => ['X', 'Y', 'Z', 'I', 'J']
1 => ['X', 'Y', 'Z', 'I']
2 => ['X', 'Y', 'Z', 'I']
3 => ['X', 'Y', 'Z', 'I']
4 => ['X', 'Y', 'Z']
5 => ['X', 'Y', 'Z']
]
solved by:
[
0 => ['', 'X', 'Y', 'Z', 'I', 'J']
1 => ['', '', 'X', 'Y', 'Z', 'I']
2 => ['I', '', '', 'X', 'Y', 'Z']
3 => ['Z', 'I', '', '', 'X', 'Y']
4 => ['Y', 'Z', '', '', '', 'X']
5 => ['X', 'Y', 'Z', '', '', '']
Смотрите это в прямом эфире на 3v4l.org.
Генератор вектора O(nn)
в и то и другое время а также пространство. Итак, если бы у вас была квадратная матрица 3х3, было бы 33 = 27 итераций и хранимых элементов массива. Для 4х4 у вас будет 44 = 256 Вы поняли идею. Генератор может быть улучшен, чтобы быть O(1)
в пространство, но, поскольку это алгоритм глубины, он всегда будет O(nn)
во время.
Кроме того, как реализовано, алгоритм останавливается, когда находит первый решение. Тривиальная модификация позволит вам найти все решения. Интересным дополнением к этой головоломке было бы найти решение с малочисленнее севообороты.
TL; DR: Этот код компактно решает случай, когда все строки содержат все значения в любой порядок. Для решения общего случая — каждый строка имеет подмножество всех возможные значения — см. мой другой ответ.
«Если бы у меня был только один час для решения проблемы, я бы потратил до двух третей этого часа, пытаясь определить, в чем заключается проблема». — Альберт Эйнштейн
Похоже, вы хотите, чтобы рваный массив был в квадрате без столбцов с одинаковым порядком. Давайте определим функции для этого:
function array_make_square(array &$array, $fill = ' ') {
$pad = count(array_keys($array));
foreach ($array as &$row) {
$row = array_pad($row, $pad, $fill);
}
}
function array_organize(array &$outer) {
$offset = 0;
foreach ($outer as &$inner) {
sort($inner);
array_rotate($inner, $offset++);
}
}
function array_rotate(array &$array, $offset) {
foreach (array_slice($array, 0, $offset, true) as $key => $val) {
unset($array[$key]);
$array[$key] = $val;
}
}
Теперь давайте поместим несколько оберток вокруг них, чтобы проверить поведение:
function solve($test) {
array_make_square($test);
array_organize($test);
return $test;
}
function display($outer) {
echo "[\n";
foreach ($outer as $row => $inner) {
echo " $row => ['" . implode("', '", $inner) . "']\n";
}
echo "]\n";
}
$tests = [
[ ['X'], ['X'], ['X'] ],
[ ['X', 'Y'], ['X', 'Y'], ['X', 'Y'] ],
[ ['X', 'Y', 'Z'], ['X', 'Y', 'Z'], ['X', 'Y', 'Z'] ],
];
array_map('display', array_map('solve', $tests));
Который производит:
[
0 => [' ', ' ', 'X']
1 => [' ', 'X', ' ']
2 => ['X', ' ', ' ']
]
[
0 => [' ', 'X', 'Y']
1 => ['X', 'Y', ' ']
2 => ['Y', ' ', 'X']
]
[
0 => ['X', 'Y', 'Z']
1 => ['Y', 'Z', 'X']
2 => ['Z', 'X', 'Y']
]
Сначала я собираю всю информацию, т. Е. Имена ключей, такие как A B C и т. Д., Все значения, такие как X Y Z и т. Д., И количество элементов в каждом подмассиве. Затем я создаю новые массивы для каждой позиции в подмассиве, а именно в этом случае pos0, pos1 и pos2. Затем я заполняю новые вложенные массивы, используя значения из valArray (случайный выбор), сохраняя ограничение на то, что значение не должно повторяться в этом или любом другом массиве для той же позиции.
Надеюсь это поможет..
<?php
$input = [
'A' => ['X', 'Y', 'Z','P'],
'B' => ['X', 'Y', 'Z','Q'],
'C' => ['X', 'Y', 'Z','R'],
'D' => ['X','Y','K']
];
// declare two arrays - keyArray will hold all key names like A, B, C
// valArray will hold all the distinct values like X, Y, Z
// valLength is number of elements in each sub array
$keyArray = [];
$valArray = [];
$valLength =0; // no of elements in sub array
echo "<table width='100%'> <tr> <td width='20%'> </td> <td width='30%'>";
// this loop will gather all the relevenat info for $keyArray, $valAray and $valLength
echo "INPUT <br><br>";
foreach($input as $key => $val) {
if(is_array($val)) {
echo "$key <br/>";
$keyArray[] = $key;
if(count($val) > $valLength) {
$valLength = count($val);
}
foreach($val as $key1 => $val1) {
echo "$key1 => $val1<br>";
if (! in_array($val1, $valArray)) {
//echo "no ...";
$valArray[] = $val1;
}
}
echo "<br>----<br>";
}
else {
// echo "$key => $val<br>";
}
}
echo "</td><td width='30%'>";
$output =[];
//create arrays for each position
for($i=0; $i<$valLength; $i++) {
$posArr = "pos" . $i;
$$posArr = [];
}
foreach($keyArray as $key) {
//echo "$key <br/>";
$thisArr = [];
for($i=0; $i<$valLength; $i++) {
//echo "$i <br>";
$posArr = "pos" . $i;
$whileIterations = 0; // no of random iterations to perform before using wildcard
$randomKey=array_rand($valArray);
$new = $valArray[$randomKey];
do {
$randomKey=array_rand($valArray);
$new = $valArray[$randomKey];
$whileIterations++;
if($whileIterations > 10) { // no of iterations ls limited to 10
$new = '';
break;
}
}while(in_array($new,$thisArr) || in_array($new, $$posArr));
$thisArr[] = $new;
//$$posArr[] = $new;
if($new != '') {
array_push($$posArr,$new);
// keep this in position array so that same value is not repeated in the same position
}
}
// now one subarray is ready to be assigned
$keyName = $key;
$$keyName = [];
$$keyName = $thisArr;
}
// push sub arrays into output array
foreach($keyArray as $key) {
$keyName = $key;
//echo "$keyName <br>";
foreach ($$keyName as $key2) {
// echo "$key2 <br/>";
}
//echo "<br>----<br>";
//array_push($output, $$keyName);
$output[$keyName] = $$keyName;
}
echo "OUTPUT <br/><br/>";
foreach($output as $key => $val) {
if(is_array($val)) {
echo "$key <br/>";
$keyArray[] = $key;
foreach($val as $key1 => $val1) {
echo "$key1 => $val1<br>";
}
}
echo "<br>----<br>";
}
echo "</td><td width='20%'> </td></tr></table>";
?>
Ты можешь использовать array_shift
сделать массив со всеми элементами. Затем установите потерянный элемент в ''
, Ниже код хорошо прокомментирован. https://eval.in/895863
<?php
$ks = ['A', 'B', 'C', 'D'];
$vs = ['X', 'Y', 'Z', 'A'];
$input = [
'A' => ['X', 'Z'],
'B' => ['X', 'Z'],
'C' => ['X', 'Z'],
];
// element not in $inpupt
$diff = array_diff($vs, $input[$ks[0]]);
// array with all the values
$arr = [];
foreach($ks as $k)
{
$arr[$k] = $vs;
array_push($vs, array_shift($vs));
}
// set value in the $diff array to ''
$result = array_map(function($value) use ($diff) {
return array_map( function($v) use ($diff) {
return in_array($v, $diff) ? '' : $v;
}, $value);
}, $arr);print_r($result);
Изменить для комментария ОП
Для $input
с другим элементом, то $diff
массив должен рассчитываться отдельно. https://eval.in/896322
<?php
$ks = ['A', 'B', 'C', 'D', 'E'];
$vs = ['X', 'Y', 'Z', 'A'];
$input = [
'A' => ['X', 'Z'],
'B' => ['X', 'Z'],
'C' => ['X', 'A'],
];
// array with all the values
$arr = [];
foreach($ks as $k)
{
$arr[$k] = $vs;
array_push($vs, array_shift($vs));
}
// set value in the $diff array to ''
$result = array_map(function($value, $k) use ($input, $vs) {
// element not in $inpupt[$k]
$diff = array_diff($vs, isset($input[$k]) ? $input[$k] : []);
return array_map( function($v) use ($diff) {
return in_array($v, $diff) ? '' : $v;
}, $value);
}, $arr, $ks);
// when use multi array for `array_map` the index lost, so combine index with the result here.
$result = array_combine($ks, $result);
print_r($result);
хорошо, я думаю, что мы близки, теперь у меня есть эта идея, есть одна вещь, которую нужно сделать, описал ее в конце
- увеличение размера входного массива до числа элементов с наивысшими значениями или наивысшей длиной дочернего элемента зависит от того, что является ошибкой
- попытаться найти все места для
X
, а затем пытается все места дляY
и так далее.- каждый раз, когда его находят, добавляют его в массив, который хранит используемые элементы, чтобы избежать дублирования.
помощники:
function getHeight($array) {
$max2 = $max1 = 0;
$max = [];
foreach ($array as $k => $v) {
if ($max2 < count($v)) {
$max2 = count($v);
}
foreach($v as $elem) {
if(isset($max[$elem])) {
$max[$elem]++;
} else {
$max[$elem] = 1;
}
}
}
foreach($max as $m) {
if ($max1 < $m) {
$max1 = $m;
}
}
return max($max2, $max1);
}
function getRow($array, $j)
{
$res = [];
foreach ($array as $k => $v) {
$res[] = $v[$j] ?? '';
}
return $res;
}
код
function solve_this_array_please() {
$input = [
'A' => ['X', 'Y', 'Z', 'I'],
'B' => ['X', 'Y', 'Z', 'I'],
'C' => ['X', 'Y', 'Z', 'I'],
'D' => ['X', 'Y', 'Z', 'I'],
];
usort($input, function($i, $j) {
return count($i) <=> count($j);
});
$output = [];
$h = getHeight($input);
// filling te missing wildcards;
foreach($input as $k => $v) {
for($i=0;$i<$h;$i++) {
if (!isset($input[$k][$i]))
$input[$k][$i] = '';
}
$output[$k] = [];
$used[$k] = [];
}
$j = 0;
foreach($input as $key2 => $col2) {
foreach($col2 as $k => $elem) {
foreach($input as $key => $col) {
for($i=0;$i<$h;$i++) {
dump("TRYING: $key $i = $elem");
if (!in_array($elem, getRow($output, $i)) && !in_array($elem, $used[$key]) && (!isset($output[$key][$i]) || $output[$key][$i] == '')) {
if (in_array($elem, $col)) {
dump("ok");
$output[$key][$i] = $elem;
$used[$key][] = $elem;
$i = 0;
break;
}
}
}
}
}
}foreach($output as $k => $o) {
ksort($output[$k]);
}dump($output);
}
одна вещь, в которой мне все еще нужна помощь, — заставить этот скрипт начать поиск следующего массива по ключу, когда он найдет последний вместо 0