【问题标题】:Find all possible permutations without repeating the value查找所有可能的排列而不重复值
【发布时间】:2016-04-19 00:27:26
【问题描述】:

假设我有这组数组作为输入:

[
 0 => [1,2,4,5],
 1 => [2,3,4],
 2 => [1,3],
]

我想找到所有可能的排列,从每个数组中选择一个值。该值在最终结果中将是唯一的,因此不会重复。例如,我不能在结果中出现两次1

输入上的数组计数与输出上的数组计数相同。

想要的组合示例(键=>值):

[0 => 1,1 => 2,2 => 3]
[0 => 2,1 => 3,2 => 1]
[0 => 5,1 => 2,2 => 1]
[0 => 1,1 => 3,2 => null]

错误的结果

[0 => 1,1 => 2,2 => 1]

[0 => 2,1 => 2,2 => 3]

我想使用 PHP 获得所有可能的排列组合。我该怎么做?

我附上了真实的数据集http://pastebin.com/U6Hyawm4 但是,我不知道可能有多少排列。

【问题讨论】:

  • 我发现你的示例输出有点混乱 - 你能提供一个 full 示例输出吗?
  • 我也对您的预期输出感到有些困惑。请提供您期望的示例数据的所有组合。正如我现在所看到的,您只想从每个子数组中选择一个数字,但不想要重复的值。但是您仍然希望每个组合都是所有子数组的长度。您似乎还使用 NULL 作为“填充符”来获得您的组合长度。所以你认为它是每个子数组中的一个值?!如果最后两个子数组仅包含 1 和 2,那么可能是这样的组合:[1, 2, NULL, NULL]
  • 如果你有 3 个子数组,[NULL, NULL, NULL] 是一个可能的组合吗?还是“NULL”只是一个填充物?此外,当您在输出中显式显示键时,您是否要使用子数组的键?还是恰好是同一个?
  • @Rizier123 [NULL, NULL, NULL] 是不可能的组合。此外,[1, 2, NULL, NULL] 不是因为它有 4 个元素。基本上,NULL 仅在当前子数组的每个元素都已被使用时出现,如本例所示:[0 => 1,1 => 3,2 => null][1,3] 作为第三个子数组。但是 13 已经从之前的子数组中被挑选出来,所以它不能被再次挑选出来。
  • @simPod 好的,所以它只是获得组合的预期长度的填充物。但只是为了确保:如果您有 4 个子数组,那么如果最后两个子数组仅包含 1 个或 2 个,[1, 2, NULL, NULL] 是可能的吗? + 它们是来自输入数组的输出的键还是只是枚举的?

标签: php arrays combinations


【解决方案1】:

这是一个经过优化的非递归版本

/**
 * Generates all the possible unique N-tuples from an array of N arrays of integers
 *
 * @param array $input
 * @return array
 */
function generateCombinations(array &$input) {
    // since the example results included [1, 3, null] I have assumed that
    // null is a possible value of each set.
    $sets = [];
    foreach($input as $set) {
        if(!in_array(null, $set)) {
            $set[] = null;
        }
        $sets[] = $set;
    }

    // by working on the iterators of each array this loop
    // linearizes the entire set of possible combinations
    // and iterates it (skipping as many as it can).
    $output = [];
    $setCount = count($sets);
    while(current($sets[0]) !== false) {
        $testCombo = [];
        for($setIdx = 0; $setIdx < $setCount; $setIdx++) {
            if(!in_array(current($sets[$setIdx]), $testCombo)) {
                $testCombo[] = current($sets[$setIdx]);
            }
            else {
                // when a combination is thrown out as duplicate
                // iterate to skip any other combo's that would also
                // contain that duplicate
                iterateSets($sets, $setIdx);
                break;
            }
        }
        // if there were no duplicates add it to the output and iterate
        if(count($testCombo) == $setCount) {
            $output[] = $testCombo;
            iterateSets($sets, $setCount - 1);
        }
    }
    return $output;
}

/**
 * Iterates to the next potentially valid combination. I think of
 * this like doing long-hand addition. Add 1 and carry is akin to
 * next and reset.
 *
 * @param array $sets
 * @param $index
 */
function iterateSets(array &$sets, $index) {
    // reset iterators of all sets past the current one to skip
    // combos that cannot be valid
    for($i = $index + 1, $ic = count($sets); $i < $ic; $i++) {
        reset($sets[$i]);
    }
    // always move one on current set
    next($sets[$index]);
    while($index > 0 && current($sets[$index]) === false) {
        // wrap if current set is at the end
        reset($sets[$index]);
        $index--;
        // move one for the preceding set
        next($sets[$index]);
        // then repeat
    }
}

得到的数组是:

[
    [1,2,3]
    [1,2,null]
    [1,3,null]
    [1,4,3]
    [1,4,null]
    [1,null,3]
    [2,3,1]
    [2,3,null]
    [2,4,1]
    [2,4,3]
    [2,4,null]
    [2,null,1]
    [2,null,3]
    [4,2,1]
    [4,2,3]
    [4,2,null]
    [4,3,1]
    [4,3,null]
    [4,null,1]
    [4,null,3]
    [5,2,1]
    [5,2,3]
    [5,2,null]
    [5,3,1]
    [5,3,null]
    [5,4,1]
    [5,4,3]
    [5,4,null]
    [5,null,1]
    [5,null,3]
    [null,2,1]
    [null,2,3]
    [null,3,1]
    [null,4,1]
    [null,4,3]
]

【讨论】:

  • [1,2,3][2,3,1] 是相同的组合。与[2,4,1][4,2,1] 相同。
  • @Rizier123,simPod 将 [1,2,3] 和 [2,3,1] 显示为正确结果。我认为这意味着顺序很重要。这将构成这些不同的组合。
  • 你说得对,我看起来不够好,但是他想要排列而不是组合,我会在 cmets 中询问。
  • 你是对的。 OP想要排列。但是接下来会有比你的代码输出更多的东西。
  • 很抱歉,没有办法计算这么多项目。即使在超级计算机上,它所花费的时间也比宇宙存在的时间还要长。 (不夸张)。
【解决方案2】:

这是一个低效的版本:

$input = array(
    [1,2,4,5],
    [2,3,4],
    [1,3]
);

function appendUnique($subs, $i) {
    global $input;
    if ($i == count($input)) {
        return $subs;
    }

    $output = array();
    foreach ($subs as $sub) {
        foreach ($input[$i] as $v) {
            $new_sub = array_values($sub);
            if (in_array($v, $sub)) {
                $new_sub[] = null;
            } else {
                $new_sub[] = $v;
            }
            $output[] = $new_sub;
        }
    }
    return appendUnique($output, $i+1);
}

$output = appendUnique([[]], 0);
$output_json = array();
foreach ($output as $row) {
    $output_json[] = json_encode($row);
}
$output_json = array_unique($output_json);
$deduped = array();
foreach ($output_json as $json) {
    $deduped[] = json_decode($json);
}
print_r($deduped);

输出:

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 
        )

    [1] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

    [2] => Array
        (
            [0] => 1
            [1] => 3
            [2] => 
        )

    [3] => Array
        (
            [0] => 1
            [1] => 4
            [2] => 
        )

    [4] => Array
        (
            [0] => 1
            [1] => 4
            [2] => 3
        )

    [5] => Array
        (
            [0] => 2
            [1] => 
            [2] => 1
        )

    [6] => Array
        (
            [0] => 2
            [1] => 
            [2] => 3
        )

    [7] => Array
        (
            [0] => 2
            [1] => 3
            [2] => 1
        )

    [8] => Array
        (
            [0] => 2
            [1] => 3
            [2] => 
        )

    [9] => Array
        (
            [0] => 2
            [1] => 4
            [2] => 1
        )

    [10] => Array
        (
            [0] => 2
            [1] => 4
            [2] => 3
        )

    [11] => Array
        (
            [0] => 4
            [1] => 2
            [2] => 1
        )

    [12] => Array
        (
            [0] => 4
            [1] => 2
            [2] => 3
        )

    [13] => Array
        (
            [0] => 4
            [1] => 3
            [2] => 1
        )

    [14] => Array
        (
            [0] => 4
            [1] => 3
            [2] => 
        )

    [15] => Array
        (
            [0] => 4
            [1] => 
            [2] => 1
        )

    [16] => Array
        (
            [0] => 4
            [1] => 
            [2] => 3
        )

    [17] => Array
        (
            [0] => 5
            [1] => 2
            [2] => 1
        )

    [18] => Array
        (
            [0] => 5
            [1] => 2
            [2] => 3
        )

    [19] => Array
        (
            [0] => 5
            [1] => 3
            [2] => 1
        )

    [20] => Array
        (
            [0] => 5
            [1] => 3
            [2] => 
        )

    [21] => Array
        (
            [0] => 5
            [1] => 4
            [2] => 1
        )

    [22] => Array
        (
            [0] => 5
            [1] => 4
            [2] => 3
        )

)

【讨论】:

  • 感谢您的帮助。我尝试输入[[ 1, 2], [2, 3, 4 ], [ 1, 3 ]],所以它也应该返回这个组合[null, 2, 1],但它没有:(
  • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性!
  • 看来 OP 想要获得所有排列,而不仅仅是组合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-11
  • 2015-06-18
  • 2023-01-30
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
相关资源
最近更新 更多